From 73211bc6423de846dc5884b136d49e91eb12009e Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 26 Jan 2026 11:58:29 +0100 Subject: [PATCH 001/449] wip --- clibs/lib/selva/fields.c | 2 +- native/modify/modify.zig | 55 +- native/modify/references.zig | 75 +- native/modify/subscription.zig | 4 + native/selva/node.zig | 2 + native/types.zig | 86 +- scripts/test_push_exports.ts | 98 ++ scripts/zigTsExports.ts | 133 ++- src/client/websocket/FakeWebsocket.ts | 13 +- src/client/websocket/types.ts | 2 +- src/db-client/index.ts | 2 +- src/db-client/modify/Ctx.ts | 19 +- src/db-client/modify/Tmp.ts | 6 +- src/db-client/modify/props/references.ts | 40 +- src/db-client/modify/update/index.ts | 69 ++ src/db-client/modify2.ts | 93 ++ src/db-client/query/BasedDbQuery.ts | 2 +- src/db-client/query/subscription/index.ts | 4 +- src/db-server/index.ts | 5 +- src/functions/client.ts | 4 +- src/modify/AutoSizedUint8Array.ts | 280 ++++++ src/modify/defs/base.ts | 26 + src/modify/defs/fixed.ts | 86 ++ src/modify/defs/getTypeDefs.ts | 120 +++ src/modify/defs/index.ts | 50 + src/modify/defs/references.ts | 207 ++++ src/modify/defs/vars.ts | 119 +++ src/modify/index.ts | 120 +++ src/modify/types.ts | 60 ++ src/schema/def/typeDef.ts | 5 +- src/schema/def/types.ts | 1 + src/schema/schema/schema.ts | 43 +- src/server/functionApi/client/query.ts | 2 +- src/server/functions/index.ts | 4 +- src/server/server.ts | 16 +- src/zigTsExports.ts | 1097 ++++++++++++++++++++- test/youzi.ts | 136 ++- tsconfig.json | 1 + 38 files changed, 2935 insertions(+), 152 deletions(-) create mode 100644 scripts/test_push_exports.ts create mode 100644 src/db-client/modify2.ts create mode 100644 src/modify/AutoSizedUint8Array.ts create mode 100644 src/modify/defs/base.ts create mode 100644 src/modify/defs/fixed.ts create mode 100644 src/modify/defs/getTypeDefs.ts create mode 100644 src/modify/defs/index.ts create mode 100644 src/modify/defs/references.ts create mode 100644 src/modify/defs/vars.ts create mode 100644 src/modify/index.ts create mode 100644 src/modify/types.ts diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 4abbdff32b..083b98d007 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -1781,7 +1781,7 @@ struct SelvaNode *selva_fields_ensure_ref_edge( { struct SelvaTypeEntry *edge_type = selva_get_type_by_index(db, efc->edge_node_type); struct SelvaNode *edge = nullptr; - + if (!edge_type) { return nullptr; } diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 0713ebffa8..70d1b6ac45 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -107,30 +107,30 @@ fn getLargeRef(db: *DbCtx, node: Node.Node, fs: Schema.FieldSchema, dstId: u32) return null; } -fn switchEdgeId(ctx: *ModifyCtx, srcId: u32, dstId: u32, refField: u8) !u32 { +pub fn switchEdgeId(ctx: *ModifyCtx, srcId: u32, dstId: u32, refField: u8) anyerror!u32 { var prevNodeId: u32 = 0; - if (srcId == 0 or ctx.node == null) { return 0; } - const fs = Schema.getFieldSchema(ctx.typeEntry, refField) catch { return 0; }; ctx.fieldSchema = fs; - if (getLargeRef(ctx.db, ctx.node.?, fs, dstId)) |ref| { const efc = Schema.getEdgeFieldConstraint(fs); - switchType(ctx, efc.edge_node_type) catch { - return 0; - }; + const edgeNode = Node.ensureRefEdgeNode(ctx, ctx.node.?, efc, ref) catch { return 0; }; + const edgeId = ref.*.edge; // if its zero then we don't want to switch (for upsert) prevNodeId = ctx.id; + + switchType(ctx, efc.edge_node_type) catch { + return 0; + }; ctx.id = edgeId; ctx.node = edgeNode; if (ctx.node == null) { @@ -146,11 +146,13 @@ fn switchEdgeId(ctx: *ModifyCtx, srcId: u32, dstId: u32, refField: u8) !u32 { return prevNodeId; } -pub fn writeData(ctx: *ModifyCtx, buf: []u8) !usize { +pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { var i: usize = 0; while (i < buf.len) { const op: t.ModOp = @enumFromInt(buf[i]); + // TODO set i += 1; HERE and remove from each individual thing const data: []u8 = buf[i + 1 ..]; + std.debug.print("OP: {any}\n", .{op}); switch (op) { .padding => { i += 1; @@ -302,17 +304,54 @@ pub fn writeData(ctx: *ModifyCtx, buf: []u8) !usize { Node.expireNode(ctx, ctx.typeId, ctx.id, std.time.timestamp() + read(u32, data, 0)); i += 5; }, + .end => { + i += 1; + break; + }, } } return i; } pub fn modify( + thread: *Thread.Thread, + buf: []u8, + ctx: *DbCtx, + opType: t.OpType, +) !void { + var i: usize = 0; + const header = utils.readNext(t.ModifyHeader, buf, &i); + const size = 4 + header.count * 5; + const result = try thread.modify.result(size, header.opId, header.opType); + _ = result; + _ = ctx; + _ = opType; + while (i < buf.len) { + const op: t.Modify = @enumFromInt(buf[i]); + switch (op) { + .create => { + const create = utils.readNext(t.ModifyCreateHeader, buf, &i); + const data: []u8 = buf[i .. i + create.size]; + std.debug.print("create: {any}, {any}, {any}\n", .{ create.size, buf.len, data }); + i += create.size; + }, + .update => {}, + .delete => {}, + } + } + + // const id = read(u32, batch, 0); + // const count = read(u32, batch, 13); + // const expectedLen = 4 + nodeCount * 5; +} + +pub fn _modify( thread: *Thread.Thread, batch: []u8, dbCtx: *DbCtx, opType: t.OpType, ) !void { + std.debug.print("hahaha??\n", .{}); const modifyId = read(u32, batch, 0); const nodeCount = read(u32, batch, 13); const expectedLen = 4 + nodeCount * 5; diff --git a/native/modify/references.zig b/native/modify/references.zig index 9d98ed4696..96c35edd30 100644 --- a/native/modify/references.zig +++ b/native/modify/references.zig @@ -11,10 +11,10 @@ const edge = @import("edges.zig"); const t = @import("../types.zig"); const RefEdgeOp = t.RefEdgeOp; const move = @import("../utils.zig").move; - +const modify = @import("./modify.zig"); const ModifyCtx = Modify.ModifyCtx; -pub fn writeReferences(ctx: *ModifyCtx, buf: []u8) !usize { +pub fn writeReferences(ctx: *ModifyCtx, buf: []u8) anyerror!usize { var i: usize = 0; while (i < buf.len) { const op: t.RefOp = @enumFromInt(buf[i]); @@ -22,11 +22,12 @@ pub fn writeReferences(ctx: *ModifyCtx, buf: []u8) !usize { i += 1; switch (op) { t.RefOp.set => { - const len: usize = read(u32, data, 0); - const u8IdsUnaligned = data[4 .. 4 + len]; + const len: u32 = read(u32, data, 0); + const size: u32 = len * 4; + const u8IdsUnaligned = data[4 .. 4 + size]; const address = @intFromPtr(u8IdsUnaligned.ptr); const offset: u8 = @truncate(address % 4); - const u8IdsAligned = data[4 - offset .. 4 + len - offset]; + const u8IdsAligned = data[4 - offset .. 4 + size - offset]; if (offset != 0) move(u8IdsAligned, u8IdsUnaligned); const u32Ids = read([]u32, u8IdsAligned, 0); try References.putReferences( @@ -35,36 +36,52 @@ pub fn writeReferences(ctx: *ModifyCtx, buf: []u8) !usize { ctx.fieldSchema.?, u32Ids, ); - i += 4 + len; - }, - t.RefOp.setIndex => { - // i += 1; - }, - t.RefOp.setTmp => { - // i += 1; + i += 4 + size; }, t.RefOp.setEdge => { - // i += 1; - }, - t.RefOp.setIndexTmp => { - // i += 1; - }, - t.RefOp.setEdgeIndex => { - // i += 1; - }, - t.RefOp.setEdgeIndexTmp => { - // i += 1;/ - }, - t.RefOp.setEdgeTmp => { - // i += 1; + const len: u32 = read(u32, data, 0); + var j: u32 = 0; + + // modify.switchEdgeId(ctx, ctx.id, ); + + var localCtx = ctx.*; + const srcId = ctx.id; + const refField = ctx.field; + const refTypeId = Schema.getRefTypeIdFromFieldSchema(ctx.fieldSchema.?); + const refTypeEntry = try Node.getType(ctx.db, refTypeId); + + while (j < len) : (j += 1) { + const id = read(u32, data, 4); + if (Node.getNode(refTypeEntry, id)) |dstNode| { + _ = try References.insertReference(ctx, ctx.node.?, ctx.fieldSchema.?, dstNode, 0, false); + } + _ = try modify.switchEdgeId(&localCtx, srcId, id, refField); + i += 4; + i += try modify.writeData(&localCtx, data[i..]); + } }, + // t.RefOp.setTmp => { + + // }, + // t.RefOp.setEdge => { + + // }, + // t.RefOp.setIndexTmp => { + + // }, + // t.RefOp.setEdgeIndex => { + + // }, + // t.RefOp.setEdgeIndexTmp => { + + // }, + // t.RefOp.setEdgeTmp => { + + // }, t.RefOp.clear => { References.clearReferences(ctx, ctx.node.?, ctx.fieldSchema.?); - // i += 1; - }, - t.RefOp.del => { - // i += 1; }, + t.RefOp.del => {}, t.RefOp.end => break, } } diff --git a/native/modify/subscription.zig b/native/modify/subscription.zig index 6e3ff3fa1c..dae41f51d0 100644 --- a/native/modify/subscription.zig +++ b/native/modify/subscription.zig @@ -191,6 +191,10 @@ pub fn suscription(thread: *Thread.Thread, batch: []u8) !void { .expire => { i += 5; }, + .end => { + i += 1; + break; + }, } } } diff --git a/native/selva/node.zig b/native/selva/node.zig index 9cfdf0e0b2..0c9c984c53 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -156,7 +156,9 @@ pub inline fn getNodeFromReference(dstType: selva.Type, ref: anytype) ?Node { } pub inline fn ensureRefEdgeNode(ctx: *Modify.ModifyCtx, node: Node, efc: selva.EdgeFieldConstraint, ref: selva.ReferenceLarge) !Node { + std.debug.print("here we are?\n", .{}); const edgeNode = selva.c.selva_fields_ensure_ref_edge(ctx.db.selva, node, efc, ref, 0); + std.debug.print("here we are: {any}\n", .{edgeNode}); if (edgeNode) |n| { selva.markDirty(ctx, efc.edge_node_type, getNodeId(n)); return n; diff --git a/native/types.zig b/native/types.zig index 5e24d6dfaf..af1ed9dd71 100644 --- a/native/types.zig +++ b/native/types.zig @@ -69,10 +69,76 @@ pub const ModOp = enum(u8) { deleteTextField = 16, upsert = 17, insert = 18, + end = 254, // TODO remove when modify is not used for response padding = 255, }; +pub const Modify = enum(u8) { + create = 0, + update = 1, + delete = 2, +}; + +pub const ModifyHeader = packed struct { + opId: u32, + opType: OpType, + schema: u64, + count: u32, +}; + +pub const ModifyUpdateHeader = packed struct { + op: Modify, + type: u8, + id: u32, + size: u32, +}; + +pub const ModifyDeleteHeader = packed struct { + op: Modify, + type: u8, + id: u32, +}; + +pub const ModifyCreateHeader = packed struct { + op: Modify, + type: u8, + size: u32, +}; + +pub const ModifyMainHeader = packed struct { + id: u8, + start: u16, + size: u16, +}; + +pub const ModifyPropHeader = packed struct { + id: u8, + type: u8, + size: u32, +}; + +pub const ModifyReferences = enum(u8) { + clear = 0, + ids = 1, + idsAndEdges = 2, + tmpIds = 3, + delIds = 4, + delTmpIds = 5, +}; + +pub const ModifyReferencesHeader = packed struct { + op: ModifyReferences, + size: u32, +}; + +pub const ModifyEdgesHeader = packed struct { + id: u32, + withIndex: bool, + index: u32, + size: u32, +}; + pub const PropType = enum(u8) { null = 0, timestamp = 1, @@ -180,17 +246,17 @@ pub const PropType = enum(u8) { pub const RefOp = enum(u8) { clear = 0, del = 1, - end = 2, - + end = @intFromEnum(ModOp.end), set = 3, - setIndex = 4, - setTmp = 5, - setEdge = 6, - - setIndexTmp = 7, - setEdgeIndex = 8, - setEdgeIndexTmp = 9, - setEdgeTmp = 10, + setEdge = 4, + // setIndex = 4, + // setTmp = 5, + // // setEdge = 6, + + // setIndexTmp = 7, + // setEdgeIndex = 8, + // setEdgeIndexTmp = 9, + // setEdgeTmp = 10, // overwrite = 0, // add = 1, diff --git a/scripts/test_push_exports.ts b/scripts/test_push_exports.ts new file mode 100644 index 0000000000..0db36c5c1e --- /dev/null +++ b/scripts/test_push_exports.ts @@ -0,0 +1,98 @@ +import { + ModifyMainHeader, + pushModifyMainHeader, + createModifyMainHeader, + readModifyMainHeader, + QueryHeader, + pushQueryHeader, + createQueryHeader, + OpType, + QueryType, + QueryIteratorType, +} from '../src/zigTsExports.js' +import { AutoSizedUint8Array } from '../src/modify/AutoSizedUint8Array.js' +import { deepEqual } from '../src/utils/index.js' + +const test = () => { + const header: ModifyMainHeader = { + id: 1, + start: 123, + size: 456, + } + + // Use createModifyMainHeader (existing logic) + const expectedBuf = createModifyMainHeader(header) + + // Use new pushModifyMainHeader + const autoBuf = new AutoSizedUint8Array() + const idx = pushModifyMainHeader(autoBuf, header) + + if (idx !== 0) { + console.error(`Expected index 0, got ${idx}`) + process.exit(1) + } + + const actualBuf = autoBuf.view + + console.log('Expected:', expectedBuf) + console.log('Actual: ', actualBuf) + + if (expectedBuf.length !== actualBuf.length) { + console.error('Length mismatch') + process.exit(1) + } + + for (let i = 0; i < expectedBuf.length; i++) { + if (expectedBuf[i] !== actualBuf[i]) { + console.error( + `Mismatch at index ${i}: expected ${expectedBuf[i]}, got ${actualBuf[i]}`, + ) + process.exit(1) + } + } + + console.log('ModifyMainHeader match!') + + // Test packed struct: QueryHeader + const queryHeader: QueryHeader = { + op: QueryType.ids, + prop: 10, + typeId: 99, + edgeTypeId: 88, + offset: 100, + limit: 50, + filterSize: 10, + searchSize: 20, + edgeSize: 30, + edgeFilterSize: 40, + includeSize: 5, + iteratorType: QueryIteratorType.desc, + size: 200, + sort: true, + } + + const expectedQueryBuf = createQueryHeader(queryHeader) + const autoQueryBuf = new AutoSizedUint8Array() + pushQueryHeader(autoQueryBuf, queryHeader) + const actualQueryBuf = autoQueryBuf.view + + console.log('QueryHeader Expected:', expectedQueryBuf) + console.log('QueryHeader Actual: ', actualQueryBuf) + + if (expectedQueryBuf.length !== actualQueryBuf.length) { + console.error('QueryHeader Length mismatch') + process.exit(1) + } + + for (let i = 0; i < expectedQueryBuf.length; i++) { + if (expectedQueryBuf[i] !== actualQueryBuf[i]) { + console.error( + `QueryHeader Mismatch at index ${i}: expected ${expectedQueryBuf[i]}, got ${actualQueryBuf[i]}`, + ) + process.exit(1) + } + } + console.log('QueryHeader match!') +} + +test() diff --git a/scripts/zigTsExports.ts b/scripts/zigTsExports.ts index dc81c086af..3afc8a4661 100644 --- a/scripts/zigTsExports.ts +++ b/scripts/zigTsExports.ts @@ -16,7 +16,8 @@ const parseZig = (input: string): string => { readUint32, readInt32, readUint64, readInt64, readFloatLE, readDoubleLE -} from './utils/index.js'\n\n` +} from './utils/index.js' +import { AutoSizedUint8Array } from './modify/AutoSizedUint8Array.js'\n\n` // Symbol tables const typeSizes: Record = { @@ -1020,6 +1021,136 @@ const parseZig = (input: string): string => { output += ` write${name}(buffer, header, 0)\n` output += ` return buffer\n` output += `}\n\n` + + // 7. Export Push Function + output += `export const push${name} = (\n` + output += ` buf: AutoSizedUint8Array,\n` + output += ` header: ${name},\n` + output += `): number => {\n` + output += ` const index = buf.length\n` + + if (!isPacked) { + fields.forEach((f) => { + const fName = f.name + const prim = getPrimitive( + body + .find((l) => l.includes(`${fName}:`)) + ?.match(regexStructField)?.[2] || 'u8', + ) + const valRef = f.isPadding ? '0' : `header.${fName}` + + switch (prim) { + case 'u8': + case 'LangCode': + output += ` buf.pushU8(${valRef})\n` + break + case 'bool': + output += ` buf.pushU8(${valRef} ? 1 : 0)\n` + break + case 'i8': + output += ` buf.pushU8(${valRef})\n` + break + case 'u16': + output += ` buf.pushU16(${valRef})\n` + break + case 'i16': + output += ` buf.pushU16(${valRef})\n` + break + case 'u32': + output += ` buf.pushU32(${valRef})\n` + break + case 'i32': + output += ` buf.pushU32(${valRef})\n` + break + case 'f32': + output += ` buf.pushF32(${valRef})\n` + break + case 'u64': + case 'usize': + output += ` buf.pushU64(${valRef})\n` + break + case 'i64': + output += ` buf.pushI64(${valRef})\n` + break + case 'f64': + output += ` buf.pushDouble(${valRef})\n` + break + default: + // Fallback for unknown types or padding greater than handled above + const byteCount = Math.ceil(f.bitSize / 8) + for (let k = 0; k < byteCount; k++) { + output += ` buf.pushU8(0)\n` + } + } + }) + } else { + let currentBitGlobal = 0 + + for (let i = 0; i < fields.length; i++) { + const f = fields[i] + + if (currentBitGlobal % 8 === 0 && [8, 16, 32, 64].includes(f.bitSize)) { + const fName = f.name + const valRef = f.isPadding ? '0' : `header.${fName}` + let valWithTernary = + f.isBoolean && !f.isPadding ? `(${valRef} ? 1 : 0)` : valRef + + if (f.isStruct && !f.isPadding) { + valWithTernary = `pack${f.type}(${valRef})` + } + + if (f.bitSize === 8) { + output += ` buf.pushU8(Number(${valWithTernary}))\n` + } else if (f.bitSize === 16) { + output += ` buf.pushU16(Number(${valWithTernary}))\n` + } else if (f.bitSize === 32) { + output += ` buf.pushU32(Number(${valWithTernary}))\n` + } else if (f.bitSize === 64) { + output += ` buf.pushU64(${valWithTernary})\n` + } + currentBitGlobal += f.bitSize + } else { + let remainingBits = f.bitSize + let valExpression = f.isPadding ? '0' : `header.${f.name}` + + if (f.isBoolean && !f.isPadding) { + valExpression = `(${valExpression} ? 1 : 0)` + } else if (f.isStruct && !f.isPadding) { + valExpression = `Number(pack${f.type}(${valExpression}))` + } + + let bitsProcessed = 0 + while (remainingBits > 0) { + const bitInByte = currentBitGlobal % 8 + const bitsCanFitInByte = 8 - bitInByte + const bitsToWrite = Math.min(remainingBits, bitsCanFitInByte) + + const mask = (1 << bitsToWrite) - 1 + + if (bitInByte === 0) { + // New byte started + output += ` buf.pushU8(0)\n` + } + + // Access the last byte using view directly OR ensure pushU8(0) initialized it + // We know we just pushed a byte if bitInByte == 0. + // If bitInByte > 0, the byte exists at buf.length - 1 + // But we need to be careful about not relying on `buf.view` if possible? + // Actually `buf.view` property exists on AutoSizedUint8Array. + // Let's use `buf.view[buf.length - 1] |= ...` + + output += ` buf.view[buf.length - 1] |= ((${valExpression} >>> ${bitsProcessed}) & ${mask}) << ${bitInByte}\n` + + currentBitGlobal += bitsToWrite + bitsProcessed += bitsToWrite + remainingBits -= bitsToWrite + } + } + } + } + + output += ` return index\n` + output += `}\n\n` } return output diff --git a/src/client/websocket/FakeWebsocket.ts b/src/client/websocket/FakeWebsocket.ts index ec32a3b544..71f10fc21a 100644 --- a/src/client/websocket/FakeWebsocket.ts +++ b/src/client/websocket/FakeWebsocket.ts @@ -20,7 +20,8 @@ const syncSubs = (ws: FakeWebsocket) => { export class FakeWebsocket { url: string - authState: string[] + // TODO remove these ts guards + authState!: string[] constructor(url: string, restPrefix: string, client: BasedClient) { // wss://1x7j3eroh-5mzale2mp.based.dev/ze1xch7kjGQtwg const segments = url.split('/') @@ -30,13 +31,13 @@ export class FakeWebsocket { this._r = restPrefix syncSubs(this) } - timer: ReturnType + timer!: ReturnType client: BasedClient - _c: boolean + _c!: boolean _r: string - _om: (x?: any) => void - _oe: (x?: any) => void - _oc: (x?: any) => void + _om!: (x?: any) => void + _oe!: (x?: any) => void + _oc!: (x?: any) => void close() { this._c = true if (this._oc) { diff --git a/src/client/websocket/types.ts b/src/client/websocket/types.ts index 6ef861381a..bab7ab62ef 100644 --- a/src/client/websocket/types.ts +++ b/src/client/websocket/types.ts @@ -3,7 +3,7 @@ import WebSocket from 'isomorphic-ws' export class Connection { public ws?: WebSocket public disconnected?: boolean - destroy: () => void + destroy!: () => void public fallBackTimer?: ReturnType public fallBackInProgress?: boolean public useFallback?: string diff --git a/src/db-client/index.ts b/src/db-client/index.ts index f8ebe125dd..6857af1406 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -55,7 +55,7 @@ export class DbClient extends DbShared { } subs = new Map() - stopped: boolean + stopped!: boolean hooks: DbClientHooks // modify diff --git a/src/db-client/modify/Ctx.ts b/src/db-client/modify/Ctx.ts index 16b3ae1a75..1873fcb9a4 100644 --- a/src/db-client/modify/Ctx.ts +++ b/src/db-client/modify/Ctx.ts @@ -8,7 +8,7 @@ export const MODIFY_HEADER_SIZE = 1 + 4 + 8 + 4 export class Ctx { constructor(schemaChecksum: number, buf: Uint8Array) { this.buf = buf - buf[4] = OpType.modify // make enum later 1 means normal MODIFY + buf[4] = OpType.modify writeUint64(buf, schemaChecksum, 5) this.reset() } @@ -19,18 +19,18 @@ export class Ctx { this.cursor = {} this.batch = {} } - start: number - index: number - schema: SchemaTypeDef + start!: number + index!: number + schema!: SchemaTypeDef buf: Uint8Array - max: number - size: number + max!: number + size!: number unsafe?: boolean - operation: ModOpEnum + operation!: ModOpEnum main: Map = new Map() - draining: Promise + draining!: Promise scheduled: Promise | undefined - locale: LangCodeEnum + locale!: LangCodeEnum sort: number = 0 sortText: number = 0 defaults: number = 0 @@ -39,7 +39,6 @@ export class Ctx { prop?: number main?: number operation?: ModOpEnum - upserting?: boolean } = {} batch: { count?: number diff --git a/src/db-client/modify/Tmp.ts b/src/db-client/modify/Tmp.ts index aa14f30083..7cb6fb0b9f 100644 --- a/src/db-client/modify/Tmp.ts +++ b/src/db-client/modify/Tmp.ts @@ -43,10 +43,10 @@ export class Tmp implements Promise { this.batch = ctx.batch this.tmpId = ctx.batch.count++ } - [Symbol.toStringTag]: 'ModifyPromise' + [Symbol.toStringTag]!: 'ModifyPromise' #schema: SchemaTypeDef - #id: number - #err: number + #id!: number + #err!: number get error(): Error | undefined { if (this.batch.ready && !this.id) { if (this.#err in errorMap) { diff --git a/src/db-client/modify/props/references.ts b/src/db-client/modify/props/references.ts index 041116a1e8..886af6be5a 100644 --- a/src/db-client/modify/props/references.ts +++ b/src/db-client/modify/props/references.ts @@ -5,6 +5,19 @@ import type { Ctx } from '../Ctx.js' import { PROP_CURSOR_SIZE, writePropCursor } from '../cursor.js' import { reserve } from '../resize.js' import { writeU32, writeU8 } from '../uint.js' +import { writeObject } from './object.js' + +type Edges = Record | undefined +const getEdges = (obj: Record): Edges => { + let edges: Edges + for (const i in obj) { + if (i[0] === '$' && i !== '$index') { + edges ??= {} + edges[i] = obj[i] + } + } + return edges +} export const writeReferences = (ctx: Ctx, def: PropDef, val: any) => { if (typeof val !== 'object') { @@ -27,40 +40,51 @@ export const writeReferences = (ctx: Ctx, def: PropDef, val: any) => { writeU8(ctx, RefOp.clear) } - let op: RefOpEnum | undefined + let prevOp: RefOpEnum | undefined let pos = 0 let len = 0 for (const item of val) { let id: number - let nextOp: RefOpEnum + let op: RefOpEnum + let edges: Edges if (typeof item === 'number') { id = item - nextOp = RefOp.set + op = RefOp.set } else if (typeof item === 'object' && item !== null && item.id) { id = item.id - nextOp = RefOp.set + edges = getEdges(item) + if (edges) { + op = RefOp.setEdge + } else { + op = RefOp.set + } } else { // not handled yet throw new Error('references payload, not handled yet - wip') } - if (op !== nextOp!) { - if (op) { + if (prevOp !== op!) { + if (prevOp) { // write previous len writeUint32(ctx.buf, len * 4, pos) } - writeU8(ctx, (op = nextOp!)) + writeU8(ctx, (prevOp = op!)) pos = ctx.index len = 0 ctx.index += 4 } writeU32(ctx, id) + if (edges) { + writeObject(ctx, def.edges as any, edges) + writeU8(ctx, ModOp.end) + } + len++ } - writeUint32(ctx.buf, len * 4, pos) + writeUint32(ctx.buf, len, pos) writeU8(ctx, RefOp.end) return } diff --git a/src/db-client/modify/update/index.ts b/src/db-client/modify/update/index.ts index 144bced155..ce416a74da 100644 --- a/src/db-client/modify/update/index.ts +++ b/src/db-client/modify/update/index.ts @@ -114,3 +114,72 @@ export function update( return handleError(db, ctx, update, arguments, e) } } + +// const simpleUpdate = () => { + +// } + +/* + CREATE + + article { + body: 'xxx', + age: 10, + rating: 4 + } + + ==> + + create|article|main:10,4|body:xxx + + UPDATE + + article 10 { + body: 'xxx', + age: 10, + rating: 4 + } + + ==> + + update|article|id:10|main:0:10|main:1:4|body:xxx + + update(u8)|size(u32)|type(u8)|id(u32)|...[prop(u8)] + + + propType,size,value + // age:number:81 + 1,4,81 + + + + name,string,value + + + UPSERT + + article 'abc', { + body: 'xxx', + age: 10, + rating: 4 + } + + ==> + + upsert|article|abc|main:0:10|main:1:4|body:xxx + + + + + + + + + + -------------------------------------------------------------------- + + + + + +*/ diff --git a/src/db-client/modify2.ts b/src/db-client/modify2.ts new file mode 100644 index 0000000000..8fdf86c47f --- /dev/null +++ b/src/db-client/modify2.ts @@ -0,0 +1,93 @@ +import { + parseSchema, + type Schema, + type SchemaOut, + type SchemaTypes, +} from '../schema.js' + +type TypedArray = + | Uint8Array + | Float32Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + +type TypeMap = { + string: string + number: number + int8: number + uint8: number + int16: number + uint16: number + int32: number + uint32: number + boolean: boolean + text: string + json: any + timestamp: number | string | Date + binary: Uint8Array + alias: string + vector: TypedArray + colvec: TypedArray + cardinality: string | string[] +} + +type InferProp = Prop extends { type: 'object'; props: infer P } + ? InferType + : Prop extends { type: infer T extends keyof TypeMap } + ? TypeMap[T] + : Prop extends { enum: infer E extends readonly any[] } + ? E[number] + : Prop extends { ref: string } + ? string | number + : Prop extends { items: { ref: string } } + ? (string | number)[] + : never + +type InferType = { + [K in keyof Props as Props[K] extends { required: true } + ? K + : never]: InferProp +} & { + [K in keyof Props as Props[K] extends { required: true } + ? never + : K]?: InferProp +} + +type InferPayload> = { + [K in keyof Types]: InferType +} + +const schema = parseSchema({ + types: { + user: { + props: { + name: 'string', + foo: 'string', + }, + }, + }, +}) + +export const create = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] = keyof S['types'], +>( + schema: S, + type: T, + payload: InferPayload[T], +) => { + const buf: Uint8Array = new Uint8Array(100) + buf + + const buffers = [] +} + +create(schema, 'user', { + name: 'xxx', + foo: 'abc', +}) diff --git a/src/db-client/query/BasedDbQuery.ts b/src/db-client/query/BasedDbQuery.ts index 313cf169f0..0304c43b07 100644 --- a/src/db-client/query/BasedDbQuery.ts +++ b/src/db-client/query/BasedDbQuery.ts @@ -554,7 +554,7 @@ class GetPromise extends Promise { export class BasedDbQuery extends QueryBranch { skipValidation?: boolean = false target: QueryTarget - readSchema: ReaderSchema + readSchema!: ReaderSchema constructor( db: DbClient, type: string, diff --git a/src/db-client/query/subscription/index.ts b/src/db-client/query/subscription/index.ts index 2161a77b72..ae7b31296e 100644 --- a/src/db-client/query/subscription/index.ts +++ b/src/db-client/query/subscription/index.ts @@ -5,8 +5,8 @@ import { registerSubscription } from './toByteCode.js' import { OnData, OnError, OnClose } from './types.js' export class SubStore { - listeners: Map - onClose: OnClose + listeners!: Map + onClose!: OnClose response?: BasedQueryResponse checksum?: number len?: number diff --git a/src/db-server/index.ts b/src/db-server/index.ts index c55c27de5c..95a12cb59c 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -22,13 +22,13 @@ import { readUint32, writeUint32 } from '../utils/uint8.js' export class DbServer extends DbShared { dbCtxExternal: any // pointer to zig dbCtx - migrating: number + migrating!: number saveInProgress: boolean = false fileSystemPath: string activeReaders = 0 // processing queries or other DB reads modifyQueue: Map = new Map() queryQueue: Map = new Map() - stopped: boolean // = true does not work + stopped!: boolean // = true does not work saveIntervalInSeconds?: number saveInterval?: NodeJS.Timeout delayInMs?: number @@ -189,6 +189,7 @@ export class DbServer extends DbShared { } const id = this.modifyCnt++ writeUint32(payload, id, 0) + payload[4] = OpType.modify return new Promise((resolve) => { native.modify(payload, this.dbCtxExternal) this.addOpOnceListener(OpType.modify, id, (v) => { diff --git a/src/functions/client.ts b/src/functions/client.ts index f7f7619980..d5587bb977 100644 --- a/src/functions/client.ts +++ b/src/functions/client.ts @@ -9,8 +9,8 @@ import type { DbClient } from '../db-client/index.js' export abstract class BasedFunctionClient { server: any - db: DbClient - dbs: Record + db!: DbClient + dbs!: Record abstract call(name: string, payload?: any, ctx?: Context): Promise diff --git a/src/modify/AutoSizedUint8Array.ts b/src/modify/AutoSizedUint8Array.ts new file mode 100644 index 0000000000..b870d80842 --- /dev/null +++ b/src/modify/AutoSizedUint8Array.ts @@ -0,0 +1,280 @@ +import { + writeDoubleLE, + writeFloatLE, + writeUint64, + writeInt64, +} from '../utils/index.js' + +// Runtime method injection +const delegateMethods = [ + 'toString', + 'toLocaleString', + 'join', + 'indexOf', + 'lastIndexOf', + 'includes', + 'every', + 'some', + 'forEach', + 'map', + 'filter', + 'find', + 'findIndex', + 'reduce', + 'reduceRight', + 'entries', + 'keys', + 'values', +] as const + +export class AutoSizedUint8Array { + static readonly ERR_OVERFLOW = 1 + + data: Uint8Array + length: number + private readonly _maxCapacity: number + + constructor( + initialCapacity: number = 256, + maxCapacity: number = 1024 * 1024 * 1024, + ) { + const buffer = new (ArrayBuffer as any)(initialCapacity, { + maxByteLength: maxCapacity, + }) + this.data = new Uint8Array(buffer) + this.length = 0 + this._maxCapacity = maxCapacity + } + + private ensureCapacity(requiredCapacity: number): void { + const currentCapacity = this.data.byteLength + if (currentCapacity >= requiredCapacity) return + + if (requiredCapacity > this._maxCapacity) { + throw AutoSizedUint8Array.ERR_OVERFLOW + } + + // Manual Max for speed + const doubleCapacity = currentCapacity * 2 + const newCapacity = + requiredCapacity > doubleCapacity ? requiredCapacity : doubleCapacity + // Cap at maxCapacity + const finalCapacity = + newCapacity > this._maxCapacity ? this._maxCapacity : newCapacity + + ;(this.data.buffer as any).resize(finalCapacity) + } + + get capacity(): number { + return this.data.length + } + + get view(): Uint8Array { + return this.data.subarray(0, this.length) + } + + set(array: ArrayLike, offset: number = 0): void { + const requiredEnd = offset + array.length + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data.set(array, offset) + if (requiredEnd > this.length) this.length = requiredEnd + } + + flush(): void { + this.length = 0 + } + + fill(value: number, start: number = 0, end: number = this.length): this { + if (end > this.length) { + if (end > this.data.byteLength) { + this.ensureCapacity(end) + } + this.length = end + } + this.data.fill(value, start, end) + return this + } + + copyWithin(target: number, start: number, end: number = this.length): this { + // Calculate required size conservatively if indices are positive + if (target >= 0 && start >= 0 && end >= 0 && end > start) { + const count = end - start + const requiredEnd = target + count + if (requiredEnd > this.data.byteLength) { + this.ensureCapacity(requiredEnd) + } + if (requiredEnd > this.length) { + this.length = requiredEnd + } + } + this.data.copyWithin(target, start, end) + return this + } + + get(index: number): number | undefined { + return index >= 0 && index < this.length ? this.data[index] : undefined + } + + pushU8(value: number): void { + const requiredEnd = this.length + 1 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data[this.length] = value + this.length = requiredEnd + } + + setU8(value: number, offset: number): void { + const requiredEnd = offset + 1 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data[offset] = value + if (requiredEnd > this.length) this.length = requiredEnd + } + + pushU16(value: number): void { + const requiredEnd = this.length + 2 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data[this.length] = value + this.data[this.length + 1] = value >> 8 + this.length = requiredEnd + } + + setU16(value: number, offset: number): void { + const requiredEnd = offset + 2 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data[offset] = value + this.data[offset + 1] = value >> 8 + if (requiredEnd > this.length) this.length = requiredEnd + } + + pushU32(value: number): void { + const requiredEnd = this.length + 4 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data[this.length] = value + this.data[this.length + 1] = value >> 8 + this.data[this.length + 2] = value >> 16 + this.data[this.length + 3] = value >> 24 + this.length = requiredEnd + } + + pushDouble(value: number): void { + const requiredEnd = this.length + 8 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + writeDoubleLE(this.data, value, this.length) + this.length = requiredEnd + } + + pushF32(value: number): void { + const requiredEnd = this.length + 4 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + writeFloatLE(this.data, value, this.length) + this.length = requiredEnd + } + + pushU64(value: number): void { + const requiredEnd = this.length + 8 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + writeUint64(this.data, value, this.length) + this.length = requiredEnd + } + + pushI64(value: number): void { + const requiredEnd = this.length + 8 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + writeInt64(this.data, value, this.length) + this.length = requiredEnd + } + + setU32(value: number, offset: number): void { + const requiredEnd = offset + 4 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.data[offset] = value + this.data[offset + 1] = value >> 8 + this.data[offset + 2] = value >> 16 + this.data[offset + 3] = value >> 24 + if (requiredEnd > this.length) this.length = requiredEnd + } + + reserveU32(): number { + const index = this.length + const requiredEnd = index + 4 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.length = requiredEnd + return index + } + + setSizeU32(start: number) { + this.setU32(this.length - start - 4, start) + } + + // Core array methods restored for type safety and performance + push(byte: number): void { + return this.pushU8(byte) + } + + subarray(begin?: number, end?: number): Uint8Array { + return this.view.subarray(begin, end) + } + + slice(start?: number, end?: number): Uint8Array { + return this.view.slice(start, end) + } +} + +// Methods delegated to the underlying Uint8Array view +type DelegatedProps = (typeof delegateMethods)[number] + +export interface AutoSizedUint8Array extends Pick { + [Symbol.iterator](): IterableIterator + reverse(): this + sort(compareFn?: (a: number, b: number) => number): this +} + +// Runtime method injection +for (const method of delegateMethods) { + ;(AutoSizedUint8Array.prototype as any)[method] = function ( + this: AutoSizedUint8Array, + ...args: any[] + ) { + return (this.view as any)[method](...args) + } +} + +const mutatingDelegateMethods = ['reverse', 'sort'] +for (const method of mutatingDelegateMethods) { + ;(AutoSizedUint8Array.prototype as any)[method] = function ( + this: AutoSizedUint8Array, + ...args: any[] + ) { + ;(this.view as any)[method](...args) + return this + } +} + +;(AutoSizedUint8Array.prototype as any)[Symbol.iterator] = function ( + this: AutoSizedUint8Array, +) { + return this.view[Symbol.iterator]() +} diff --git a/src/modify/defs/base.ts b/src/modify/defs/base.ts new file mode 100644 index 0000000000..1065922620 --- /dev/null +++ b/src/modify/defs/base.ts @@ -0,0 +1,26 @@ +import type { SchemaProp } from '../../schema.js' +import type { LangCodeEnum, ModifyEnum } from '../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import type { PropDef } from './index.js' + +export class BasePropDef implements PropDef { + constructor(prop: SchemaProp, path: string[]) { + this.prop = prop + this.path = path + } + id = 0 + start = 0 + size = 0 + type = 0 + prop: SchemaProp + path: string[] + + pushValue( + buf: AutoSizedUint8Array, + value: unknown, + op: ModifyEnum, + lang: LangCodeEnum, + ): void { + // To be implemented by subclasses + } +} diff --git a/src/modify/defs/fixed.ts b/src/modify/defs/fixed.ts new file mode 100644 index 0000000000..0ac8fec35d --- /dev/null +++ b/src/modify/defs/fixed.ts @@ -0,0 +1,86 @@ +import type { EnumItem, SchemaEnum } from '../../schema.js' +import { PropType } from '../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' + +export const number = class extends BasePropDef { + override type: number = PropType.number + override size = 8 + override pushValue(buf: AutoSizedUint8Array, value: number) { + buf.pushDouble(value) + } +} + +export const timestamp = class extends number { + override type = PropType.timestamp +} + +export const uint8 = class extends BasePropDef { + override type: number = PropType.uint8 + override size = 1 + override pushValue(buf: AutoSizedUint8Array, value: number): void { + buf.pushU8(value) + } +} + +export const int8 = class extends uint8 { + override type = PropType.int8 +} + +export const uint16 = class extends BasePropDef { + override type: number = PropType.uint16 + override size = 2 + override pushValue(buf: AutoSizedUint8Array, value: number): void { + buf.pushU16(value) + } +} + +export const int16 = class extends uint16 { + override type = PropType.int16 +} + +export const uint32 = class extends BasePropDef { + override type: number = PropType.uint32 + override size = 4 + override pushValue(buf: AutoSizedUint8Array, value: number): void { + buf.pushU32(value) + } +} + +export const int32 = class extends uint32 { + override type = PropType.int32 +} + +export const enum_ = class extends uint8 { + constructor(prop: SchemaEnum, path: string[]) { + super(prop, path) + prop.enum.forEach((val, i) => { + const byte = i + 1 + this.enum[byte] = val + this.vals.set(val, byte) + }) + } + override type = PropType.enum + enum: Record = {} + vals = new Map() + + override pushValue(buf: AutoSizedUint8Array, value: EnumItem): void { + buf.pushU8(this.vals.get(value) ?? 0) + } +} + +export const boolean = class extends BasePropDef { + override type = PropType.boolean + override size = 1 + override pushValue(buf: AutoSizedUint8Array, value: boolean): void { + buf.pushU8(value ? 1 : 0) + } +} + +// export const created = class extends number { +// override type = PropType.created +// } + +// export const updated = class extends number { +// override type = PropType.updated +// } diff --git a/src/modify/defs/getTypeDefs.ts b/src/modify/defs/getTypeDefs.ts new file mode 100644 index 0000000000..5f094db6c6 --- /dev/null +++ b/src/modify/defs/getTypeDefs.ts @@ -0,0 +1,120 @@ +import { + type SchemaOut, + type SchemaProp, + type SchemaProps, + type SchemaType, +} from '../../schema.js' +import { defs, type TypeDef } from './index.js' + +const mainSorter = (a, b) => { + if (a.size === 8) return -1 + if (a.size === 4 && b.size !== 8) return -1 + if (a.size === b.size) return 0 + return 1 +} + +const getTypeDef = ({ props }: SchemaType): TypeDef => { + const typeDef: TypeDef = { + id: 0, + separate: [], + props: new Map(), + main: [], + tree: new Map(), + } + + let propId = 1 + + const walk = ( + props: SchemaProps, + pPath: string[], + tree: TypeDef['tree'], + ): void => { + for (const key in props) { + const prop = props[key] + const path = [...pPath, key] + if (prop.type === 'object') { + const branch = new Map() + walk(prop.props, path, branch) + tree.set(key, branch) + continue + } + + const Def = defs[prop.type] + if (!Def) { + console.error('unknown def') + // TODO: handle missing type + continue + } + + const def = new Def(prop, path) + if (def.size) { + typeDef.main.push(def) + } else { + def.id = propId++ + typeDef.separate.push(def) + } + typeDef.props.set(path.join('.'), def) + tree.set(key, def) + } + } + + walk(props, [], typeDef.tree) + + // -------- finish main -------- + typeDef.main.sort(mainSorter) + + let start = 0 + for (const prop of typeDef.main) { + prop.start = start + start += prop.size + } + + return typeDef +} + +const cache = new WeakMap() +export const getTypeDefs = (schema: SchemaOut): Map => { + const cached = cache.get(schema) + if (cached) return cached + const typeDefs = new Map( + Object.entries(schema.types) + .sort() + .map(([key, type]) => [key, getTypeDef(type)]), + ) + + // -------- connect references, add edges and assign ids -------- + let typeId = 1 + for (const [typeName, typeDef] of typeDefs) { + typeDef.id = typeId++ + for (const [propPath, def] of typeDef.props) { + const prop = def.prop.type === 'references' ? def.prop.items : def.prop + if (prop.type !== 'reference') continue + def.ref = typeDefs.get(prop.ref)! + if (!prop.prop) { + continue + } + def.refProp = def.ref.props.get(prop.prop)! + const inverseEdges = def.refProp.edges + if (inverseEdges) { + def.edges = inverseEdges + continue + } + let edges: undefined | Record> + for (const edge in prop) { + if (edge[0] !== '$') continue + edges ??= {} + edges[edge] = prop[edge] + } + if (edges) { + def.edges = getTypeDef({ props: edges }) + const edgeTypeName = `_${typeName}.${propPath}` + typeDefs.set(edgeTypeName, def.edges) + } + } + } + + // ----------- add to cache -------- + cache.set(schema, typeDefs) + + return typeDefs +} diff --git a/src/modify/defs/index.ts b/src/modify/defs/index.ts new file mode 100644 index 0000000000..467bfe5d40 --- /dev/null +++ b/src/modify/defs/index.ts @@ -0,0 +1,50 @@ +import type { SchemaProp } from '../../schema.js' +import type { LangCodeEnum, ModifyEnum } from '../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import * as fixed from './fixed.js' +import * as references from './references.js' + +import * as vars from './vars.js' + +export type PropTree = Map + +export type TypeDef = { + // mainSize: number + id: number + main: PropDef[] + separate: PropDef[] + props: Map + tree: PropTree +} + +export type PropDef = { + id: number + type: number + start: number + path: string[] + size: number + prop: SchemaProp + edges?: TypeDef + ref?: TypeDef + refProp?: PropDef + pushValue( + buf: AutoSizedUint8Array, + value: unknown, + op: ModifyEnum, + lang: LangCodeEnum, + ): void +} + +export type PropDefClass = { + new (prop: SchemaProp, path: string[]): PropDef +} + +export const defs: Record< + Exclude['type'], 'object'>, + PropDefClass +> = { + ...references, + ...fixed, + ...vars, + enum: fixed.enum_, +} diff --git a/src/modify/defs/references.ts b/src/modify/defs/references.ts new file mode 100644 index 0000000000..6a7acb0aba --- /dev/null +++ b/src/modify/defs/references.ts @@ -0,0 +1,207 @@ +import { + Modify, + ModifyReferences, + PropType, + pushModifyReferencesHeader, + writeModifyReferencesHeaderProps, + type LangCodeEnum, + type ModifyEnum, +} from '../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import type { SchemaProp } from '../../schema.js' +import { BasePropDef } from './base.js' +import type { PropDef, TypeDef } from './index.js' +import { serializeProps } from '../index.js' + +type Edges = Record<`${string}`, unknown> | undefined + +const hasEdgesAndOrIndex = (obj: Record): boolean | void => { + for (const i in obj) { + if (i[0] === '$') return true + } +} + +const getEdges = (obj: Record): Edges => { + let edges: Edges + for (const i in obj) { + if (i[0] === '$' && i !== '$index') { + edges ??= {} + edges[i] = obj[i] + } + } + return edges +} + +const serializeIds = ( + buf: AutoSizedUint8Array, + ids: number[], + offset: number, +): number => { + let i = offset + for (; i < ids.length; i++) { + const id = ids[i] + if (typeof id !== 'number') { + break + } + buf.pushU32(id) + } + return i +} + +const serializeTmpIds = ( + buf: AutoSizedUint8Array, + items: { tmpId: number }[], + offset: number, +): undefined | any => { + let i = offset + for (; i < items.length; i++) { + const item = items[i] + if (typeof item !== 'object' || item === null || !item.tmpId) { + // TODO handle async await for tmp in other batch + break + } + buf.pushU32(item.tmpId) + } + + return i +} + +const serializeIdsAndMeta = ( + buf: AutoSizedUint8Array, + items: any[], + op: ModifyEnum, + offset: number, + lang: LangCodeEnum, + edgesType?: TypeDef, +): number => { + let i = offset + for (; i < items.length; i++) { + const item = items[i] + if (item === null || typeof item !== 'object') { + throw 'error' + } + const edges = getEdges(item) + if (typeof item.id !== 'number' || !edges) { + break + } + buf.pushU32(item.id) + if (edgesType) { + serializeProps(edgesType.tree, edges, buf, op, lang) + } + } + return i +} + +const setReferences = ( + buf: AutoSizedUint8Array, + value: any[], + prop: PropDef, + op: ModifyEnum, + lang: LangCodeEnum, +) => { + let offset = 0 + while (offset < value.length) { + const item = value[offset] + + if (typeof item === 'number') { + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.ids, + size: 0, + }) + const start = buf.length + offset = serializeIds(buf, value, offset) + writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) + continue + } + if (typeof item === 'object' && item !== null) { + if (item.tmpId) { + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.tmpIds, + size: 0, + }) + const start = buf.length + offset = serializeTmpIds(buf, value, offset) + writeModifyReferencesHeaderProps.size( + buf.data, + buf.length - start, + index, + ) + continue + } + if (typeof item.id === 'number') { + // TODO can optimize, don't need whole object + if (hasEdgesAndOrIndex(item)) { + offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) + } else { + // TODO with index + } + } + } + + throw 'bad ref' + } +} + +const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { + let offset = 0 + while (offset < value.length) { + const item = value[offset] + if (typeof item === 'number') { + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.delIds, + size: 0, + }) + const start = buf.length + offset = serializeIds(buf, value, offset) + writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) + continue + } + if (typeof item === 'object' && item !== null && item.tmpId) { + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.tmpIds, + size: 0, + }) + const start = buf.length + offset = serializeTmpIds(buf, value, offset) + writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) + continue + } + throw 'bad ref' + } +} + +export const references = class extends BasePropDef { + override type = PropType.references + override pushValue( + buf: AutoSizedUint8Array, + value: any, + op: ModifyEnum, + lang: LangCodeEnum, + ) { + if (typeof value !== 'object' || value === null) { + throw new Error('References value must be an object and not null') + } + if (Array.isArray(value)) { + if (op === Modify.update) { + buf.push(ModifyReferences.clear) + } + setReferences(buf, value, this, op, lang) + } + if (value.add) { + setReferences(buf, value.add, this, op, lang) + } + if (value.update) { + setReferences(buf, value.update, this, op, lang) + } + if (value.delete) { + deleteReferences(buf, value) + } + } +} + +export const reference = class extends references { + override type = PropType.references + override pushValue(buf: AutoSizedUint8Array, value: any, op: ModifyEnum) { + console.error('TODO reference') + } +} diff --git a/src/modify/defs/vars.ts b/src/modify/defs/vars.ts new file mode 100644 index 0000000000..d8fc4fb873 --- /dev/null +++ b/src/modify/defs/vars.ts @@ -0,0 +1,119 @@ +import type { SchemaString, SchemaVector } from '../../schema.js' +import { + PropType, + type LangCodeEnum, + type ModifyEnum, +} from '../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' + +const encoder = new TextEncoder() + +export const string = class extends BasePropDef { + constructor(prop: SchemaString, path) { + super(prop, path) + if (prop.maxBytes && prop.maxBytes < 61) { + this.size = prop.maxBytes + 1 + } else if (prop.max && prop.max < 31) { + this.size = prop.max * 2 + 1 + } + if (this.size) { + } + } + override type: number = PropType.string + override pushValue( + buf: AutoSizedUint8Array, + val: unknown, + op: ModifyEnum, + lang: LangCodeEnum, + ) { + const isUint8 = val instanceof Uint8Array + if (val === null || val === '' || (isUint8 && val.byteLength === 0)) { + // deleteString(ctx, def, lang) + // return + } + // const encoded = encoder.encode(value) + // buf.set(encoded, buf.length) + // buf.length += encoded.length + } +} + +// TODO do it nice +export const text = class extends string { + override type = PropType.text +} + +export const json = class extends string { + override type = PropType.json + override pushValue( + buf: AutoSizedUint8Array, + value: any, + op: ModifyEnum, + lang: LangCodeEnum, + ) { + super.pushValue(buf, JSON.stringify(value), op, lang) + } +} + +export const binary = class extends BasePropDef { + override type = PropType.binary + override pushValue(buf: AutoSizedUint8Array, value: Uint8Array) { + buf.set(value, buf.length) + buf.length += value.length + } +} + +export const alias = class extends BasePropDef { + override type = PropType.alias + override pushValue(buf: AutoSizedUint8Array, value: any) { + throw new Error('Serialize alias not implemented') + } +} + +export const cardinality = class extends BasePropDef { + override type = PropType.cardinality + override pushValue(buf: AutoSizedUint8Array, value: any) { + throw new Error('Serialize cardinality not implemented') + } +} + +export const vector = class extends BasePropDef { + constructor(prop: SchemaVector, path) { + super(prop, path) + this.vectorSize = prop.size * 4 + } + vectorSize: number + override type: number = PropType.vector + override pushValue(buf: AutoSizedUint8Array, value: any) { + throw new Error('Serialize vector not implemented') + } +} + +export const colvec = class extends BasePropDef { + constructor(prop: SchemaVector, path) { + super(prop, path) + this.colvecSize = prop.size * getByteSize(prop.baseType) + } + colvecSize: number + override type = PropType.colVec + override pushValue(buf: AutoSizedUint8Array, value: any) { + throw new Error('Serialize colvec not implemented') + } +} + +function getByteSize(str?: string) { + switch (str) { + case 'int8': + case 'uint8': + return 1 + case 'int16': + case 'uint16': + return 2 + case 'int32': + case 'uint32': + case 'float32': + return 4 + default: + return 8 + } +} diff --git a/src/modify/index.ts b/src/modify/index.ts new file mode 100644 index 0000000000..c085117b8d --- /dev/null +++ b/src/modify/index.ts @@ -0,0 +1,120 @@ +import { SchemaOut } from '../schema.js' +import { + Modify, + pushModifyCreateHeader, + pushModifyDeleteHeader, + pushModifyMainHeader, + pushModifyPropHeader, + pushModifyUpdateHeader, + writeModifyCreateHeaderProps, + writeModifyPropHeaderProps, + writeModifyUpdateHeaderProps, + type LangCodeEnum, + type ModifyEnum, +} from '../zigTsExports.js' +import { AutoSizedUint8Array } from './AutoSizedUint8Array.js' +import type { PropDef, PropTree, TypeDef } from './defs/index.js' +import { InferPayload } from './types.js' +import { getTypeDefs } from './defs/getTypeDefs.js' +export { getTypeDefs } + +export const serializeProps = ( + tree: PropTree, + data: any, + buf: AutoSizedUint8Array, + op: ModifyEnum, + lang: LangCodeEnum, +) => { + for (const key in data) { + const def = tree.get(key) + if (def === undefined) { + continue + } + const val = data[key] + if (def.constructor === Map) { + if (val !== null && typeof val === 'object') { + serializeProps(def, val, buf, op, lang) + } + } else { + const prop = def as PropDef + if (prop.size) { + pushModifyMainHeader(buf, prop) + prop.pushValue(buf, val, op, lang) + } else { + const index = pushModifyPropHeader(buf, prop) + const start = buf.length + prop.pushValue(buf, val, op, lang) + writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) + } + } + } +} + +const getTypeDef = (schema: SchemaOut, type: string) => { + const typeDef = getTypeDefs(schema).get(type) + if (!typeDef) { + throw new Error(`Type ${type} not found`) + } + return typeDef +} + +export const serializeCreate = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyCreateHeader(buf, { + op: Modify.create, + type: typeDef.id, + size: 0, + }) + const start = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) +} + +export const serializeUpdate = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + id: number, + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyUpdateHeader(buf, { + op: Modify.update, + type: typeDef.id, + id, + size: 0, + }) + const start = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + writeModifyUpdateHeaderProps.size(buf.data, buf.length - start, index) +} + +export const serializeDelete = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + id: number, + buf: AutoSizedUint8Array, +) => { + const typeDef = getTypeDef(schema, type) + pushModifyDeleteHeader(buf, { + op: Modify.delete, + type: typeDef.id, + id, + }) +} diff --git a/src/modify/types.ts b/src/modify/types.ts new file mode 100644 index 0000000000..6727f141c2 --- /dev/null +++ b/src/modify/types.ts @@ -0,0 +1,60 @@ +import { + type SchemaTypes, +} from '../schema.js' + +type TypedArray = + | Uint8Array + | Float32Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + +type TypeMap = { + string: string + number: number + int8: number + uint8: number + int16: number + uint16: number + int32: number + uint32: number + boolean: boolean + text: string + json: any + timestamp: number | string | Date + binary: Uint8Array + alias: string + vector: TypedArray + colvec: TypedArray + cardinality: string | string[] +} + +type InferProp = Prop extends { type: 'object'; props: infer P } + ? InferType + : Prop extends { type: infer T extends keyof TypeMap } + ? TypeMap[T] + : Prop extends { enum: infer E extends readonly any[] } + ? E[number] + : Prop extends { ref: string } + ? string | number + : Prop extends { items: { ref: string } } + ? (string | number)[] + : never + +type InferType = { + [K in keyof Props as Props[K] extends { required: true } + ? K + : never]: InferProp +} & { + [K in keyof Props as Props[K] extends { required: true } + ? never + : K]?: InferProp +} + +export type InferPayload> = { + [K in keyof Types]: InferType +} \ No newline at end of file diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index 25e8cf4452..6ec05066b2 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -85,11 +85,8 @@ export const updateTypeDefs = (schema: SchemaOut) => { if (prop.edges) { const edgeTypeName = `_${[`${schemaType.type}_${prop.path.join('_')}`, `${dstType.type}_${dstType.props[prop.inversePropName as string].path.join('_')}`].sort().join(':')}` - // console.log(edgeTypeName, Object.keys(schemaTypesParsed)) - if (!schemaTypesParsed[edgeTypeName]) { // make it - // console.log('have to make edge type') //prop.edges, schema.types // const type = schema.types[edgeTypeName] @@ -115,9 +112,11 @@ export const updateTypeDefs = (schema: SchemaOut) => { } const edgeType = schemaTypesParsed[edgeTypeName] + prop.edgeNodeTypeId = edgeType.id dstType.props[prop.inversePropName as string].edgeNodeTypeId = edgeType.id + // prop.edgeType = edgeType } else { prop.edgeNodeTypeId = 0 } diff --git a/src/schema/def/types.ts b/src/schema/def/types.ts index 13ffa6680a..9157953318 100644 --- a/src/schema/def/types.ts +++ b/src/schema/def/types.ts @@ -45,6 +45,7 @@ export type PropDef = { hasDefaultEdges?: boolean reverseEnum?: { [key: string]: number } edgesSeperateCnt?: number + edgeType?: SchemaTypeDef edges?: { [key: string]: PropDefEdge } diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index 7531f7c57e..51efc4a3bc 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -2,7 +2,6 @@ import { assert, assertExpectedProps, deleteUndefined, - isBoolean, isFunction, isRecord, isString, @@ -33,6 +32,40 @@ export type Schema = { export type SchemaIn = Schema | Schema export type SchemaOut = Schema +type NormalizeProp = T extends string + ? { type: T } + : T extends readonly (infer U)[] + ? { type: 'enum'; enum: U[] } + : T extends { type: 'object'; props: infer P } + ? Omit & { + type: 'object' + props: { [K in keyof P]: NormalizeProp } + } + : T extends { props: infer P } + ? Omit & { + type: 'object' + props: { [K in keyof P]: NormalizeProp } + } + : T extends { items: infer I } + ? Omit & { type: 'references'; items: NormalizeProp } + : T extends { ref: string } + ? T & { type: 'reference' } + : T extends { enum: any[] } + ? T & { type: 'enum' } + : T + +type NormalizeType = T extends { props: infer P } + ? Omit & { props: { [K in keyof P]: NormalizeProp } } + : { props: { [K in keyof T]: NormalizeProp } } + +type ResolveSchema = Omit & { + hash: number + locales: SchemaLocales + types: { + [K in keyof S['types']]: NormalizeType + } +} & SchemaOut + const isMigrations = (v: unknown): v is SchemaMigrations => isRecord(v) && Object.values(v).every( @@ -86,7 +119,10 @@ const track =

>(input: P): P => { return _track(input, 0, input) } -export const parseSchema = (input: SchemaIn): SchemaOut => { +/* + This returns a "public" parsed schema, suitable for external users +*/ +export const parseSchema = (input: S): ResolveSchema => { const v: unknown = track(input) assert(isRecord(v), 'Schema should be record') try { @@ -130,9 +166,10 @@ export const parseSchema = (input: SchemaIn): SchemaOut => { } } + // TODO we can remove hash from here after we finish new schema defs (internal schema) result.hash = hash(result) - return result + return result as ResolveSchema } catch (e) { if (tracking) { e = Error(`${path.join('.')}: ${inspect(value)} - ${e}`, { cause: e }) diff --git a/src/server/functionApi/client/query.ts b/src/server/functionApi/client/query.ts index b4beff133d..8009352bc9 100644 --- a/src/server/functionApi/client/query.ts +++ b/src/server/functionApi/client/query.ts @@ -19,7 +19,7 @@ export class BasedQuery extends BasedQueryAbstract { public payload: any public name: string public ctx: Context - public attachedCtx: AttachedCtx + public attachedCtx!: AttachedCtx public id: number public route: BasedRoute<'query'> diff --git a/src/server/functions/index.ts b/src/server/functions/index.ts index 78849901a2..1e9f7ce60e 100644 --- a/src/server/functions/index.ts +++ b/src/server/functions/index.ts @@ -24,9 +24,9 @@ export class BasedFunctions { reqId: number = 0 - config: FunctionConfig + config!: FunctionConfig - unregisterTimeout: NodeJS.Timeout + unregisterTimeout!: NodeJS.Timeout installsInProgress: Record> = {} diff --git a/src/server/server.ts b/src/server/server.ts index 16a734fd85..5934dd2b8e 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -120,15 +120,15 @@ export class BasedServer { public auth: BasedAuth - public port: number + public port!: number - public uwsApp: uws.TemplatedApp + public uwsApp!: uws.TemplatedApp - public silent: boolean + public silent!: boolean - public queryEvents: QueryEvents + public queryEvents!: QueryEvents - public channelEvents: ChannelEvents + public channelEvents!: ChannelEvents public rateLimit: RateLimit = { ws: 2e3, @@ -140,7 +140,7 @@ export class BasedServer { public forceReloadLastSeqId: number = -1 - public encodedForceReload: Uint8Array + public encodedForceReload!: Uint8Array public sendInitialForceReload: boolean = false @@ -209,7 +209,7 @@ export class BasedServer { public requestsCounterInProgress: boolean = false - public requestsCounterTimeout: NodeJS.Timeout + public requestsCounterTimeout!: NodeJS.Timeout public obsCleanTimeout?: NodeJS.Timeout @@ -237,7 +237,7 @@ export class BasedServer { [E in Event]?: Listener[] } = {} - public workerRequest: (type: string, payload?: any) => void | Promise + public workerRequest!: (type: string, payload?: any) => void | Promise private http: ServerOptions['http'] = {}; diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index b515320673..42eeef06da 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -8,6 +8,7 @@ import { readUint64, readInt64, readFloatLE, readDoubleLE } from './utils/index.js' +import { AutoSizedUint8Array } from './modify/AutoSizedUint8Array.js' export type TypeId = number @@ -136,6 +137,7 @@ export const ModOp = { deleteTextField: 16, upsert: 17, insert: 18, + end: 254, padding: 255, } as const @@ -160,6 +162,7 @@ export const ModOpInverse = { 16: 'deleteTextField', 17: 'upsert', 18: 'insert', + 254: 'end', 255: 'padding', } as const @@ -184,10 +187,807 @@ export const ModOpInverse = { deleteTextField, upsert, insert, + end, padding */ export type ModOpEnum = (typeof ModOp)[keyof typeof ModOp] +export const Modify = { + create: 0, + update: 1, + delete: 2, +} as const + +export const ModifyInverse = { + 0: 'create', + 1: 'update', + 2: 'delete', +} as const + +/** + create, + update, + delete + */ +export type ModifyEnum = (typeof Modify)[keyof typeof Modify] + +export type ModifyHeader = { + opId: number + opType: OpTypeEnum + schema: number + count: number +} + +export const ModifyHeaderByteSize = 17 + +export const packModifyHeader = (obj: ModifyHeader): bigint => { + let val = 0n + val |= (BigInt(obj.opId) & 4294967295n) << 0n + val |= (BigInt(obj.opType) & 255n) << 32n + val |= (BigInt(obj.schema) & 18446744073709551615n) << 40n + val |= (BigInt(obj.count) & 4294967295n) << 104n + return val +} + +export const unpackModifyHeader = (val: bigint): ModifyHeader => { + return { + opId: Number((val >> 0n) & 4294967295n), + opType: (Number((val >> 32n) & 255n)) as OpTypeEnum, + schema: Number((val >> 40n) & 18446744073709551615n), + count: Number((val >> 104n) & 4294967295n), + } +} + +export const writeModifyHeader = ( + buf: Uint8Array, + header: ModifyHeader, + offset: number, +): number => { + writeUint32(buf, Number(header.opId), offset) + offset += 4 + buf[offset] = Number(header.opType) + offset += 1 + writeUint64(buf, header.schema, offset) + offset += 8 + writeUint32(buf, Number(header.count), offset) + offset += 4 + return offset +} + +export const writeModifyHeaderProps = { + opId: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset) + }, + opType: (buf: Uint8Array, value: OpTypeEnum, offset: number) => { + buf[offset + 4] = Number(value) + }, + schema: (buf: Uint8Array, value: number, offset: number) => { + writeUint64(buf, value, offset + 5) + }, + count: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 13) + }, +} + +export const readModifyHeader = ( + buf: Uint8Array, + offset: number, +): ModifyHeader => { + const value: ModifyHeader = { + opId: readUint32(buf, offset), + opType: (buf[offset + 4]) as OpTypeEnum, + schema: readUint64(buf, offset + 5), + count: readUint32(buf, offset + 13), + } + return value +} + +export const readModifyHeaderProps = { + opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + opType: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as OpTypeEnum, + schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), + count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), +} + +export const createModifyHeader = (header: ModifyHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyHeaderByteSize) + writeModifyHeader(buffer, header, 0) + return buffer +} + +export const pushModifyHeader = ( + buf: AutoSizedUint8Array, + header: ModifyHeader, +): number => { + const index = buf.length + buf.pushU32(Number(header.opId)) + buf.pushU8(Number(header.opType)) + buf.pushU64(header.schema) + buf.pushU32(Number(header.count)) + return index +} + +export type ModifyUpdateHeader = { + op: ModifyEnum + type: number + id: number + size: number +} + +export const ModifyUpdateHeaderByteSize = 10 + +export const packModifyUpdateHeader = (obj: ModifyUpdateHeader): bigint => { + let val = 0n + val |= (BigInt(obj.op) & 255n) << 0n + val |= (BigInt(obj.type) & 255n) << 8n + val |= (BigInt(obj.id) & 4294967295n) << 16n + val |= (BigInt(obj.size) & 4294967295n) << 48n + return val +} + +export const unpackModifyUpdateHeader = (val: bigint): ModifyUpdateHeader => { + return { + op: (Number((val >> 0n) & 255n)) as ModifyEnum, + type: Number((val >> 8n) & 255n), + id: Number((val >> 16n) & 4294967295n), + size: Number((val >> 48n) & 4294967295n), + } +} + +export const writeModifyUpdateHeader = ( + buf: Uint8Array, + header: ModifyUpdateHeader, + offset: number, +): number => { + buf[offset] = Number(header.op) + offset += 1 + buf[offset] = Number(header.type) + offset += 1 + writeUint32(buf, Number(header.id), offset) + offset += 4 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyUpdateHeaderProps = { + op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { + buf[offset] = Number(value) + }, + type: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + id: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 6) + }, +} + +export const readModifyUpdateHeader = ( + buf: Uint8Array, + offset: number, +): ModifyUpdateHeader => { + const value: ModifyUpdateHeader = { + op: (buf[offset]) as ModifyEnum, + type: buf[offset + 1], + id: readUint32(buf, offset + 2), + size: readUint32(buf, offset + 6), + } + return value +} + +export const readModifyUpdateHeaderProps = { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), +} + +export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyUpdateHeaderByteSize) + writeModifyUpdateHeader(buffer, header, 0) + return buffer +} + +export const pushModifyUpdateHeader = ( + buf: AutoSizedUint8Array, + header: ModifyUpdateHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.type)) + buf.pushU32(Number(header.id)) + buf.pushU32(Number(header.size)) + return index +} + +export type ModifyDeleteHeader = { + op: ModifyEnum + type: number + id: number +} + +export const ModifyDeleteHeaderByteSize = 6 + +export const packModifyDeleteHeader = (obj: ModifyDeleteHeader): bigint => { + let val = 0n + val |= (BigInt(obj.op) & 255n) << 0n + val |= (BigInt(obj.type) & 255n) << 8n + val |= (BigInt(obj.id) & 4294967295n) << 16n + return val +} + +export const unpackModifyDeleteHeader = (val: bigint): ModifyDeleteHeader => { + return { + op: (Number((val >> 0n) & 255n)) as ModifyEnum, + type: Number((val >> 8n) & 255n), + id: Number((val >> 16n) & 4294967295n), + } +} + +export const writeModifyDeleteHeader = ( + buf: Uint8Array, + header: ModifyDeleteHeader, + offset: number, +): number => { + buf[offset] = Number(header.op) + offset += 1 + buf[offset] = Number(header.type) + offset += 1 + writeUint32(buf, Number(header.id), offset) + offset += 4 + return offset +} + +export const writeModifyDeleteHeaderProps = { + op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { + buf[offset] = Number(value) + }, + type: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + id: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, +} + +export const readModifyDeleteHeader = ( + buf: Uint8Array, + offset: number, +): ModifyDeleteHeader => { + const value: ModifyDeleteHeader = { + op: (buf[offset]) as ModifyEnum, + type: buf[offset + 1], + id: readUint32(buf, offset + 2), + } + return value +} + +export const readModifyDeleteHeaderProps = { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), +} + +export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyDeleteHeaderByteSize) + writeModifyDeleteHeader(buffer, header, 0) + return buffer +} + +export const pushModifyDeleteHeader = ( + buf: AutoSizedUint8Array, + header: ModifyDeleteHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.type)) + buf.pushU32(Number(header.id)) + return index +} + +export type ModifyCreateHeader = { + op: ModifyEnum + type: number + size: number +} + +export const ModifyCreateHeaderByteSize = 6 + +export const packModifyCreateHeader = (obj: ModifyCreateHeader): bigint => { + let val = 0n + val |= (BigInt(obj.op) & 255n) << 0n + val |= (BigInt(obj.type) & 255n) << 8n + val |= (BigInt(obj.size) & 4294967295n) << 16n + return val +} + +export const unpackModifyCreateHeader = (val: bigint): ModifyCreateHeader => { + return { + op: (Number((val >> 0n) & 255n)) as ModifyEnum, + type: Number((val >> 8n) & 255n), + size: Number((val >> 16n) & 4294967295n), + } +} + +export const writeModifyCreateHeader = ( + buf: Uint8Array, + header: ModifyCreateHeader, + offset: number, +): number => { + buf[offset] = Number(header.op) + offset += 1 + buf[offset] = Number(header.type) + offset += 1 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyCreateHeaderProps = { + op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { + buf[offset] = Number(value) + }, + type: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, +} + +export const readModifyCreateHeader = ( + buf: Uint8Array, + offset: number, +): ModifyCreateHeader => { + const value: ModifyCreateHeader = { + op: (buf[offset]) as ModifyEnum, + type: buf[offset + 1], + size: readUint32(buf, offset + 2), + } + return value +} + +export const readModifyCreateHeaderProps = { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), +} + +export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyCreateHeaderByteSize) + writeModifyCreateHeader(buffer, header, 0) + return buffer +} + +export const pushModifyCreateHeader = ( + buf: AutoSizedUint8Array, + header: ModifyCreateHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.type)) + buf.pushU32(Number(header.size)) + return index +} + +export type ModifyMainHeader = { + id: number + start: number + size: number +} + +export const ModifyMainHeaderByteSize = 5 + +export const packModifyMainHeader = (obj: ModifyMainHeader): bigint => { + let val = 0n + val |= (BigInt(obj.id) & 255n) << 0n + val |= (BigInt(obj.start) & 65535n) << 8n + val |= (BigInt(obj.size) & 65535n) << 24n + return val +} + +export const unpackModifyMainHeader = (val: bigint): ModifyMainHeader => { + return { + id: Number((val >> 0n) & 255n), + start: Number((val >> 8n) & 65535n), + size: Number((val >> 24n) & 65535n), + } +} + +export const writeModifyMainHeader = ( + buf: Uint8Array, + header: ModifyMainHeader, + offset: number, +): number => { + buf[offset] = Number(header.id) + offset += 1 + writeUint16(buf, Number(header.start), offset) + offset += 2 + writeUint16(buf, Number(header.size), offset) + offset += 2 + return offset +} + +export const writeModifyMainHeaderProps = { + id: (buf: Uint8Array, value: number, offset: number) => { + buf[offset] = Number(value) + }, + start: (buf: Uint8Array, value: number, offset: number) => { + writeUint16(buf, Number(value), offset + 1) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint16(buf, Number(value), offset + 3) + }, +} + +export const readModifyMainHeader = ( + buf: Uint8Array, + offset: number, +): ModifyMainHeader => { + const value: ModifyMainHeader = { + id: buf[offset], + start: readUint16(buf, offset + 1), + size: readUint16(buf, offset + 3), + } + return value +} + +export const readModifyMainHeaderProps = { + id: (buf: Uint8Array, offset: number) => buf[offset], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), +} + +export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyMainHeaderByteSize) + writeModifyMainHeader(buffer, header, 0) + return buffer +} + +export const pushModifyMainHeader = ( + buf: AutoSizedUint8Array, + header: ModifyMainHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.id)) + buf.pushU16(Number(header.start)) + buf.pushU16(Number(header.size)) + return index +} + +export type ModifyPropHeader = { + id: number + type: number + size: number +} + +export const ModifyPropHeaderByteSize = 6 + +export const packModifyPropHeader = (obj: ModifyPropHeader): bigint => { + let val = 0n + val |= (BigInt(obj.id) & 255n) << 0n + val |= (BigInt(obj.type) & 255n) << 8n + val |= (BigInt(obj.size) & 4294967295n) << 16n + return val +} + +export const unpackModifyPropHeader = (val: bigint): ModifyPropHeader => { + return { + id: Number((val >> 0n) & 255n), + type: Number((val >> 8n) & 255n), + size: Number((val >> 16n) & 4294967295n), + } +} + +export const writeModifyPropHeader = ( + buf: Uint8Array, + header: ModifyPropHeader, + offset: number, +): number => { + buf[offset] = Number(header.id) + offset += 1 + buf[offset] = Number(header.type) + offset += 1 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyPropHeaderProps = { + id: (buf: Uint8Array, value: number, offset: number) => { + buf[offset] = Number(value) + }, + type: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, +} + +export const readModifyPropHeader = ( + buf: Uint8Array, + offset: number, +): ModifyPropHeader => { + const value: ModifyPropHeader = { + id: buf[offset], + type: buf[offset + 1], + size: readUint32(buf, offset + 2), + } + return value +} + +export const readModifyPropHeaderProps = { + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), +} + +export const createModifyPropHeader = (header: ModifyPropHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyPropHeaderByteSize) + writeModifyPropHeader(buffer, header, 0) + return buffer +} + +export const pushModifyPropHeader = ( + buf: AutoSizedUint8Array, + header: ModifyPropHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.id)) + buf.pushU8(Number(header.type)) + buf.pushU32(Number(header.size)) + return index +} + +export const ModifyReferences = { + clear: 0, + ids: 1, + idsAndEdges: 2, + tmpIds: 3, + delIds: 4, + delTmpIds: 5, +} as const + +export const ModifyReferencesInverse = { + 0: 'clear', + 1: 'ids', + 2: 'idsAndEdges', + 3: 'tmpIds', + 4: 'delIds', + 5: 'delTmpIds', +} as const + +/** + clear, + ids, + idsAndEdges, + tmpIds, + delIds, + delTmpIds + */ +export type ModifyReferencesEnum = (typeof ModifyReferences)[keyof typeof ModifyReferences] + +export type ModifyReferencesHeader = { + op: ModifyReferencesEnum + size: number +} + +export const ModifyReferencesHeaderByteSize = 5 + +export const packModifyReferencesHeader = (obj: ModifyReferencesHeader): bigint => { + let val = 0n + val |= (BigInt(obj.op) & 255n) << 0n + val |= (BigInt(obj.size) & 4294967295n) << 8n + return val +} + +export const unpackModifyReferencesHeader = (val: bigint): ModifyReferencesHeader => { + return { + op: (Number((val >> 0n) & 255n)) as ModifyReferencesEnum, + size: Number((val >> 8n) & 4294967295n), + } +} + +export const writeModifyReferencesHeader = ( + buf: Uint8Array, + header: ModifyReferencesHeader, + offset: number, +): number => { + buf[offset] = Number(header.op) + offset += 1 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyReferencesHeaderProps = { + op: (buf: Uint8Array, value: ModifyReferencesEnum, offset: number) => { + buf[offset] = Number(value) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 1) + }, +} + +export const readModifyReferencesHeader = ( + buf: Uint8Array, + offset: number, +): ModifyReferencesHeader => { + const value: ModifyReferencesHeader = { + op: (buf[offset]) as ModifyReferencesEnum, + size: readUint32(buf, offset + 1), + } + return value +} + +export const readModifyReferencesHeaderProps = { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyReferencesEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), +} + +export const createModifyReferencesHeader = (header: ModifyReferencesHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyReferencesHeaderByteSize) + writeModifyReferencesHeader(buffer, header, 0) + return buffer +} + +export const pushModifyReferencesHeader = ( + buf: AutoSizedUint8Array, + header: ModifyReferencesHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU32(Number(header.size)) + return index +} + +export type ModifyEdgesHeader = { + id: number + withIndex: boolean + index: number + size: number +} + +export const ModifyEdgesHeaderByteSize = 13 + +export const packModifyEdgesHeader = (obj: ModifyEdgesHeader): bigint => { + let val = 0n + val |= (BigInt(obj.id) & 4294967295n) << 0n + val |= ((obj.withIndex ? 1n : 0n) & 1n) << 32n + val |= (BigInt(obj.index) & 4294967295n) << 33n + val |= (BigInt(obj.size) & 4294967295n) << 65n + return val +} + +export const unpackModifyEdgesHeader = (val: bigint): ModifyEdgesHeader => { + return { + id: Number((val >> 0n) & 4294967295n), + withIndex: ((val >> 32n) & 1n) === 1n, + index: Number((val >> 33n) & 4294967295n), + size: Number((val >> 65n) & 4294967295n), + } +} + +export const writeModifyEdgesHeader = ( + buf: Uint8Array, + header: ModifyEdgesHeader, + offset: number, +): number => { + writeUint32(buf, Number(header.id), offset) + offset += 4 + buf[offset] = 0 + buf[offset] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= ((header.index >>> 0) & 127) << 1 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.index >>> 7) & 255) << 0 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.index >>> 15) & 255) << 0 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.index >>> 23) & 255) << 0 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.index >>> 31) & 1) << 0 + buf[offset] |= ((header.size >>> 0) & 127) << 1 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.size >>> 7) & 255) << 0 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.size >>> 15) & 255) << 0 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.size >>> 23) & 255) << 0 + offset += 1 + buf[offset] = 0 + buf[offset] |= ((header.size >>> 31) & 1) << 0 + return offset +} + +export const writeModifyEdgesHeaderProps = { + id: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset) + }, + withIndex: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 4] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + }, + index: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 4] |= ((value >>> 0) & 127) << 1 + buf[offset + 5] |= ((value >>> 7) & 255) << 0 + buf[offset + 6] |= ((value >>> 15) & 255) << 0 + buf[offset + 7] |= ((value >>> 23) & 255) << 0 + buf[offset + 8] |= ((value >>> 31) & 1) << 0 + }, + size: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 8] |= ((value >>> 0) & 127) << 1 + buf[offset + 9] |= ((value >>> 7) & 255) << 0 + buf[offset + 10] |= ((value >>> 15) & 255) << 0 + buf[offset + 11] |= ((value >>> 23) & 255) << 0 + buf[offset + 12] |= ((value >>> 31) & 1) << 0 + }, +} + +export const readModifyEdgesHeader = ( + buf: Uint8Array, + offset: number, +): ModifyEdgesHeader => { + const value: ModifyEdgesHeader = { + id: readUint32(buf, offset), + withIndex: (((buf[offset + 4] >>> 0) & 1)) === 1, + index: (((buf[offset + 4] >>> 1) & 127) | (((buf[offset + 5] >>> 0) & 255) << 7) | (((buf[offset + 6] >>> 0) & 255) << 15) | (((buf[offset + 7] >>> 0) & 255) << 23) | (((buf[offset + 8] >>> 0) & 1) << 31)), + size: (((buf[offset + 8] >>> 1) & 127) | (((buf[offset + 9] >>> 0) & 255) << 7) | (((buf[offset + 10] >>> 0) & 255) << 15) | (((buf[offset + 11] >>> 0) & 255) << 23) | (((buf[offset + 12] >>> 0) & 1) << 31)), + } + return value +} + +export const readModifyEdgesHeaderProps = { + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + index: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 127) | (((buf[offset + 5] >>> 0) & 255) << 7) | (((buf[offset + 6] >>> 0) & 255) << 15) | (((buf[offset + 7] >>> 0) & 255) << 23) | (((buf[offset + 8] >>> 0) & 1) << 31)), + size: (buf: Uint8Array, offset: number) => (((buf[offset + 8] >>> 1) & 127) | (((buf[offset + 9] >>> 0) & 255) << 7) | (((buf[offset + 10] >>> 0) & 255) << 15) | (((buf[offset + 11] >>> 0) & 255) << 23) | (((buf[offset + 12] >>> 0) & 1) << 31)), +} + +export const createModifyEdgesHeader = (header: ModifyEdgesHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyEdgesHeaderByteSize) + writeModifyEdgesHeader(buffer, header, 0) + return buffer +} + +export const pushModifyEdgesHeader = ( + buf: AutoSizedUint8Array, + header: ModifyEdgesHeader, +): number => { + const index = buf.length + buf.pushU32(Number(header.id)) + buf.pushU8(0) + buf.view[buf.length - 1] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((header.index >>> 0) & 127) << 1 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.index >>> 7) & 255) << 0 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.index >>> 15) & 255) << 0 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.index >>> 23) & 255) << 0 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.index >>> 31) & 1) << 0 + buf.view[buf.length - 1] |= ((header.size >>> 0) & 127) << 1 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.size >>> 7) & 255) << 0 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.size >>> 15) & 255) << 0 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.size >>> 23) & 255) << 0 + buf.pushU8(0) + buf.view[buf.length - 1] |= ((header.size >>> 31) & 1) << 0 + return index +} + export const PropType = { null: 0, timestamp: 1, @@ -285,29 +1085,17 @@ export type PropTypeEnum = (typeof PropType)[keyof typeof PropType] export const RefOp = { clear: 0, del: 1, - end: 2, + end: ModOp.end, set: 3, - setIndex: 4, - setTmp: 5, - setEdge: 6, - setIndexTmp: 7, - setEdgeIndex: 8, - setEdgeIndexTmp: 9, - setEdgeTmp: 10, + setEdge: 4, } as const export const RefOpInverse = { 0: 'clear', 1: 'del', - 2: 'end', + [ModOp.end]: 'end', 3: 'set', - 4: 'setIndex', - 5: 'setTmp', - 6: 'setEdge', - 7: 'setIndexTmp', - 8: 'setEdgeIndex', - 9: 'setEdgeIndexTmp', - 10: 'setEdgeTmp', + 4: 'setEdge', } as const /** @@ -315,13 +1103,7 @@ export const RefOpInverse = { del, end, set, - setIndex, - setTmp, - setEdge, - setIndexTmp, - setEdgeIndex, - setEdgeIndexTmp, - setEdgeTmp + setEdge */ export type RefOpEnum = (typeof RefOp)[keyof typeof RefOp] @@ -1160,6 +1942,21 @@ export const createSortHeader = (header: SortHeader): Uint8Array => { return buffer } +export const pushSortHeader = ( + buf: AutoSizedUint8Array, + header: SortHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.order)) + buf.pushU8(Number(header.prop)) + buf.pushU8(Number(header.propType)) + buf.pushU16(Number(header.start)) + buf.pushU16(Number(header.len)) + buf.pushU8(Number(header.lang)) + buf.pushU16(Number(header.edgeType)) + return index +} + export const QUERY_ITERATOR_DEFAULT = 0 export const QUERY_ITERATOR_EDGE = 20 export const QUERY_ITERATOR_EDGE_INCLUDE = 30 @@ -1419,6 +2216,17 @@ export const createIncludeHeader = (header: IncludeHeader): Uint8Array => { return buffer } +export const pushIncludeHeader = ( + buf: AutoSizedUint8Array, + header: IncludeHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.prop)) + buf.pushU8(Number(header.propType)) + return index +} + export type IncludeMetaHeader = { op: IncludeOpEnum prop: number @@ -1493,6 +2301,17 @@ export const createIncludeMetaHeader = (header: IncludeMetaHeader): Uint8Array = return buffer } +export const pushIncludeMetaHeader = ( + buf: AutoSizedUint8Array, + header: IncludeMetaHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.prop)) + buf.pushU8(Number(header.propType)) + return index +} + export type IncludePartialHeader = { op: IncludeOpEnum prop: number @@ -1577,6 +2396,18 @@ export const createIncludePartialHeader = (header: IncludePartialHeader): Uint8A return buffer } +export const pushIncludePartialHeader = ( + buf: AutoSizedUint8Array, + header: IncludePartialHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.prop)) + buf.pushU8(Number(header.propType)) + buf.pushU16(Number(header.amount)) + return index +} + export type IncludePartialProp = { start: number size: number @@ -1641,6 +2472,16 @@ export const createIncludePartialProp = (header: IncludePartialProp): Uint8Array return buffer } +export const pushIncludePartialProp = ( + buf: AutoSizedUint8Array, + header: IncludePartialProp, +): number => { + const index = buf.length + buf.pushU16(Number(header.start)) + buf.pushU16(Number(header.size)) + return index +} + export type IncludeOpts = { end: number isChars: boolean @@ -1736,6 +2577,21 @@ export const createIncludeOpts = (header: IncludeOpts): Uint8Array => { return buffer } +export const pushIncludeOpts = ( + buf: AutoSizedUint8Array, + header: IncludeOpts, +): number => { + const index = buf.length + buf.pushU32(Number(header.end)) + buf.pushU8(0) + buf.view[buf.length - 1] |= (((header.isChars ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= (((header.hasOpts ? 1 : 0) >>> 0) & 1) << 1 + buf.view[buf.length - 1] |= ((0 >>> 0) & 63) << 2 + buf.pushU8(Number(header.langFallbackSize)) + buf.pushU8(Number(header.lang)) + return index +} + export type IncludeResponse = { prop: number size: number @@ -1800,6 +2656,16 @@ export const createIncludeResponse = (header: IncludeResponse): Uint8Array => { return buffer } +export const pushIncludeResponse = ( + buf: AutoSizedUint8Array, + header: IncludeResponse, +): number => { + const index = buf.length + buf.pushU8(Number(header.prop)) + buf.pushU32(Number(header.size)) + return index +} + export type IncludeResponseMeta = { op: ReadOpEnum prop: number @@ -1906,6 +2772,22 @@ export const createIncludeResponseMeta = (header: IncludeResponseMeta): Uint8Arr return buffer } +export const pushIncludeResponseMeta = ( + buf: AutoSizedUint8Array, + header: IncludeResponseMeta, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.prop)) + buf.pushU8(Number(header.lang)) + buf.pushU8(0) + buf.view[buf.length - 1] |= (((header.compressed ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 + buf.pushU32(Number(header.crc32)) + buf.pushU32(Number(header.size)) + return index +} + export type SubscriptionHeader = { op: OpTypeEnum typeId: TypeId @@ -1990,6 +2872,18 @@ export const createSubscriptionHeader = (header: SubscriptionHeader): Uint8Array return buffer } +export const pushSubscriptionHeader = ( + buf: AutoSizedUint8Array, + header: SubscriptionHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU16(Number(header.typeId)) + buf.pushU8(Number(header.fieldsLen)) + buf.pushU8(Number(header.partialLen)) + return index +} + export type QueryHeader = { op: QueryTypeEnum prop: number @@ -2176,6 +3070,30 @@ export const createQueryHeader = (header: QueryHeader): Uint8Array => { return buffer } +export const pushQueryHeader = ( + buf: AutoSizedUint8Array, + header: QueryHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.prop)) + buf.pushU16(Number(header.typeId)) + buf.pushU16(Number(header.edgeTypeId)) + buf.pushU32(Number(header.offset)) + buf.pushU32(Number(header.limit)) + buf.pushU16(Number(header.filterSize)) + buf.pushU16(Number(header.searchSize)) + buf.pushU16(Number(header.edgeSize)) + buf.pushU16(Number(header.edgeFilterSize)) + buf.pushU16(Number(header.includeSize)) + buf.pushU8(Number(header.iteratorType)) + buf.pushU16(Number(header.size)) + buf.pushU8(0) + buf.view[buf.length - 1] |= (((header.sort ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 + return index +} + export type QueryHeaderSingle = { op: QueryTypeEnum typeId: TypeId @@ -2290,6 +3208,21 @@ export const createQueryHeaderSingle = (header: QueryHeaderSingle): Uint8Array = return buffer } +export const pushQueryHeaderSingle = ( + buf: AutoSizedUint8Array, + header: QueryHeaderSingle, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU16(Number(header.typeId)) + buf.pushU8(Number(header.prop)) + buf.pushU32(Number(header.id)) + buf.pushU16(Number(header.filterSize)) + buf.pushU16(Number(header.includeSize)) + buf.pushU16(Number(header.aliasSize)) + return index +} + export type QueryHeaderSingleReference = { op: QueryTypeEnum prop: number @@ -2394,6 +3327,20 @@ export const createQueryHeaderSingleReference = (header: QueryHeaderSingleRefere return buffer } +export const pushQueryHeaderSingleReference = ( + buf: AutoSizedUint8Array, + header: QueryHeaderSingleReference, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU8(Number(header.prop)) + buf.pushU16(Number(header.typeId)) + buf.pushU16(Number(header.edgeTypeId)) + buf.pushU16(Number(header.edgeSize)) + buf.pushU16(Number(header.includeSize)) + return index +} + export const VectorBaseType = { int8: 1, uint8: 2, @@ -2592,6 +3539,28 @@ export const createAggHeader = (header: AggHeader): Uint8Array => { return buffer } +export const pushAggHeader = ( + buf: AutoSizedUint8Array, + header: AggHeader, +): number => { + const index = buf.length + buf.pushU8(Number(header.op)) + buf.pushU16(Number(header.typeId)) + buf.pushU32(Number(header.offset)) + buf.pushU32(Number(header.limit)) + buf.pushU16(Number(header.filterSize)) + buf.pushU8(Number(header.iteratorType)) + buf.pushU16(Number(header.size)) + buf.pushU16(Number(header.resultsSize)) + buf.pushU16(Number(header.accumulatorSize)) + buf.pushU8(0) + buf.view[buf.length - 1] |= (((header.sort ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= (((header.hasGroupBy ? 1 : 0) >>> 0) & 1) << 1 + buf.view[buf.length - 1] |= (((header.isSamplingSet ? 1 : 0) >>> 0) & 1) << 2 + buf.view[buf.length - 1] |= ((0 >>> 0) & 31) << 3 + return index +} + export type addMultiSubscriptionHeader = { typeId: number } @@ -2646,6 +3615,15 @@ export const createaddMultiSubscriptionHeader = (header: addMultiSubscriptionHea return buffer } +export const pushaddMultiSubscriptionHeader = ( + buf: AutoSizedUint8Array, + header: addMultiSubscriptionHeader, +): number => { + const index = buf.length + buf.pushU16(Number(header.typeId)) + return index +} + export type removeMultiSubscriptionHeader = { typeId: number } @@ -2700,6 +3678,15 @@ export const createremoveMultiSubscriptionHeader = (header: removeMultiSubscript return buffer } +export const pushremoveMultiSubscriptionHeader = ( + buf: AutoSizedUint8Array, + header: removeMultiSubscriptionHeader, +): number => { + const index = buf.length + buf.pushU16(Number(header.typeId)) + return index +} + export type AggProp = { propId: number propType: PropTypeEnum @@ -2804,6 +3791,20 @@ export const createAggProp = (header: AggProp): Uint8Array => { return buffer } +export const pushAggProp = ( + buf: AutoSizedUint8Array, + header: AggProp, +): number => { + const index = buf.length + buf.pushU8(Number(header.propId)) + buf.pushU8(Number(header.propType)) + buf.pushU16(Number(header.propDefStart)) + buf.pushU8(Number(header.aggFunction)) + buf.pushU16(Number(header.resultPos)) + buf.pushU16(Number(header.accumulatorPos)) + return index +} + export type GroupByKeyProp = { propId: number propType: PropTypeEnum @@ -2908,6 +3909,20 @@ export const createGroupByKeyProp = (header: GroupByKeyProp): Uint8Array => { return buffer } +export const pushGroupByKeyProp = ( + buf: AutoSizedUint8Array, + header: GroupByKeyProp, +): number => { + const index = buf.length + buf.pushU8(Number(header.propId)) + buf.pushU8(Number(header.propType)) + buf.pushU16(Number(header.propDefStart)) + buf.pushU8(Number(header.stepType)) + buf.pushU32(Number(header.stepRange)) + buf.pushU16(Number(header.timezone)) + return index +} + export const FilterOpCompare = { eq: 4, neq: 5, @@ -3042,6 +4057,16 @@ export const createFilterOp = (header: FilterOp): Uint8Array => { return buffer } +export const pushFilterOp = ( + buf: AutoSizedUint8Array, + header: FilterOp, +): number => { + const index = buf.length + buf.pushU8(Number(header.prop)) + buf.pushU8(Number(header.compare)) + return index +} + export type FilterCondition = { op: FilterOp size: number @@ -3156,6 +4181,21 @@ export const createFilterCondition = (header: FilterCondition): Uint8Array => { return buffer } +export const pushFilterCondition = ( + buf: AutoSizedUint8Array, + header: FilterCondition, +): number => { + const index = buf.length + buf.pushU16(Number(packFilterOp(header.op))) + buf.pushU32(Number(header.size)) + buf.pushU8(Number(header.prop)) + buf.pushU16(Number(header.start)) + buf.pushU8(Number(header.len)) + buf.pushU64(header.fieldSchema) + buf.pushU8(Number(header.offset)) + return index +} + export type FilterSelect = { typeId: TypeId edgeTypeId: TypeId @@ -3230,3 +4270,14 @@ export const createFilterSelect = (header: FilterSelect): Uint8Array => { return buffer } +export const pushFilterSelect = ( + buf: AutoSizedUint8Array, + header: FilterSelect, +): number => { + const index = buf.length + buf.pushU16(Number(header.typeId)) + buf.pushU16(Number(header.edgeTypeId)) + buf.pushU32(Number(header.size)) + return index +} + diff --git a/test/youzi.ts b/test/youzi.ts index 0264210cd9..df4ac8caf6 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -1,45 +1,127 @@ import { BasedDb } from '../src/index.js' -import { equal } from './shared/assert.js' +import { AutoSizedUint8Array } from '../src/modify/AutoSizedUint8Array.js' +import { getTypeDefs, serializeCreate } from '../src/modify/index.js' +import { parseSchema } from '../src/schema.js' +import { LangCode, Modify, pushModifyHeader } from '../src/zigTsExports.js' import test from './shared/test.js' -await test('reffies', async (t) => { - const db = new BasedDb({ - path: t.tmp, +await test.skip('schema defs', async (t) => { + const schema = parseSchema({ + types: { + // role: { + // name: 'string', + // }, + user: { + age: 'number', + rating: 'uint8', + nested: { + props: { + friends: { + items: { + ref: 'user', + prop: 'nested.friends', + $rating: 'number', + }, + }, + }, + }, + }, + // article: { + // user: { + // ref: 'user', + // prop: 'articles', + // $rating: 'number', + // $role: { + // ref: 'role', + // }, + // }, + // users: { + // items: { + // ref: 'user', + // prop: 'favourites', + // }, + // }, + // }, + }, }) + const defs = getTypeDefs(schema) +}) +await test('modify', async (t) => { + const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ types: { user: { - name: 'string', - others: { - items: { - ref: 'user', - prop: 'others', - // $rating: 'number', - }, - }, + age: 'number', + rating: 'uint8', }, }, }) - const userId = await db.create('user', { name: 'a' }) - - await db.create('user', { - // others: [ - // { - // id: userId, - // // $rating: 1, - // }, - // ], - others: [userId], - name: 'bxxxxxxxx', + const buf = new AutoSizedUint8Array() + const index = pushModifyHeader(buf, { + opId: 0, // is filled on server + opType: 0, // is filled on server + schema: 0, + count: 1, }) + serializeCreate( + db.client.schema!, + 'user', + { + age: 32, + rating: 5, + }, + buf, + LangCode.nl, + ) - const res = await db.query('user').include('*', '**').get().toObject() + await db.server.modify(new Uint8Array(buf.view)) + buf.flush() - console.dir(res, { depth: null }) + console.log('done did it!') }) + +// await test('reffies', async (t) => { +// const db = new BasedDb({ +// path: t.tmp, +// }) + +// await db.start({ clean: true }) + +// t.after(() => t.backup(db)) + +// await db.setSchema({ +// types: { +// user: { +// name: 'string', +// others: { +// items: { +// ref: 'user', +// prop: 'others', +// $rating: 'number', +// }, +// }, +// }, +// }, +// }) + +// const userId = await db.create('user', { name: 'a' }) + +// await db.create('user', { +// others: [ +// { +// id: userId, +// $rating: 20, +// }, +// ], +// // others: [userId], +// name: 'bxxxxxxxx', +// }) + +// const res = await db.query('user').include('*', '**').get().toObject() + +// console.dir(res, { depth: null }) +// }) diff --git a/tsconfig.json b/tsconfig.json index a8678cdf2c..97e1789593 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "noEmit": true, "jsx": "react", "declarationMap": true, + "strictPropertyInitialization": true, "pretty": true }, "include": ["src", "test", "scripts"], From 08eff8eb748d9a19b428361b938a453266046ef8 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 27 Jan 2026 14:19:48 +0100 Subject: [PATCH 002/449] wip --- {src/db-client/modify => _modify}/Ctx.ts | 0 {src/db-client/modify => _modify}/Tmp.ts | 0 .../modify => _modify}/create/index.ts | 0 .../modify => _modify}/create/mark.ts | 0 {src/db-client/modify => _modify}/cursor.ts | 0 .../modify => _modify}/delete/index.ts | 0 {src/db-client/modify => _modify}/drain.ts | 0 .../modify => _modify}/edges/binary.ts | 0 .../modify => _modify}/edges/cardinality.ts | 0 .../modify => _modify}/edges/header.ts | 0 .../modify => _modify}/edges/index.ts | 0 .../modify => _modify}/edges/reference.ts | 0 .../modify => _modify}/edges/references.ts | 0 .../modify => _modify}/edges/separate.ts | 0 .../modify => _modify}/edges/string.ts | 0 {src/db-client/modify => _modify}/error.ts | 0 .../modify => _modify}/expire/index.ts | 0 .../modify => _modify}/props/alias.ts | 0 .../modify => _modify}/props/binary.ts | 0 .../modify => _modify}/props/cardinality.ts | 0 .../modify => _modify}/props/delete.ts | 0 .../modify => _modify}/props/fixed.ts | 0 .../modify => _modify}/props/increment.ts | 0 .../modify => _modify}/props/json.ts | 0 .../modify => _modify}/props/main.ts | 0 .../modify => _modify}/props/object.ts | 0 .../modify => _modify}/props/reference.ts | 0 .../modify => _modify}/props/references.ts | 0 .../modify => _modify}/props/separate.ts | 0 .../modify => _modify}/props/string.ts | 0 .../modify => _modify}/props/text.ts | 0 .../modify => _modify}/props/vector.ts | 0 {src/db-client/modify => _modify}/resize.ts | 0 _modify/types.ts | 35 ++ {src/db-client/modify => _modify}/uint.ts | 0 .../modify => _modify}/update/index.ts | 0 .../modify => _modify}/upsert/index.ts | 0 {src/db-client/modify => _modify}/validate.ts | 0 src/db-client/string.ts => _string.ts | 14 +- native/errors.zig | 5 - native/modify/edges.zig | 2 +- native/modify/modify.zig | 162 ++++++++- native/modify/reference.zig | 2 +- native/modify/references.zig | 1 - native/selva/node.zig | 16 +- native/selva/references.zig | 25 +- native/selva/selva.zig | 5 +- native/types.zig | 36 +- scripts/zigTsExports.ts | 2 +- src/db-client/hooks.ts | 10 +- src/db-client/index.ts | 251 +++++-------- .../modify/AutoSizedUint8Array.ts | 26 +- src/{ => db-client}/modify/defs/base.ts | 10 +- src/{ => db-client}/modify/defs/fixed.ts | 12 +- .../modify/defs/getTypeDefs.ts | 2 +- src/{ => db-client}/modify/defs/index.ts | 10 +- src/{ => db-client}/modify/defs/references.ts | 84 +++-- src/{ => db-client}/modify/defs/vars.ts | 29 +- src/db-client/modify/index.ts | 286 +++++++++++++++ src/db-client/modify/types.ts | 81 +++-- src/db-client/setLocalClientSchema.ts | 15 +- src/db-server/index.ts | 2 +- src/db-server/schemaSelvaBuffer.ts | 78 ++-- src/index.ts | 30 +- src/modify/index.ts | 120 ------- src/modify/types.ts | 60 ---- src/zigTsExports.ts | 333 +++++++++++++----- test/include/thread.perf.ts | 6 +- test/scenarios/e-commerce.ts | 2 +- test/youzi.ts | 83 ++++- tsconfig.build.json | 3 +- tsconfig.json | 2 +- 72 files changed, 1188 insertions(+), 652 deletions(-) rename {src/db-client/modify => _modify}/Ctx.ts (100%) rename {src/db-client/modify => _modify}/Tmp.ts (100%) rename {src/db-client/modify => _modify}/create/index.ts (100%) rename {src/db-client/modify => _modify}/create/mark.ts (100%) rename {src/db-client/modify => _modify}/cursor.ts (100%) rename {src/db-client/modify => _modify}/delete/index.ts (100%) rename {src/db-client/modify => _modify}/drain.ts (100%) rename {src/db-client/modify => _modify}/edges/binary.ts (100%) rename {src/db-client/modify => _modify}/edges/cardinality.ts (100%) rename {src/db-client/modify => _modify}/edges/header.ts (100%) rename {src/db-client/modify => _modify}/edges/index.ts (100%) rename {src/db-client/modify => _modify}/edges/reference.ts (100%) rename {src/db-client/modify => _modify}/edges/references.ts (100%) rename {src/db-client/modify => _modify}/edges/separate.ts (100%) rename {src/db-client/modify => _modify}/edges/string.ts (100%) rename {src/db-client/modify => _modify}/error.ts (100%) rename {src/db-client/modify => _modify}/expire/index.ts (100%) rename {src/db-client/modify => _modify}/props/alias.ts (100%) rename {src/db-client/modify => _modify}/props/binary.ts (100%) rename {src/db-client/modify => _modify}/props/cardinality.ts (100%) rename {src/db-client/modify => _modify}/props/delete.ts (100%) rename {src/db-client/modify => _modify}/props/fixed.ts (100%) rename {src/db-client/modify => _modify}/props/increment.ts (100%) rename {src/db-client/modify => _modify}/props/json.ts (100%) rename {src/db-client/modify => _modify}/props/main.ts (100%) rename {src/db-client/modify => _modify}/props/object.ts (100%) rename {src/db-client/modify => _modify}/props/reference.ts (100%) rename {src/db-client/modify => _modify}/props/references.ts (100%) rename {src/db-client/modify => _modify}/props/separate.ts (100%) rename {src/db-client/modify => _modify}/props/string.ts (100%) rename {src/db-client/modify => _modify}/props/text.ts (100%) rename {src/db-client/modify => _modify}/props/vector.ts (100%) rename {src/db-client/modify => _modify}/resize.ts (100%) create mode 100644 _modify/types.ts rename {src/db-client/modify => _modify}/uint.ts (100%) rename {src/db-client/modify => _modify}/update/index.ts (100%) rename {src/db-client/modify => _modify}/upsert/index.ts (100%) rename {src/db-client/modify => _modify}/validate.ts (100%) rename src/db-client/string.ts => _string.ts (86%) rename src/{ => db-client}/modify/AutoSizedUint8Array.ts (92%) rename src/{ => db-client}/modify/defs/base.ts (72%) rename src/{ => db-client}/modify/defs/fixed.ts (85%) rename src/{ => db-client}/modify/defs/getTypeDefs.ts (99%) rename src/{ => db-client}/modify/defs/index.ts (84%) rename src/{ => db-client}/modify/defs/references.ts (72%) rename src/{ => db-client}/modify/defs/vars.ts (81%) create mode 100644 src/db-client/modify/index.ts delete mode 100644 src/modify/index.ts delete mode 100644 src/modify/types.ts diff --git a/src/db-client/modify/Ctx.ts b/_modify/Ctx.ts similarity index 100% rename from src/db-client/modify/Ctx.ts rename to _modify/Ctx.ts diff --git a/src/db-client/modify/Tmp.ts b/_modify/Tmp.ts similarity index 100% rename from src/db-client/modify/Tmp.ts rename to _modify/Tmp.ts diff --git a/src/db-client/modify/create/index.ts b/_modify/create/index.ts similarity index 100% rename from src/db-client/modify/create/index.ts rename to _modify/create/index.ts diff --git a/src/db-client/modify/create/mark.ts b/_modify/create/mark.ts similarity index 100% rename from src/db-client/modify/create/mark.ts rename to _modify/create/mark.ts diff --git a/src/db-client/modify/cursor.ts b/_modify/cursor.ts similarity index 100% rename from src/db-client/modify/cursor.ts rename to _modify/cursor.ts diff --git a/src/db-client/modify/delete/index.ts b/_modify/delete/index.ts similarity index 100% rename from src/db-client/modify/delete/index.ts rename to _modify/delete/index.ts diff --git a/src/db-client/modify/drain.ts b/_modify/drain.ts similarity index 100% rename from src/db-client/modify/drain.ts rename to _modify/drain.ts diff --git a/src/db-client/modify/edges/binary.ts b/_modify/edges/binary.ts similarity index 100% rename from src/db-client/modify/edges/binary.ts rename to _modify/edges/binary.ts diff --git a/src/db-client/modify/edges/cardinality.ts b/_modify/edges/cardinality.ts similarity index 100% rename from src/db-client/modify/edges/cardinality.ts rename to _modify/edges/cardinality.ts diff --git a/src/db-client/modify/edges/header.ts b/_modify/edges/header.ts similarity index 100% rename from src/db-client/modify/edges/header.ts rename to _modify/edges/header.ts diff --git a/src/db-client/modify/edges/index.ts b/_modify/edges/index.ts similarity index 100% rename from src/db-client/modify/edges/index.ts rename to _modify/edges/index.ts diff --git a/src/db-client/modify/edges/reference.ts b/_modify/edges/reference.ts similarity index 100% rename from src/db-client/modify/edges/reference.ts rename to _modify/edges/reference.ts diff --git a/src/db-client/modify/edges/references.ts b/_modify/edges/references.ts similarity index 100% rename from src/db-client/modify/edges/references.ts rename to _modify/edges/references.ts diff --git a/src/db-client/modify/edges/separate.ts b/_modify/edges/separate.ts similarity index 100% rename from src/db-client/modify/edges/separate.ts rename to _modify/edges/separate.ts diff --git a/src/db-client/modify/edges/string.ts b/_modify/edges/string.ts similarity index 100% rename from src/db-client/modify/edges/string.ts rename to _modify/edges/string.ts diff --git a/src/db-client/modify/error.ts b/_modify/error.ts similarity index 100% rename from src/db-client/modify/error.ts rename to _modify/error.ts diff --git a/src/db-client/modify/expire/index.ts b/_modify/expire/index.ts similarity index 100% rename from src/db-client/modify/expire/index.ts rename to _modify/expire/index.ts diff --git a/src/db-client/modify/props/alias.ts b/_modify/props/alias.ts similarity index 100% rename from src/db-client/modify/props/alias.ts rename to _modify/props/alias.ts diff --git a/src/db-client/modify/props/binary.ts b/_modify/props/binary.ts similarity index 100% rename from src/db-client/modify/props/binary.ts rename to _modify/props/binary.ts diff --git a/src/db-client/modify/props/cardinality.ts b/_modify/props/cardinality.ts similarity index 100% rename from src/db-client/modify/props/cardinality.ts rename to _modify/props/cardinality.ts diff --git a/src/db-client/modify/props/delete.ts b/_modify/props/delete.ts similarity index 100% rename from src/db-client/modify/props/delete.ts rename to _modify/props/delete.ts diff --git a/src/db-client/modify/props/fixed.ts b/_modify/props/fixed.ts similarity index 100% rename from src/db-client/modify/props/fixed.ts rename to _modify/props/fixed.ts diff --git a/src/db-client/modify/props/increment.ts b/_modify/props/increment.ts similarity index 100% rename from src/db-client/modify/props/increment.ts rename to _modify/props/increment.ts diff --git a/src/db-client/modify/props/json.ts b/_modify/props/json.ts similarity index 100% rename from src/db-client/modify/props/json.ts rename to _modify/props/json.ts diff --git a/src/db-client/modify/props/main.ts b/_modify/props/main.ts similarity index 100% rename from src/db-client/modify/props/main.ts rename to _modify/props/main.ts diff --git a/src/db-client/modify/props/object.ts b/_modify/props/object.ts similarity index 100% rename from src/db-client/modify/props/object.ts rename to _modify/props/object.ts diff --git a/src/db-client/modify/props/reference.ts b/_modify/props/reference.ts similarity index 100% rename from src/db-client/modify/props/reference.ts rename to _modify/props/reference.ts diff --git a/src/db-client/modify/props/references.ts b/_modify/props/references.ts similarity index 100% rename from src/db-client/modify/props/references.ts rename to _modify/props/references.ts diff --git a/src/db-client/modify/props/separate.ts b/_modify/props/separate.ts similarity index 100% rename from src/db-client/modify/props/separate.ts rename to _modify/props/separate.ts diff --git a/src/db-client/modify/props/string.ts b/_modify/props/string.ts similarity index 100% rename from src/db-client/modify/props/string.ts rename to _modify/props/string.ts diff --git a/src/db-client/modify/props/text.ts b/_modify/props/text.ts similarity index 100% rename from src/db-client/modify/props/text.ts rename to _modify/props/text.ts diff --git a/src/db-client/modify/props/vector.ts b/_modify/props/vector.ts similarity index 100% rename from src/db-client/modify/props/vector.ts rename to _modify/props/vector.ts diff --git a/src/db-client/modify/resize.ts b/_modify/resize.ts similarity index 100% rename from src/db-client/modify/resize.ts rename to _modify/resize.ts diff --git a/_modify/types.ts b/_modify/types.ts new file mode 100644 index 0000000000..43426e1df9 --- /dev/null +++ b/_modify/types.ts @@ -0,0 +1,35 @@ +import { LangCode, ModOp } from '../../zigTsExports.js' + +export const RANGE_ERR = 1 +export const MOD_OPS_TO_STRING = { + [ModOp.createProp]: 'create', + [ModOp.updateProp]: 'update', + [ModOp.increment]: 'update', + [ModOp.expire]: 'update', +} as const + +export const enum SIZE { + DEFAULT_CURSOR = 11, +} + +export type ModifyOpts = { + unsafe?: boolean + locale?: keyof typeof LangCode +} + +export const NOEDGE_NOINDEX_REALID = 0 +export const EDGE_NOINDEX_REALID = 1 +export const EDGE_INDEX_REALID = 2 +export const NOEDGE_INDEX_REALID = 3 +export const NOEDGE_NOINDEX_TMPID = 4 +export const EDGE_NOINDEX_TMPID = 5 +export const EDGE_INDEX_TMPID = 6 +export const NOEDGE_INDEX_TMPID = 7 + +// export const REF_OP_OVERWRITE = 0 +// export const REF_OP_UPDATE = 1 +// export const REF_OP_DELETE = 2 +// export const REF_OP_PUT_OVERWRITE = 3 +// export const REF_OP_PUT_ADD = 4 + +// export type RefOp = typeof REF_OP_OVERWRITE | typeof REF_OP_UPDATE diff --git a/src/db-client/modify/uint.ts b/_modify/uint.ts similarity index 100% rename from src/db-client/modify/uint.ts rename to _modify/uint.ts diff --git a/src/db-client/modify/update/index.ts b/_modify/update/index.ts similarity index 100% rename from src/db-client/modify/update/index.ts rename to _modify/update/index.ts diff --git a/src/db-client/modify/upsert/index.ts b/_modify/upsert/index.ts similarity index 100% rename from src/db-client/modify/upsert/index.ts rename to _modify/upsert/index.ts diff --git a/src/db-client/modify/validate.ts b/_modify/validate.ts similarity index 100% rename from src/db-client/modify/validate.ts rename to _modify/validate.ts diff --git a/src/db-client/string.ts b/_string.ts similarity index 86% rename from src/db-client/string.ts rename to _string.ts index 69c1a943da..e2b1d6c97e 100644 --- a/src/db-client/string.ts +++ b/_string.ts @@ -1,6 +1,6 @@ import native from '../native.js' -import { Ctx } from './modify/Ctx.js' -import { resize } from './modify/resize.js' +import { Ctx } from './_modify/Ctx.js' +import { resize } from './_modify/resize.js' import { ENCODER, makeTmpBuffer, writeUint32 } from '../utils/uint8.js' import { COMPRESSED, NOT_COMPRESSED } from '../protocol/index.js' import { LangCode, LangCodeEnum } from '../zigTsExports.js' @@ -18,7 +18,7 @@ export const write = ( value = value.normalize('NFKD') buf[offset] = lang const { written: l } = ENCODER.encodeInto(value, buf.subarray(offset + 2)) - let crc = native.crc32(buf.subarray(offset + 2, offset + 2 + l)) + const crc = native.crc32(buf.subarray(offset + 2, offset + 2 + l)) if (value.length > 200 && !noCompression) { const insertPos = offset + 6 + l const startPos = offset + 2 @@ -51,7 +51,13 @@ export const write = ( export const stringCompress = (str: string): Uint8Array => { const s = str.normalize('NFKD') const tmpCompressBlock = getTmpBuffer(2 * native.stringByteLength(s) + 10) - const l = write({ buf: tmpCompressBlock } as Ctx, str, 0, LangCode.none, false) + const l = write( + { buf: tmpCompressBlock } as Ctx, + str, + 0, + LangCode.none, + false, + ) const nBuffer = new Uint8Array(l) nBuffer.set(tmpCompressBlock.subarray(0, l)) return nBuffer diff --git a/native/errors.zig b/native/errors.zig index 9019a4c2da..4877f6534a 100644 --- a/native/errors.zig +++ b/native/errors.zig @@ -75,8 +75,3 @@ pub const DbError = error{ pub const DbIncludeError = error{ EDGE_FROM_WEAKREF, }; - -pub const ClientError = enum(u8) { - null = 0, - nx = 1, -}; diff --git a/native/modify/edges.zig b/native/modify/edges.zig index 2a69b9138e..bfc1df763d 100644 --- a/native/modify/edges.zig +++ b/native/modify/edges.zig @@ -79,7 +79,7 @@ pub fn writeEdges( offset = 0; const dstId = read(u32, data, i + offset); if (Node.getNode(try Node.getRefDstType(ctx.db, edgeFieldSchema), dstId)) |dstNode| { - _ = try References.writeReference(ctx, edgeNode, edgeFieldSchema, dstNode); + _ = try References.writeReference(ctx.db.selva, edgeNode, edgeFieldSchema, dstNode); } else { return errors.SelvaError.SELVA_ENOENT; } diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 70d1b6ac45..8486ffd670 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -16,7 +16,6 @@ const utils = @import("../utils.zig"); const Update = @import("update.zig"); const dbSort = @import("../sort/sort.zig"); const config = @import("config"); -const errors = @import("../errors.zig"); const Thread = @import("../thread/thread.zig"); const t = @import("../types.zig"); const DbCtx = @import("../db/ctx.zig").DbCtx; @@ -41,6 +40,7 @@ pub fn modifyThread(env: napi.Env, info: napi.Info) callconv(.c) napi.Value { ) catch undefined; return null; } + fn modifyInternalThread(env: napi.Env, info: napi.Info) !void { const args = try napi.getArgs(2, env, info); const batch = try napi.get([]u8, env, args[0]); @@ -64,7 +64,7 @@ fn writeoutPrevNodeId(ctx: *ModifyCtx, resultLen: *u32, prevNodeId: u32, result: if (prevNodeId != 0) { utils.write(result, prevNodeId, resultLen.*); utils.writeAs(u8, result, ctx.err, resultLen.* + 4); - ctx.err = errors.ClientError.null; + ctx.err = t.ModifyError.null; resultLen.* += 5; } } @@ -134,7 +134,7 @@ pub fn switchEdgeId(ctx: *ModifyCtx, srcId: u32, dstId: u32, refField: u8) anyer ctx.id = edgeId; ctx.node = edgeNode; if (ctx.node == null) { - ctx.err = errors.ClientError.nx; + ctx.err = t.ModifyError.nx; } else { // try subs.checkId(ctx); // It would be even better if we'd mark it dirty only in the case @@ -152,7 +152,7 @@ pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { const op: t.ModOp = @enumFromInt(buf[i]); // TODO set i += 1; HERE and remove from each individual thing const data: []u8 = buf[i + 1 ..]; - std.debug.print("OP: {any}\n", .{op}); + switch (op) { .padding => { i += 1; @@ -215,7 +215,7 @@ pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { ctx.id = id; ctx.node = Node.getNode(ctx.typeEntry.?, ctx.id); if (ctx.node == null) { - ctx.err = errors.ClientError.nx; + ctx.err = t.ModifyError.nx; } else { // try subs.checkId(ctx); // It would be even better if we'd mark it dirty only in the case @@ -263,7 +263,7 @@ pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { if (Fields.getAliasByName(ctx.typeEntry.?, prop, val)) |node| { const id = Node.getNodeId(node); write(buf, id, ctx.resultLen); - write(buf, errors.ClientError.null, ctx.resultLen + 4); + write(buf, t.ModifyError.null, ctx.resultLen + 4); ctx.resultLen += 5; nextIndex = endIndex; break; @@ -313,36 +313,163 @@ pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { return i; } +pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []align(1) t.ModifyResultItem) !void { + var j: usize = 0; + while (j < data.len) { + const propId = data[j]; + const propSchema = try Schema.getFieldSchema(typeEntry, propId); + if (propId == 0) { + const main = utils.readNext(t.ModifyMainHeader, data, &j); + const value = data[j .. j + main.size]; + const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); + utils.copy(u8, current, value, main.start); + j += main.size; + } else { + const prop = utils.readNext(t.ModifyPropHeader, data, &j); + const value = data[j .. j + prop.size]; + switch (prop.type) { + .reference => { + const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); + const refTypeEntry = try Node.getType(db, refTypeId); + // TODO add TMP handling + const refId = read(u32, value, 0); + if (Node.getNode(refTypeEntry, refId)) |dst| { + _ = try References.writeReference(db, node, propSchema, dst); + } + }, + .references => { + var k: usize = 0; + + if (@as(t.ModifyReferences, @enumFromInt(value[0])) == t.ModifyReferences.clear) { + References.clearReferences(db, node, propSchema); + k += 1; + } + + while (k < value.len) { + const references = utils.readNext(t.ModifyReferencesHeader, value, &k); + const refs = value[k .. k + references.size]; + switch (references.op) { + .ids => { + const offset = utils.alignLeft(u32, refs); + const u32Ids = read([]u32, refs[4 - offset .. refs.len - offset], 0); + try References.putReferences(db, node, propSchema, u32Ids); + }, + .idsWithMeta => { + const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); + const refTypeEntry = try Node.getType(db, refTypeId); + const count = read(u32, refs, 0); + var x: usize = 4; + _ = selva.c.selva_fields_prealloc_refs(db.selva, node, propSchema, count); + while (x < refs.len) { + const meta = utils.readNext(t.ModifyReferencesMetaHeader, refs, &x); + var refId = meta.id; + + if (meta.isTmp) { + // TODO handle isTmp case + refId = meta.id; + } + + if (Node.getNode(refTypeEntry, refId)) |dst| { + const ref = try References.insertReference(db, node, propSchema, dst, meta.index, meta.withIndex); + if (meta.size != 0) { + const edgeProps = refs[x .. x + meta.size]; + const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); + const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); + const edgeNode = try Node.ensureRefEdgeNode(db, node, edgeConstraint, ref.p.large); + try modifyProps(db, edgeType, edgeNode, edgeProps, items); + } + } + + x += meta.size; + } + }, + else => {}, + } + + k += references.size; + } + }, + else => { + try Fields.write(node, propSchema, value); + }, + } + + j += prop.size; + } + } +} + pub fn modify( thread: *Thread.Thread, buf: []u8, - ctx: *DbCtx, + db: *DbCtx, opType: t.OpType, ) !void { var i: usize = 0; + var j: u32 = 4; const header = utils.readNext(t.ModifyHeader, buf, &i); - const size = 4 + header.count * 5; + const size = j + header.count * 5; const result = try thread.modify.result(size, header.opId, header.opType); - _ = result; - _ = ctx; + const items = std.mem.bytesAsSlice(t.ModifyResultItem, result[j..]); _ = opType; + while (i < buf.len) { const op: t.Modify = @enumFromInt(buf[i]); + switch (op) { .create => { const create = utils.readNext(t.ModifyCreateHeader, buf, &i); + const typeEntry = try Node.getType(db, create.type); const data: []u8 = buf[i .. i + create.size]; - std.debug.print("create: {any}, {any}, {any}\n", .{ create.size, buf.len, data }); + const id = db.ids[create.type - 1] + 1; + const node = try Node.upsertNode(typeEntry, id); + modifyProps(db, typeEntry, node, data, items) catch { + // handle errors + }; + db.ids[create.type - 1] = id; + utils.write(result, id, j); + + utils.writeAs(u8, result, t.ModifyError.null, j + 4); i += create.size; + j += 5; + }, + .update => { + const update = utils.readNext(t.ModifyUpdateHeader, buf, &i); + const typeEntry = try Node.getType(db, update.type); + utils.write(result, update.id, j); + if (Node.getNode(typeEntry, update.id)) |node| { + const data: []u8 = buf[i .. i + update.size]; + modifyProps(db, typeEntry, node, data, items) catch { + // handle errors + }; + utils.writeAs(u8, result, t.ModifyError.null, j + 4); + } else { + utils.writeAs(u8, result, t.ModifyError.nx, j + 4); + } + i += update.size; + j += 5; + }, + .delete => { + const delete = utils.readNext(t.ModifyDeleteHeader, buf, &i); + const typeEntry = try Node.getType(db, delete.type); + utils.write(result, delete.id, j); + if (Node.getNode(typeEntry, delete.id)) |node| { + Node.deleteNode(db, typeEntry, node) catch { + // handle errors + }; + utils.writeAs(u8, result, t.ModifyError.null, j + 4); + } else { + utils.writeAs(u8, result, t.ModifyError.nx, j + 4); + } + j += 5; }, - .update => {}, - .delete => {}, } } - // const id = read(u32, batch, 0); - // const count = read(u32, batch, 13); - // const expectedLen = 4 + nodeCount * 5; + Node.expire(db); + utils.write(result, j, 0); + + if (j < size) @memset(result[j..size], 0); } pub fn _modify( @@ -351,7 +478,6 @@ pub fn _modify( dbCtx: *DbCtx, opType: t.OpType, ) !void { - std.debug.print("hahaha??\n", .{}); const modifyId = read(u32, batch, 0); const nodeCount = read(u32, batch, 13); const expectedLen = 4 + nodeCount * 5; @@ -370,7 +496,7 @@ pub fn _modify( .fieldType = t.PropType.null, .db = dbCtx, .batch = batch, - .err = errors.ClientError.null, + .err = t.ModifyError.null, .idSubs = null, .subTypes = null, .thread = thread, diff --git a/native/modify/reference.zig b/native/modify/reference.zig index d28d1eaf24..92392e9f03 100644 --- a/native/modify/reference.zig +++ b/native/modify/reference.zig @@ -40,7 +40,7 @@ pub fn updateReference(ctx: *ModifyCtx, data: []u8) !usize { if (ref == null) { if (Node.getNode(refTypeEntry, id)) |dst| { - ref = try References.writeReference(ctx, ctx.node.?, ctx.fieldSchema.?, dst); + ref = try References.writeReference(ctx.db.selva, ctx.node.?, ctx.fieldSchema.?, dst); } else { return 5; //TODO WARN errors.SelvaError.SELVA_ENOENT } diff --git a/native/modify/references.zig b/native/modify/references.zig index 96c35edd30..682f01a1e6 100644 --- a/native/modify/references.zig +++ b/native/modify/references.zig @@ -127,7 +127,6 @@ pub fn updateReferences(ctx: *ModifyCtx, data: []u8) !usize { } const index: i32 = if (hasIndex) read(i32, data, i + 5) else -1; - var ref: References.ReferenceAny = undefined; if (Node.getNode(refTypeEntry, id)) |dstNode| { ref = try References.insertReference(ctx, ctx.node.?, ctx.fieldSchema.?, dstNode, index, hasIndex); diff --git a/native/selva/node.zig b/native/selva/node.zig index 0c9c984c53..7d9edeba01 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -64,7 +64,7 @@ pub inline fn getNodeTypeId(node: Node) t.TypeId { return selva.c.selva_get_node_type(node); } -pub inline fn upsertNode(_: *Modify.ModifyCtx, typeEntry: selva.Type, id: u32) !Node { +pub inline fn upsertNode(typeEntry: selva.Type, id: u32) !Node { const res = selva.c.selva_upsert_node(typeEntry, id); // TODO Partials if (res.node == null) { @@ -155,12 +155,12 @@ pub inline fn getNodeFromReference(dstType: selva.Type, ref: anytype) ?Node { return null; } -pub inline fn ensureRefEdgeNode(ctx: *Modify.ModifyCtx, node: Node, efc: selva.EdgeFieldConstraint, ref: selva.ReferenceLarge) !Node { +pub inline fn ensureRefEdgeNode(db: *DbCtx, node: Node, efc: selva.EdgeFieldConstraint, ref: selva.ReferenceLarge) !Node { std.debug.print("here we are?\n", .{}); - const edgeNode = selva.c.selva_fields_ensure_ref_edge(ctx.db.selva, node, efc, ref, 0); + const edgeNode = selva.c.selva_fields_ensure_ref_edge(db.selva, node, efc, ref, 0); std.debug.print("here we are: {any}\n", .{edgeNode}); if (edgeNode) |n| { - selva.markDirty(ctx, efc.edge_node_type, getNodeId(n)); + selva.markDirty(db, efc.edge_node_type, getNodeId(n)); return n; } else { return errors.SelvaError.SELVA_ENOTSUP; @@ -176,8 +176,8 @@ pub inline fn getEdgeNode(db: *DbCtx, efc: selva.EdgeFieldConstraint, ref: selva return selva.c.selva_find_node(edge_type, ref.*.edge); } -pub inline fn deleteNode(ctx: *Modify.ModifyCtx, typeEntry: Type, node: Node) !void { - selva.c.selva_del_node(ctx.db.selva, typeEntry, node); +pub inline fn deleteNode(db: *DbCtx, typeEntry: Type, node: Node) !void { + selva.c.selva_del_node(db.selva, typeEntry, node); } pub inline fn flushNode(ctx: *Modify.ModifyCtx, typeEntry: Type, node: Node) void { @@ -189,9 +189,9 @@ pub inline fn expireNode(ctx: *Modify.ModifyCtx, typeId: t.TypeId, nodeId: u32, selva.markDirty(ctx, typeId, nodeId); } -pub inline fn expire(ctx: *Modify.ModifyCtx) void { +pub inline fn expire(db: *DbCtx) void { // Expire things before query - selva.c.selva_db_expire_tick(ctx.db.selva, std.time.timestamp()); + selva.c.selva_db_expire_tick(db.selva, std.time.timestamp()); } pub inline fn getNodeBlockHash(db: *DbCtx, typeEntry: Type, start: u32, hashOut: *SelvaHash128) c_int { diff --git a/native/selva/references.zig b/native/selva/references.zig index 26f89a346e..f43eeb8569 100644 --- a/native/selva/references.zig +++ b/native/selva/references.zig @@ -30,7 +30,7 @@ pub fn deleteReference(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Sch const efc = selva.c.selva_get_edge_field_constraint(fieldSchema); const dstType = efc.*.dst_node_type; - selva.markDirty(ctx, dstType, id); + selva.markDirty(ctx.db, dstType, id); } pub fn referencesHas(refs: References, dstNodeId: u32) bool { @@ -146,15 +146,15 @@ pub fn iterator( } } -pub fn clearReferences(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Schema.FieldSchema) void { - selva.c.selva_fields_clear_references(ctx.db.selva, node, fieldSchema); +pub fn clearReferences(db: *DbCtx, node: Node.Node, fieldSchema: Schema.FieldSchema) void { + selva.c.selva_fields_clear_references(db.selva, node, fieldSchema); } -pub fn writeReference(ctx: *Modify.ModifyCtx, src: Node.Node, fieldSchema: Schema.FieldSchema, dst: Node.Node) !?ReferenceLarge { +pub fn writeReference(db: *DbCtx, src: Node.Node, fieldSchema: Schema.FieldSchema, dst: Node.Node) !?ReferenceLarge { var refAny: selva.c.SelvaNodeReferenceAny = undefined; errors.selva(selva.c.selva_fields_reference_set( - ctx.db.selva, + db.selva, src, fieldSchema, dst, @@ -178,22 +178,21 @@ pub fn writeReference(ctx: *Modify.ModifyCtx, src: Node.Node, fieldSchema: Schem return refAny.p.large; } -pub fn putReferences(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, ids: []u32) !void { - try errors.selva(selva.c.selva_fields_references_insert_tail(ctx.db.selva, node, fieldSchema, try Node.getRefDstType(ctx.db, fieldSchema), ids.ptr, ids.len)); - +pub fn putReferences(db: *DbCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, ids: []u32) !void { + try errors.selva(selva.c.selva_fields_references_insert_tail(db.selva, node, fieldSchema, try Node.getRefDstType(db, fieldSchema), ids.ptr, ids.len)); const efc = selva.c.selva_get_edge_field_constraint(fieldSchema); const dstType = efc.*.dst_node_type; for (ids) |id| { - selva.markDirty(ctx, dstType, id); + selva.markDirty(db, dstType, id); } } // @param index 0 = first; -1 = last. -pub fn insertReference(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, dstNode: Node.Node, index: isize, reorder: bool) !selva.c.SelvaNodeReferenceAny { - const te_dst = selva.c.selva_get_type_by_node(ctx.db.selva, dstNode); +pub fn insertReference(db: *DbCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, dstNode: Node.Node, index: isize, reorder: bool) !selva.c.SelvaNodeReferenceAny { + const te_dst = selva.c.selva_get_type_by_node(db.selva, dstNode); var ref: selva.c.SelvaNodeReferenceAny = undefined; const insertFlags: selva.c.selva_fields_references_insert_flags = if (reorder) selva.c.SELVA_FIELDS_REFERENCES_INSERT_FLAGS_REORDER else 0; - const code = selva.c.selva_fields_references_insert(ctx.db.selva, node, fieldSchema, index, insertFlags, te_dst, dstNode, &ref); + const code = selva.c.selva_fields_references_insert(db.selva, node, fieldSchema, index, insertFlags, te_dst, dstNode, &ref); if (code != selva.c.SELVA_EEXIST) { try errors.selva(code); @@ -202,7 +201,7 @@ pub fn insertReference(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Sch // relevant when updating const efc = selva.c.selva_get_edge_field_constraint(fieldSchema); const dstType = efc.*.dst_node_type; - selva.markDirty(ctx, dstType, Node.getNodeId(dstNode)); + selva.markDirty(db, dstType, Node.getNodeId(dstNode)); } return ref; diff --git a/native/selva/selva.zig b/native/selva/selva.zig index b305415a27..6088d1a00c 100644 --- a/native/selva/selva.zig +++ b/native/selva/selva.zig @@ -33,6 +33,7 @@ pub const c = @cImport({ const std = @import("std"); const Modify = @import("../modify/common.zig"); +const DbCtx = @import("../db/ctx.zig").DbCtx; pub const Node = *c.SelvaNode; pub const Aliases = *c.SelvaAliases; @@ -54,8 +55,8 @@ pub fn selvaStringDestroy(str: ?c.selva_string) void { } // TODO Accept also Type as an arg -pub inline fn markDirty(ctx: *Modify.ModifyCtx, typeId: u16, nodeId: u32) void { - c.selva_mark_dirty(c.selva_get_type_by_index(ctx.db.selva, typeId), nodeId); +pub inline fn markDirty(db: *DbCtx, typeId: u16, nodeId: u32) void { + c.selva_mark_dirty(c.selva_get_type_by_index(db.selva, typeId), nodeId); } pub fn markReferencesDirty(ctx: *Modify.ModifyCtx, dstTypeId: u16, refs: []u32) void { diff --git a/native/types.zig b/native/types.zig index af1ed9dd71..b348bc2f3a 100644 --- a/native/types.zig +++ b/native/types.zig @@ -1,5 +1,4 @@ const Schema = @import("./selva/schema.zig"); - pub const TypeId = u16; pub const BridgeResponse = enum(u32) { @@ -114,14 +113,14 @@ pub const ModifyMainHeader = packed struct { pub const ModifyPropHeader = packed struct { id: u8, - type: u8, + type: PropType, size: u32, }; pub const ModifyReferences = enum(u8) { clear = 0, ids = 1, - idsAndEdges = 2, + idsWithMeta = 2, tmpIds = 3, delIds = 4, delTmpIds = 5, @@ -132,13 +131,40 @@ pub const ModifyReferencesHeader = packed struct { size: u32, }; -pub const ModifyEdgesHeader = packed struct { +// pub const ModifyReferencesMeta = enum(u8) { +// noTmpNoIndex = 0, +// tmpNoIndex = 1, +// tmpIndex = 2, +// noTmpIndex = 4, +// }; + +pub const ModifyReferencesMetaHeader = packed struct { id: u32, + isTmp: bool, withIndex: bool, - index: u32, + _padding: u6, + index: i32, + size: u32, +}; + +pub const ModifyReferenceMetaHeader = packed struct { + id: u32, + isTmp: bool, + _padding: u7, size: u32, }; +pub const ModifyResultItem = packed struct { + id: u32, + err: ModifyError, +}; + +pub const ModifyError = enum(u8) { + null = 0, + nx = 1, + unknown = 2, +}; + pub const PropType = enum(u8) { null = 0, timestamp = 1, diff --git a/scripts/zigTsExports.ts b/scripts/zigTsExports.ts index 3afc8a4661..afa146be1d 100644 --- a/scripts/zigTsExports.ts +++ b/scripts/zigTsExports.ts @@ -17,7 +17,7 @@ const parseZig = (input: string): string => { readUint64, readInt64, readFloatLE, readDoubleLE } from './utils/index.js' -import { AutoSizedUint8Array } from './modify/AutoSizedUint8Array.js'\n\n` +import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\n\n` // Symbol tables const typeSizes: Record = { diff --git a/src/db-client/hooks.ts b/src/db-client/hooks.ts index a41d619eeb..5b628d2a4f 100644 --- a/src/db-client/hooks.ts +++ b/src/db-client/hooks.ts @@ -8,7 +8,7 @@ export type DbClientHooks = { schema: SchemaOut, transformFns?: SchemaMigrateFns, ): Promise - flushModify(buf: Uint8Array): Promise + flushModify(buf: Uint8Array): Promise getQueryBuf(buf: Uint8Array): ReturnType subscribe( q: BasedDbQuery, @@ -26,7 +26,7 @@ export const getDefaultHooks = (server: DbServer): DbClientHooks => { onError: OnError, ) { server.subscribe(q.subscriptionBuffer!, onData) - return () => { } + return () => {} }, setSchema(schema: SchemaOut, transformFns) { return server.setSchema(schema, transformFns) @@ -41,17 +41,15 @@ export const getDefaultHooks = (server: DbServer): DbClientHooks => { }, flushModify(buf: Uint8Array) { const x = buf.slice(0) - const res = server.modify(x) if (res instanceof Promise) { return res.then((res) => { server.keepRefAliveTillThisPoint(x) - return res && new Uint8Array(res) + return new Uint8Array(res) }) } - - return Promise.resolve(res && new Uint8Array(res)) + return Promise.resolve(new Uint8Array(res)) }, getQueryBuf(buf: Uint8Array) { return server.getQueryBuf(buf) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 6857af1406..4ff74ecedb 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -4,20 +4,24 @@ import { SubStore } from './query/subscription/index.js' import { DbShared } from '../shared/DbBase.js' import { DbClientHooks } from './hooks.js' import { setLocalClientSchema } from './setLocalClientSchema.js' -import { ModifyOpts } from './modify/types.js' -import { create } from './modify/create/index.js' -import { Ctx } from './modify/Ctx.js' -import { update } from './modify/update/index.js' -import { del } from './modify/delete/index.js' -import { expire } from './modify/expire/index.js' -import { cancel, drain, schedule } from './modify/drain.js' -import { insert, upsert } from './modify/upsert/index.js' import { parse, type SchemaIn, type SchemaMigrateFns, type SchemaOut, } from '../schema/index.js' +import { AutoSizedUint8Array } from './modify/AutoSizedUint8Array.js' +import { LangCode } from '../zigTsExports.js' +import { + serializeCreate, + serializeDelete, + serializeUpdate, + ModifyCtx, + modify, + schedule, + flush, + ModifyItem, +} from './modify/index.js' type DbClientOpts = { hooks: DbClientHooks @@ -26,6 +30,11 @@ type DbClientOpts = { debug?: boolean } +export type ModifyOpts = { + unsafe?: boolean + locale?: keyof typeof LangCode +} + export class DbClient extends DbShared { constructor({ hooks, @@ -35,20 +44,17 @@ export class DbClient extends DbShared { }: DbClientOpts) { super() this.hooks = hooks - this.maxModifySize = maxModifySize - this.modifyCtx = new Ctx( - 0, - new Uint8Array( - new ArrayBuffer(Math.min(1e3, maxModifySize), { - maxByteLength: maxModifySize, - }), - ), - ) - this.flushTime = flushTime - + // this.maxModifySize = maxModifySize + this.modifyCtx = { + buf: new AutoSizedUint8Array(256, maxModifySize), + flushTime, + batch: { count: 0 }, + hooks, + } if (debug) { debugMode(this) } + this.hooks.subscribeSchema((schema) => { setLocalClientSchema(this, schema) }) @@ -59,13 +65,16 @@ export class DbClient extends DbShared { hooks: DbClientHooks // modify - flushTime: number - writeTime: number = 0 - isDraining = false - modifyCtx: Ctx - maxModifySize: number - upserting: Map; p: Promise }> = - new Map() + modifyCtx: ModifyCtx + + // modify + // flushTime: number + // writeTime: number = 0 + // isDraining = false + // // maxModifySize: number + // modifyCtx: ModifyCtx + // upserting: Map; p: Promise }> = + // new Map() async schemaIsSet() { if (!this.schema) { @@ -94,50 +103,51 @@ export class DbClient extends DbShared { } create(type: string, obj = {}, opts?: ModifyOpts): Promise { - return create(this, type, obj, opts) + return modify( + this.modifyCtx, + serializeCreate, + this.schema!, + type, + obj, + this.modifyCtx.buf, + opts?.locale ? LangCode[opts.locale] : LangCode.none, + ) } - async copy( + async update( type: string, - target: number, - objOrTransformFn?: - | Record - | ((item: Record) => Promise), + id: number | Promise, + obj = {}, + opts?: ModifyOpts, ): Promise { - const item = await this.query(type, target) - .include('*', '**.id') - .get() - .toObject() - - if (typeof objOrTransformFn === 'function') { - const { id: _, ...props } = await objOrTransformFn(item) - return this.create(type, props) + if (id instanceof Promise) { + id = await id } + return modify( + this.modifyCtx, + serializeUpdate, + this.schema!, + type, + id, + obj, + this.modifyCtx.buf, + opts?.locale ? LangCode[opts.locale] : LangCode.none, + ) + } - if (typeof objOrTransformFn === 'object' && objOrTransformFn !== null) { - const { id: _, ...props } = item - await Promise.all( - Object.keys(objOrTransformFn).map(async (key) => { - const val = objOrTransformFn[key] - if (val === null) { - delete props[key] - } else if (typeof val === 'function') { - const res = await val(item) - if (Array.isArray(res)) { - props[key] = await Promise.all(res) - } else { - props[key] = res - } - } else { - props[key] = val - } - }), - ) - return this.create(type, props) + async delete(type: string, id: number | Promise) { + if (id instanceof Promise) { + id = await id } - - const { id: _, ...props } = item - return this.create(type, props) + return modify( + this.modifyCtx, + serializeDelete, + this.schema!, + type, + // TODO make it perf + id, + this.modifyCtx.buf, + ) } query( @@ -164,91 +174,9 @@ export class DbClient extends DbShared { return new BasedDbQuery(this, type, id as number | number[] | Uint32Array) } - update( - type: string, - id: number | Promise, - value: any, - opts?: ModifyOpts, - ): Promise - - update( - type: string, - value: Record & { id: number }, - opts?: ModifyOpts, - ): Promise - - update( - typeOrValue: string | any, - idOverwriteOrValue: - | number - | Promise - | boolean - | ModifyOpts - | (Record & { id: number }), - value?: any, - opts?: ModifyOpts, - ): Promise { - if (typeof typeOrValue !== 'string') { - return this.update( - '_root', - 1, - typeOrValue, - idOverwriteOrValue as ModifyOpts, - ) - } - if (typeof idOverwriteOrValue === 'object') { - if ( - 'then' in idOverwriteOrValue && - typeof idOverwriteOrValue.then === 'function' - ) { - // @ts-ignore - if (idOverwriteOrValue.id) { - // @ts-ignore - return this.update(typeOrValue, idOverwriteOrValue.id, value, opts) - } - return idOverwriteOrValue.then((id: number) => { - return this.update(typeOrValue, id, value, opts) - }) - } - if ('id' in idOverwriteOrValue) { - const { id, ...props } = idOverwriteOrValue - return this.update(typeOrValue, id, props, opts) - } - } - return update(this, typeOrValue, idOverwriteOrValue as number, value, opts) - } - - upsert(type: string, obj: Record, opts?: ModifyOpts) { - return upsert(this, type, obj, opts) - } - - insert(type: string, obj: Record, opts?: ModifyOpts) { - return insert(this, type, obj, opts) - } - - delete(type: string, id: number | Promise) { - if ( - typeof id === 'object' && - id !== null && - 'then' in id && - typeof id.then === 'function' - ) { - // @ts-ignore - if (id.id) { - // @ts-ignore - id = id.id - } else { - // @ts-ignore - return id.then((id) => this.delete(type, id)) - } - } - // @ts-ignore - return del(this, type, id) - } - - expire(type: string, id: number, seconds: number) { - return expire(this, type, id, seconds) - } + // expire(type: string, id: number, seconds: number) { + // return expire(this, type, id, seconds) + // } destroy() { this.stop() @@ -261,21 +189,28 @@ export class DbClient extends DbShared { onClose() } this.subs.clear() - cancel(this.modifyCtx, Error('Db stopped - in-flight modify cancelled')) + // cancel(this.modifyCtx, Error('Db stopped - in-flight modify cancelled')) } // For more advanced / internal usage - use isModified instead for most cases async drain() { - if (this.upserting.size) { - await Promise.all(Array.from(this.upserting).map(([, { p }]) => p)) - } - await drain(this, this.modifyCtx) - const t = this.writeTime - this.writeTime = 0 - return t + // if (this.upserting.size) { + // await Promise.all(Array.from(this.upserting).map(([, { p }]) => p)) + // } + // await drain(this, this.modifyCtx) + + // const t = this.writeTime + // this.writeTime = 0 + flush(this.modifyCtx) + await this.modifyCtx.lastModify?.catch(noop) + // await this.modifyCtx.lastModify + // return t } - isModified() { - return schedule(this, this.modifyCtx) + async isModified() { + schedule(this.modifyCtx) + await this.modifyCtx.lastModify?.catch(noop) } } + +function noop() {} diff --git a/src/modify/AutoSizedUint8Array.ts b/src/db-client/modify/AutoSizedUint8Array.ts similarity index 92% rename from src/modify/AutoSizedUint8Array.ts rename to src/db-client/modify/AutoSizedUint8Array.ts index b870d80842..6682fdfde1 100644 --- a/src/modify/AutoSizedUint8Array.ts +++ b/src/db-client/modify/AutoSizedUint8Array.ts @@ -1,9 +1,12 @@ +import native from '../../native.js' import { writeDoubleLE, writeFloatLE, writeUint64, writeInt64, -} from '../utils/index.js' +} from '../../utils/index.js' + +const ENCODER = new TextEncoder() // Runtime method injection const delegateMethods = [ @@ -49,7 +52,6 @@ export class AutoSizedUint8Array { private ensureCapacity(requiredCapacity: number): void { const currentCapacity = this.data.byteLength if (currentCapacity >= requiredCapacity) return - if (requiredCapacity > this._maxCapacity) { throw AutoSizedUint8Array.ERR_OVERFLOW } @@ -65,10 +67,6 @@ export class AutoSizedUint8Array { ;(this.data.buffer as any).resize(finalCapacity) } - get capacity(): number { - return this.data.length - } - get view(): Uint8Array { return this.data.subarray(0, this.length) } @@ -203,6 +201,20 @@ export class AutoSizedUint8Array { this.length = requiredEnd } + pushString(value: string): number { + const maxBytes = native.stringByteLength(value) + const requiredEnd = this.length + maxBytes + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + const { written } = ENCODER.encodeInto( + value, + this.data.subarray(this.length), + ) + this.length += written! + return written + } + setU32(value: number, offset: number): void { const requiredEnd = offset + 4 if (requiredEnd > this.data.length) { @@ -234,7 +246,7 @@ export class AutoSizedUint8Array { return this.pushU8(byte) } - subarray(begin?: number, end?: number): Uint8Array { + subarray(begin: number = 0, end: number = this.length): Uint8Array { return this.view.subarray(begin, end) } diff --git a/src/modify/defs/base.ts b/src/db-client/modify/defs/base.ts similarity index 72% rename from src/modify/defs/base.ts rename to src/db-client/modify/defs/base.ts index 1065922620..1e8c427dda 100644 --- a/src/modify/defs/base.ts +++ b/src/db-client/modify/defs/base.ts @@ -1,5 +1,9 @@ -import type { SchemaProp } from '../../schema.js' -import type { LangCodeEnum, ModifyEnum } from '../../zigTsExports.js' +import type { SchemaProp } from '../../../schema.js' +import type { + LangCodeEnum, + ModifyEnum, + PropTypeEnum, +} from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import type { PropDef } from './index.js' @@ -11,7 +15,7 @@ export class BasePropDef implements PropDef { id = 0 start = 0 size = 0 - type = 0 + type: PropTypeEnum = 0 as PropTypeEnum prop: SchemaProp path: string[] diff --git a/src/modify/defs/fixed.ts b/src/db-client/modify/defs/fixed.ts similarity index 85% rename from src/modify/defs/fixed.ts rename to src/db-client/modify/defs/fixed.ts index 0ac8fec35d..0ff2cb01d0 100644 --- a/src/modify/defs/fixed.ts +++ b/src/db-client/modify/defs/fixed.ts @@ -1,10 +1,10 @@ -import type { EnumItem, SchemaEnum } from '../../schema.js' -import { PropType } from '../../zigTsExports.js' +import type { EnumItem, SchemaEnum } from '../../../schema.js' +import { PropType, type PropTypeEnum } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import { BasePropDef } from './base.js' export const number = class extends BasePropDef { - override type: number = PropType.number + override type: PropTypeEnum = PropType.number override size = 8 override pushValue(buf: AutoSizedUint8Array, value: number) { buf.pushDouble(value) @@ -16,7 +16,7 @@ export const timestamp = class extends number { } export const uint8 = class extends BasePropDef { - override type: number = PropType.uint8 + override type: PropTypeEnum = PropType.uint8 override size = 1 override pushValue(buf: AutoSizedUint8Array, value: number): void { buf.pushU8(value) @@ -28,7 +28,7 @@ export const int8 = class extends uint8 { } export const uint16 = class extends BasePropDef { - override type: number = PropType.uint16 + override type: PropTypeEnum = PropType.uint16 override size = 2 override pushValue(buf: AutoSizedUint8Array, value: number): void { buf.pushU16(value) @@ -40,7 +40,7 @@ export const int16 = class extends uint16 { } export const uint32 = class extends BasePropDef { - override type: number = PropType.uint32 + override type: PropTypeEnum = PropType.uint32 override size = 4 override pushValue(buf: AutoSizedUint8Array, value: number): void { buf.pushU32(value) diff --git a/src/modify/defs/getTypeDefs.ts b/src/db-client/modify/defs/getTypeDefs.ts similarity index 99% rename from src/modify/defs/getTypeDefs.ts rename to src/db-client/modify/defs/getTypeDefs.ts index 5f094db6c6..8505a71052 100644 --- a/src/modify/defs/getTypeDefs.ts +++ b/src/db-client/modify/defs/getTypeDefs.ts @@ -3,7 +3,7 @@ import { type SchemaProp, type SchemaProps, type SchemaType, -} from '../../schema.js' +} from '../../../schema.js' import { defs, type TypeDef } from './index.js' const mainSorter = (a, b) => { diff --git a/src/modify/defs/index.ts b/src/db-client/modify/defs/index.ts similarity index 84% rename from src/modify/defs/index.ts rename to src/db-client/modify/defs/index.ts index 467bfe5d40..3a87c535bd 100644 --- a/src/modify/defs/index.ts +++ b/src/db-client/modify/defs/index.ts @@ -1,5 +1,9 @@ -import type { SchemaProp } from '../../schema.js' -import type { LangCodeEnum, ModifyEnum } from '../../zigTsExports.js' +import type { SchemaProp } from '../../../schema.js' +import type { + LangCodeEnum, + ModifyEnum, + PropTypeEnum, +} from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import * as fixed from './fixed.js' import * as references from './references.js' @@ -19,7 +23,7 @@ export type TypeDef = { export type PropDef = { id: number - type: number + type: PropTypeEnum start: number path: string[] size: number diff --git a/src/modify/defs/references.ts b/src/db-client/modify/defs/references.ts similarity index 72% rename from src/modify/defs/references.ts rename to src/db-client/modify/defs/references.ts index 6a7acb0aba..8ad333f035 100644 --- a/src/modify/defs/references.ts +++ b/src/db-client/modify/defs/references.ts @@ -2,25 +2,23 @@ import { Modify, ModifyReferences, PropType, + pushModifyReferenceMetaHeader, pushModifyReferencesHeader, + pushModifyReferencesMetaHeader, writeModifyReferencesHeaderProps, + writeModifyReferencesMetaHeaderProps, type LangCodeEnum, type ModifyEnum, -} from '../../zigTsExports.js' + type PropTypeEnum, +} from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' -import type { SchemaProp } from '../../schema.js' +import type { SchemaProp } from '../../../schema.js' import { BasePropDef } from './base.js' import type { PropDef, TypeDef } from './index.js' import { serializeProps } from '../index.js' type Edges = Record<`${string}`, unknown> | undefined -const hasEdgesAndOrIndex = (obj: Record): boolean | void => { - for (const i in obj) { - if (i[0] === '$') return true - } -} - const getEdges = (obj: Record): Edges => { let edges: Edges for (const i in obj) { @@ -38,6 +36,8 @@ const serializeIds = ( offset: number, ): number => { let i = offset + // one extra for padding + buf.pushU32(0) for (; i < ids.length; i++) { const id = ids[i] if (typeof id !== 'number') { @@ -54,6 +54,8 @@ const serializeTmpIds = ( offset: number, ): undefined | any => { let i = offset + // one extra for padding + buf.pushU32(0) for (; i < items.length; i++) { const item = items[i] if (typeof item !== 'object' || item === null || !item.tmpId) { @@ -75,20 +77,44 @@ const serializeIdsAndMeta = ( edgesType?: TypeDef, ): number => { let i = offset + const start = buf.reserveU32() + for (; i < items.length; i++) { const item = items[i] if (item === null || typeof item !== 'object') { throw 'error' } - const edges = getEdges(item) - if (typeof item.id !== 'number' || !edges) { + + // TODO handle tmp id + if (typeof item.id !== 'number') { break } - buf.pushU32(item.id) + + const index = pushModifyReferencesMetaHeader(buf, { + id: item.id, + isTmp: false, + withIndex: '$index' in item, + index: item.$index, + size: 0, + }) + if (edgesType) { - serializeProps(edgesType.tree, edges, buf, op, lang) + const edges = getEdges(item) + if (edges) { + const start = buf.length + serializeProps(edgesType.tree, edges, buf, op, lang) + writeModifyReferencesMetaHeaderProps.size( + buf.data, + buf.length - start, + index, + ) + } } } + + // store the amount of refs (for prealloc) + buf.setU32(i - offset, start) + return i } @@ -130,11 +156,18 @@ const setReferences = ( } if (typeof item.id === 'number') { // TODO can optimize, don't need whole object - if (hasEdgesAndOrIndex(item)) { - offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) - } else { - // TODO with index - } + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.idsWithMeta, + size: 0, + }) + const start = buf.length + offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) + writeModifyReferencesHeaderProps.size( + buf.data, + buf.length - start, + index, + ) + continue } } @@ -171,7 +204,7 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { } export const references = class extends BasePropDef { - override type = PropType.references + override type: PropTypeEnum = PropType.references override pushValue( buf: AutoSizedUint8Array, value: any, @@ -199,9 +232,18 @@ export const references = class extends BasePropDef { } } -export const reference = class extends references { - override type = PropType.references +export const reference = class extends BasePropDef { + override type: PropTypeEnum = PropType.reference override pushValue(buf: AutoSizedUint8Array, value: any, op: ModifyEnum) { - console.error('TODO reference') + console.error('TODO reference ALL THE CASES') + if (typeof value === 'number') { + pushModifyReferenceMetaHeader(buf, { + id: value, + isTmp: false, + size: 0, + }) + } + + // buf.pushU32(value) } } diff --git a/src/modify/defs/vars.ts b/src/db-client/modify/defs/vars.ts similarity index 81% rename from src/modify/defs/vars.ts rename to src/db-client/modify/defs/vars.ts index d8fc4fb873..eadcd9b55b 100644 --- a/src/modify/defs/vars.ts +++ b/src/db-client/modify/defs/vars.ts @@ -1,14 +1,15 @@ -import type { SchemaString, SchemaVector } from '../../schema.js' +import native from '../../../native.js' +import { NOT_COMPRESSED } from '../../../protocol/index.js' +import type { SchemaString, SchemaVector } from '../../../schema.js' import { PropType, type LangCodeEnum, type ModifyEnum, -} from '../../zigTsExports.js' + type PropTypeEnum, +} from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import { BasePropDef } from './base.js' -const encoder = new TextEncoder() - export const string = class extends BasePropDef { constructor(prop: SchemaString, path) { super(prop, path) @@ -20,21 +21,19 @@ export const string = class extends BasePropDef { if (this.size) { } } - override type: number = PropType.string + override type: PropTypeEnum = PropType.string override pushValue( buf: AutoSizedUint8Array, - val: unknown, + val: string, op: ModifyEnum, lang: LangCodeEnum, ) { - const isUint8 = val instanceof Uint8Array - if (val === null || val === '' || (isUint8 && val.byteLength === 0)) { - // deleteString(ctx, def, lang) - // return - } - // const encoded = encoder.encode(value) - // buf.set(encoded, buf.length) - // buf.length += encoded.length + const normalized = val.normalize('NFKD') + buf.pushU8(lang) + buf.pushU8(NOT_COMPRESSED) + const written = buf.pushString(normalized) + const crc = native.crc32(buf.subarray(buf.length - written)) + buf.pushU32(crc) } } @@ -83,7 +82,7 @@ export const vector = class extends BasePropDef { this.vectorSize = prop.size * 4 } vectorSize: number - override type: number = PropType.vector + override type: PropTypeEnum = PropType.vector override pushValue(buf: AutoSizedUint8Array, value: any) { throw new Error('Serialize vector not implemented') } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts new file mode 100644 index 0000000000..e286e676a6 --- /dev/null +++ b/src/db-client/modify/index.ts @@ -0,0 +1,286 @@ +import { SchemaOut } from '../../schema.js' +import { + Modify, + pushModifyCreateHeader, + pushModifyDeleteHeader, + pushModifyHeader, + pushModifyMainHeader, + pushModifyPropHeader, + pushModifyUpdateHeader, + writeModifyCreateHeaderProps, + writeModifyHeaderProps, + writeModifyPropHeaderProps, + writeModifyUpdateHeaderProps, + type LangCodeEnum, + type ModifyEnum, + type ModifyErrorEnum, +} from '../../zigTsExports.js' +import { AutoSizedUint8Array } from './AutoSizedUint8Array.js' +import type { PropDef, PropTree, TypeDef } from './defs/index.js' +import { InferPayload } from './types.js' +import { getTypeDefs } from './defs/getTypeDefs.js' +import { readUint32 } from '../../utils/uint8.js' +export { getTypeDefs } + +export const serializeProps = ( + tree: PropTree, + data: any, + buf: AutoSizedUint8Array, + op: ModifyEnum, + lang: LangCodeEnum, +) => { + for (const key in data) { + const def = tree.get(key) + if (def === undefined) { + continue + } + const val = data[key] + if (def.constructor === Map) { + if (val !== null && typeof val === 'object') { + serializeProps(def, val, buf, op, lang) + } + } else { + const prop = def as PropDef + if (prop.size) { + pushModifyMainHeader(buf, prop) + prop.pushValue(buf, val, op, lang) + } else { + const index = pushModifyPropHeader(buf, prop) + const start = buf.length + prop.pushValue(buf, val, op, lang) + writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) + } + } + } +} + +const getTypeDef = (schema: SchemaOut, type: string) => { + const typeDef = getTypeDefs(schema).get(type) + if (!typeDef) { + throw new Error(`Type ${type} not found`) + } + return typeDef +} + +export const serializeCreate = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyCreateHeader(buf, { + op: Modify.create, + type: typeDef.id, + size: 0, + }) + const start = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) +} + +export const serializeUpdate = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + id: number, + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyUpdateHeader(buf, { + op: Modify.update, + type: typeDef.id, + id, + size: 0, + }) + const start = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + writeModifyUpdateHeaderProps.size(buf.data, buf.length - start, index) +} + +export const serializeDelete = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + id: number, + buf: AutoSizedUint8Array, +) => { + const typeDef = getTypeDef(schema, type) + pushModifyDeleteHeader(buf, { + op: Modify.delete, + type: typeDef.id, + id, + }) +} + +export class ModifyItem implements Promise { + constructor(batch: ModifyBatch) { + this._batch = batch + this._index = batch.count + } + + [Symbol.toStringTag]!: 'ModifyItem' + + private _p?: Promise + private _batch: ModifyBatch + private get _promise() { + if (!this._p) { + this._p ??= new Promise((resolve, reject) => { + this._resolve = resolve + this._reject = reject + this._batch.items ??= [] + this._batch.items.push(this) + }) + } + + return this._p + } + + _index: number + _id?: number + _err?: ModifyErrorEnum + + get id(): number | undefined { + if (this._batch.result) { + this._id = readUint32(this._batch.result, this._index * 5) + } + return this._id + } + + get err(): ModifyErrorEnum | undefined { + if (this._batch.result) { + this._err = this._batch.result[this._index * 5 + 4] as ModifyErrorEnum + } + return this._err + } + + _resolve?: (value: number | PromiseLike) => void + _reject?: (reason?: any) => void + + then( + onfulfilled?: ((value: number) => Res1 | PromiseLike) | null, + onrejected?: ((reason: any) => Res2 | PromiseLike) | null, + ): Promise { + return this._promise.then(onfulfilled, onrejected) + } + catch( + onrejected?: ((reason: any) => Res | PromiseLike) | null, + ): Promise { + return this._promise.catch(onrejected) + } + finally(onfinally?: (() => void) | null): Promise { + return this._promise.finally(onfinally) + } +} + +type ModifyBatch = { + count: number + items?: ModifyItem[] + result?: Uint8Array +} + +export type ModifyCtx = { + buf: AutoSizedUint8Array + batch: ModifyBatch + flushTime: number + lastModify?: ModifyItem + flushTimer?: NodeJS.Timeout | true | undefined + hooks: { + flushModify: (buf: Uint8Array) => Promise + } +} + +export const flush = (ctx: ModifyCtx) => { + if (ctx.buf.length) { + const batch = ctx.batch + writeModifyHeaderProps.count(ctx.buf.data, batch.count, 0) + ctx.hooks.flushModify(ctx.buf.view).then((result) => { + batch.result = result + if (batch.items) { + for (const item of batch.items) { + const id = item.id + const err = item.err + if (err) { + item._reject!(err) + } else { + item._resolve!(id!) + } + } + } + }) + + ctx.buf.flush() + ctx.batch = { count: 0 } + } +} + +export const schedule = (ctx: ModifyCtx) => { + if (!ctx.flushTimer) { + if (ctx.flushTime === 0) { + ctx.flushTimer = true + process.nextTick(() => { + ctx.flushTimer = undefined + flush(ctx) + }) + } else { + ctx.flushTimer = setTimeout(() => { + ctx.flushTimer = undefined + flush(ctx) + }, ctx.flushTime) + } + } +} + +export const modify = async < + S extends + | typeof serializeCreate + | typeof serializeUpdate + | typeof serializeDelete, +>( + ctx: ModifyCtx, + serialize: S, + ...args: Parameters +): Promise => { + const isEmpty = ctx.buf.length === 0 + + if (isEmpty) { + pushModifyHeader(ctx.buf, { + opId: 0, // is filled on server + opType: 0, // is filled on server + schema: args[0].hash, + count: 0, + }) + } + + const initialLength = ctx.buf.length + try { + ;(serialize as (...args: any[]) => void)(...args) + } catch (e) { + if (e === AutoSizedUint8Array.ERR_OVERFLOW) { + if (isEmpty) { + throw new Error('Range error') + } + ctx.buf.length = initialLength + flush(ctx) + return modify(ctx, serialize, ...args) + } else { + throw e + } + } + + const item = new ModifyItem(ctx.batch) + ctx.lastModify = item + ctx.batch.count++ + schedule(ctx) + return item +} diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 43426e1df9..c39a943f8e 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -1,35 +1,58 @@ -import { LangCode, ModOp } from '../../zigTsExports.js' +import { type SchemaTypes } from '../../schema.js' -export const RANGE_ERR = 1 -export const MOD_OPS_TO_STRING = { - [ModOp.createProp]: 'create', - [ModOp.updateProp]: 'update', - [ModOp.increment]: 'update', - [ModOp.expire]: 'update', -} as const +type TypedArray = + | Uint8Array + | Float32Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array -export const enum SIZE { - DEFAULT_CURSOR = 11, +type TypeMap = { + string: string + number: number + int8: number + uint8: number + int16: number + uint16: number + int32: number + uint32: number + boolean: boolean + text: string + json: any + timestamp: number | string | Date + binary: Uint8Array + alias: string + vector: TypedArray + colvec: TypedArray + cardinality: string | string[] } -export type ModifyOpts = { - unsafe?: boolean - locale?: keyof typeof LangCode -} - -export const NOEDGE_NOINDEX_REALID = 0 -export const EDGE_NOINDEX_REALID = 1 -export const EDGE_INDEX_REALID = 2 -export const NOEDGE_INDEX_REALID = 3 -export const NOEDGE_NOINDEX_TMPID = 4 -export const EDGE_NOINDEX_TMPID = 5 -export const EDGE_INDEX_TMPID = 6 -export const NOEDGE_INDEX_TMPID = 7 +type InferProp = Prop extends { type: 'object'; props: infer P } + ? InferType + : Prop extends { type: infer T extends keyof TypeMap } + ? TypeMap[T] + : Prop extends { enum: infer E extends readonly any[] } + ? E[number] + : Prop extends { ref: string } + ? string | number + : Prop extends { items: { ref: string } } + ? (string | number)[] + : never -// export const REF_OP_OVERWRITE = 0 -// export const REF_OP_UPDATE = 1 -// export const REF_OP_DELETE = 2 -// export const REF_OP_PUT_OVERWRITE = 3 -// export const REF_OP_PUT_ADD = 4 +type InferType = { + [K in keyof Props as Props[K] extends { required: true } + ? K + : never]: InferProp +} & { + [K in keyof Props as Props[K] extends { required: true } + ? never + : K]?: InferProp +} -// export type RefOp = typeof REF_OP_OVERWRITE | typeof REF_OP_UPDATE +export type InferPayload> = { + [K in keyof Types]: InferType +} diff --git a/src/db-client/setLocalClientSchema.ts b/src/db-client/setLocalClientSchema.ts index f401af3c26..086afec767 100644 --- a/src/db-client/setLocalClientSchema.ts +++ b/src/db-client/setLocalClientSchema.ts @@ -1,6 +1,6 @@ import { DbClient } from '../index.js' -import { cancel } from './modify/drain.js' -import { Ctx, MODIFY_HEADER_SIZE } from './modify/Ctx.js' +// import { cancel } from './_modify/drain.js' +// import { Ctx, MODIFY_HEADER_SIZE } from './_modify/Ctx.js' import { updateTypeDefs, type SchemaOut } from '../schema/index.js' export const setLocalClientSchema = (client: DbClient, schema: SchemaOut) => { @@ -12,12 +12,13 @@ export const setLocalClientSchema = (client: DbClient, schema: SchemaOut) => { client.schemaTypesParsed = schemaTypesParsed client.schemaTypesParsedById = schemaTypesParsedById - if (client.modifyCtx.index > MODIFY_HEADER_SIZE) { - console.info('Modify cancelled - schema updated') - } + console.warn('TODO schema CHANGE') + // if (client.modifyCtx.index > MODIFY_HEADER_SIZE) { + // console.info('Modify cancelled - schema updated') + // } - cancel(client.modifyCtx, Error('Schema changed - in-flight modify cancelled')) - client.modifyCtx = new Ctx(schema.hash, client.modifyCtx.buf) + // cancel(client.modifyCtx, Error('Schema changed - in-flight modify cancelled')) + // client.modifyCtx = new Ctx(schema.hash, client.modifyCtx.buf) // resubscribe for (const [q, store] of client.subs) { diff --git a/src/db-server/index.ts b/src/db-server/index.ts index 95a12cb59c..c3a437ed11 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -182,7 +182,7 @@ export class DbServer extends DbShared { // allow 10 ids for special listeners on mod thread modifyCnt = 10 - modify(payload: Uint8Array): Promise { + modify(payload: Uint8Array): Promise { this.modifyCnt++ if (this.modifyCnt > MAX_ID) { this.modifyCnt = 10 diff --git a/src/db-server/schemaSelvaBuffer.ts b/src/db-server/schemaSelvaBuffer.ts index 74acc66e83..b51374587c 100644 --- a/src/db-server/schemaSelvaBuffer.ts +++ b/src/db-server/schemaSelvaBuffer.ts @@ -1,7 +1,4 @@ -import { - writeUint16, - writeUint32, -} from '../utils/index.js' +import { writeUint16, writeUint32 } from '../utils/index.js' import native from '../native.js' import { LangCode, PropType, PropTypeEnum } from '../zigTsExports.js' import { @@ -11,9 +8,9 @@ import { type PropDefEdge, type SchemaTypeDef, } from '../schema/index.js' -import { write as writeString } from '../db-client/string.js' +// import { write as writeString } from '../db-client/string.js' import { fillEmptyMain } from '../schema/def/fillEmptyMain.js' -import {Ctx} from '../db-client/modify/Ctx.js' +// import { Ctx } from '../db-client/_modify/Ctx.js' const selvaFieldType: Readonly> = { NULL: 0, @@ -117,27 +114,31 @@ const propDefBuffer = ( type === PropType.json ) { if (prop.default && supportedDefaults.has(type)) { - const defaultValue = typeof prop.default === 'string' - ? prop.default.normalize('NFKD') - : type === PropType.json - ? JSON.stringify(prop.default) - : prop.default - const defaultLen = defaultValue instanceof Uint8Array - ? defaultValue.byteLength - : 2 * native.stringByteLength(defaultValue) + STRING_EXTRA_MAX - let buf = new Uint8Array(6 + defaultLen) - - buf[0] = selvaType - buf[1] = prop.len < 50 ? prop.len : 0 - const l = (defaultValue instanceof Uint8Array) - ? (buf.set(defaultValue, 6), defaultLen) - : writeString({ buf } as Ctx, defaultValue, 6, LangCode.none, false) - if (l != buf.length) { - buf = buf.subarray(0, 6 + l) - } - writeUint32(buf, l, 2) // default len - - return [...buf] + console.warn('TODO default!!') + // const defaultValue = + // typeof prop.default === 'string' + // ? prop.default.normalize('NFKD') + // : type === PropType.json + // ? JSON.stringify(prop.default) + // : prop.default + // const defaultLen = + // defaultValue instanceof Uint8Array + // ? defaultValue.byteLength + // : 2 * native.stringByteLength(defaultValue) + STRING_EXTRA_MAX + // let buf = new Uint8Array(6 + defaultLen) + + // buf[0] = selvaType + // buf[1] = prop.len < 50 ? prop.len : 0 + // const l = + // defaultValue instanceof Uint8Array + // ? (buf.set(defaultValue, 6), defaultLen) + // : writeString({ buf } as Ctx, defaultValue, 6, LangCode.none, false) + // if (l != buf.length) { + // buf = buf.subarray(0, 6 + l) + // } + // writeUint32(buf, l, 2) // default len + + // return [...buf] } else { const buf = new Uint8Array(6) @@ -152,17 +153,18 @@ const propDefBuffer = ( // [ type, nrDefaults, [len, default], [len, default]...] for (const langName in prop.default) { - const lang = LangCode[langName] - const value = prop.default[langName].normalize('NFKD') - const tmpLen = 4 + 2 * native.stringByteLength(value) + STRING_EXTRA_MAX - let buf = new Uint8Array(tmpLen) - - const l = writeString({ buf } as Ctx, value, 4, lang, false) - if (l != buf.length) { - buf = buf.subarray(0, 4 + l) - } - writeUint32(buf, l, 0) // length of the default - fs.push(...buf) + console.warn('TODO default!!') + // const lang = LangCode[langName] + // const value = prop.default[langName].normalize('NFKD') + // const tmpLen = 4 + 2 * native.stringByteLength(value) + STRING_EXTRA_MAX + // let buf = new Uint8Array(tmpLen) + + // const l = writeString({ buf } as Ctx, value, 4, lang, false) + // if (l != buf.length) { + // buf = buf.subarray(0, 4 + l) + // } + // writeUint32(buf, l, 0) // length of the default + // fs.push(...buf) } return fs diff --git a/src/index.ts b/src/index.ts index 0706436eb6..02b4ee8385 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ -import { stringCompress } from './db-client/string.js' +// import { stringCompress } from './db-client/string.js' import { DbServer } from './db-server/index.js' import { DbClient } from './db-client/index.js' import { debugMode, debugServer } from './utils/debug.js' import { getDefaultHooks } from './db-client/hooks.js' import { Emitter } from './shared/Emitter.js' import wait from './utils/wait.js' -export { stringCompress } +// export { stringCompress } export { DbClient, DbServer } export { xxHash64 } from './db-client/xxHash64.js' export { crc32 } from './db-client/crc32.js' @@ -64,29 +64,29 @@ export class BasedDb extends Emitter { return this.client.create.apply(this.client, arguments) } - copy: DbClient['copy'] = function (this: BasedDb) { - return this.client.copy.apply(this.client, arguments) - } + // copy: DbClient['copy'] = function (this: BasedDb) { + // return this.client.copy.apply(this.client, arguments) + // } update: DbClient['update'] = function (this: BasedDb) { return this.client.update.apply(this.client, arguments) } - upsert: DbClient['upsert'] = function (this: BasedDb) { - return this.client.upsert.apply(this.client, arguments) - } + // upsert: DbClient['upsert'] = function (this: BasedDb) { + // return this.client.upsert.apply(this.client, arguments) + // } - insert: DbClient['insert'] = function (this: BasedDb) { - return this.client.insert.apply(this.client, arguments) - } + // insert: DbClient['insert'] = function (this: BasedDb) { + // return this.client.insert.apply(this.client, arguments) + // } delete: DbClient['delete'] = function (this: BasedDb) { return this.client.delete.apply(this.client, arguments) } - expire: DbClient['expire'] = function (this: BasedDb) { - return this.client.expire.apply(this.client, arguments) - } + // expire: DbClient['expire'] = function (this: BasedDb) { + // return this.client.expire.apply(this.client, arguments) + // } query: DbClient['query'] = function (this: BasedDb) { return this.client.query.apply(this.client, arguments) @@ -135,7 +135,7 @@ export class BasedDb extends Emitter { await this.isModified() // Tmp fix: Gives node time to GC existing buffers else it can incorrectly re-asign to mem // Todo: clear all active queries, queues ETC - await wait(Math.max(this.client.flushTime + 10, 10)) + await wait(Math.max(this.client.modifyCtx.flushTime + 10, 10)) this.client.destroy() await this.server.destroy() } diff --git a/src/modify/index.ts b/src/modify/index.ts deleted file mode 100644 index c085117b8d..0000000000 --- a/src/modify/index.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { SchemaOut } from '../schema.js' -import { - Modify, - pushModifyCreateHeader, - pushModifyDeleteHeader, - pushModifyMainHeader, - pushModifyPropHeader, - pushModifyUpdateHeader, - writeModifyCreateHeaderProps, - writeModifyPropHeaderProps, - writeModifyUpdateHeaderProps, - type LangCodeEnum, - type ModifyEnum, -} from '../zigTsExports.js' -import { AutoSizedUint8Array } from './AutoSizedUint8Array.js' -import type { PropDef, PropTree, TypeDef } from './defs/index.js' -import { InferPayload } from './types.js' -import { getTypeDefs } from './defs/getTypeDefs.js' -export { getTypeDefs } - -export const serializeProps = ( - tree: PropTree, - data: any, - buf: AutoSizedUint8Array, - op: ModifyEnum, - lang: LangCodeEnum, -) => { - for (const key in data) { - const def = tree.get(key) - if (def === undefined) { - continue - } - const val = data[key] - if (def.constructor === Map) { - if (val !== null && typeof val === 'object') { - serializeProps(def, val, buf, op, lang) - } - } else { - const prop = def as PropDef - if (prop.size) { - pushModifyMainHeader(buf, prop) - prop.pushValue(buf, val, op, lang) - } else { - const index = pushModifyPropHeader(buf, prop) - const start = buf.length - prop.pushValue(buf, val, op, lang) - writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) - } - } - } -} - -const getTypeDef = (schema: SchemaOut, type: string) => { - const typeDef = getTypeDefs(schema).get(type) - if (!typeDef) { - throw new Error(`Type ${type} not found`) - } - return typeDef -} - -export const serializeCreate = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - payload: InferPayload[T], - buf: AutoSizedUint8Array, - lang: LangCodeEnum, -) => { - const typeDef = getTypeDef(schema, type) - const index = pushModifyCreateHeader(buf, { - op: Modify.create, - type: typeDef.id, - size: 0, - }) - const start = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.create, lang) - writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) -} - -export const serializeUpdate = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - id: number, - payload: InferPayload[T], - buf: AutoSizedUint8Array, - lang: LangCodeEnum, -) => { - const typeDef = getTypeDef(schema, type) - const index = pushModifyUpdateHeader(buf, { - op: Modify.update, - type: typeDef.id, - id, - size: 0, - }) - const start = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.update, lang) - writeModifyUpdateHeaderProps.size(buf.data, buf.length - start, index) -} - -export const serializeDelete = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - id: number, - buf: AutoSizedUint8Array, -) => { - const typeDef = getTypeDef(schema, type) - pushModifyDeleteHeader(buf, { - op: Modify.delete, - type: typeDef.id, - id, - }) -} diff --git a/src/modify/types.ts b/src/modify/types.ts deleted file mode 100644 index 6727f141c2..0000000000 --- a/src/modify/types.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - type SchemaTypes, -} from '../schema.js' - -type TypedArray = - | Uint8Array - | Float32Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array - -type TypeMap = { - string: string - number: number - int8: number - uint8: number - int16: number - uint16: number - int32: number - uint32: number - boolean: boolean - text: string - json: any - timestamp: number | string | Date - binary: Uint8Array - alias: string - vector: TypedArray - colvec: TypedArray - cardinality: string | string[] -} - -type InferProp = Prop extends { type: 'object'; props: infer P } - ? InferType - : Prop extends { type: infer T extends keyof TypeMap } - ? TypeMap[T] - : Prop extends { enum: infer E extends readonly any[] } - ? E[number] - : Prop extends { ref: string } - ? string | number - : Prop extends { items: { ref: string } } - ? (string | number)[] - : never - -type InferType = { - [K in keyof Props as Props[K] extends { required: true } - ? K - : never]: InferProp -} & { - [K in keyof Props as Props[K] extends { required: true } - ? never - : K]?: InferProp -} - -export type InferPayload> = { - [K in keyof Types]: InferType -} \ No newline at end of file diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 42eeef06da..69eba91f2d 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -8,7 +8,7 @@ import { readUint64, readInt64, readFloatLE, readDoubleLE } from './utils/index.js' -import { AutoSizedUint8Array } from './modify/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js' export type TypeId = number @@ -660,7 +660,7 @@ export const pushModifyMainHeader = ( export type ModifyPropHeader = { id: number - type: number + type: PropTypeEnum size: number } @@ -677,7 +677,7 @@ export const packModifyPropHeader = (obj: ModifyPropHeader): bigint => { export const unpackModifyPropHeader = (val: bigint): ModifyPropHeader => { return { id: Number((val >> 0n) & 255n), - type: Number((val >> 8n) & 255n), + type: (Number((val >> 8n) & 255n)) as PropTypeEnum, size: Number((val >> 16n) & 4294967295n), } } @@ -700,7 +700,7 @@ export const writeModifyPropHeaderProps = { id: (buf: Uint8Array, value: number, offset: number) => { buf[offset] = Number(value) }, - type: (buf: Uint8Array, value: number, offset: number) => { + type: (buf: Uint8Array, value: PropTypeEnum, offset: number) => { buf[offset + 1] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -714,7 +714,7 @@ export const readModifyPropHeader = ( ): ModifyPropHeader => { const value: ModifyPropHeader = { id: buf[offset], - type: buf[offset + 1], + type: (buf[offset + 1]) as PropTypeEnum, size: readUint32(buf, offset + 2), } return value @@ -722,7 +722,7 @@ export const readModifyPropHeader = ( export const readModifyPropHeaderProps = { id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => buf[offset + 1], + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } @@ -746,7 +746,7 @@ export const pushModifyPropHeader = ( export const ModifyReferences = { clear: 0, ids: 1, - idsAndEdges: 2, + idsWithMeta: 2, tmpIds: 3, delIds: 4, delTmpIds: 5, @@ -755,7 +755,7 @@ export const ModifyReferences = { export const ModifyReferencesInverse = { 0: 'clear', 1: 'ids', - 2: 'idsAndEdges', + 2: 'idsWithMeta', 3: 'tmpIds', 4: 'delIds', 5: 'delTmpIds', @@ -764,7 +764,7 @@ export const ModifyReferencesInverse = { /** clear, ids, - idsAndEdges, + idsWithMeta, tmpIds, delIds, delTmpIds @@ -845,149 +845,298 @@ export const pushModifyReferencesHeader = ( return index } -export type ModifyEdgesHeader = { +export type ModifyReferencesMetaHeader = { id: number + isTmp: boolean withIndex: boolean index: number size: number } -export const ModifyEdgesHeaderByteSize = 13 +export const ModifyReferencesMetaHeaderByteSize = 13 -export const packModifyEdgesHeader = (obj: ModifyEdgesHeader): bigint => { +export const packModifyReferencesMetaHeader = (obj: ModifyReferencesMetaHeader): bigint => { let val = 0n val |= (BigInt(obj.id) & 4294967295n) << 0n - val |= ((obj.withIndex ? 1n : 0n) & 1n) << 32n - val |= (BigInt(obj.index) & 4294967295n) << 33n - val |= (BigInt(obj.size) & 4294967295n) << 65n + val |= ((obj.isTmp ? 1n : 0n) & 1n) << 32n + val |= ((obj.withIndex ? 1n : 0n) & 1n) << 33n + val |= (BigInt(obj.index) & 4294967295n) << 40n + val |= (BigInt(obj.size) & 4294967295n) << 72n return val } -export const unpackModifyEdgesHeader = (val: bigint): ModifyEdgesHeader => { +export const unpackModifyReferencesMetaHeader = (val: bigint): ModifyReferencesMetaHeader => { return { id: Number((val >> 0n) & 4294967295n), - withIndex: ((val >> 32n) & 1n) === 1n, - index: Number((val >> 33n) & 4294967295n), - size: Number((val >> 65n) & 4294967295n), + isTmp: ((val >> 32n) & 1n) === 1n, + withIndex: ((val >> 33n) & 1n) === 1n, + index: Number((val >> 40n) & 4294967295n), + size: Number((val >> 72n) & 4294967295n), } } -export const writeModifyEdgesHeader = ( +export const writeModifyReferencesMetaHeader = ( buf: Uint8Array, - header: ModifyEdgesHeader, + header: ModifyReferencesMetaHeader, offset: number, ): number => { writeUint32(buf, Number(header.id), offset) offset += 4 buf[offset] = 0 - buf[offset] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 0 - buf[offset] |= ((header.index >>> 0) & 127) << 1 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.index >>> 7) & 255) << 0 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.index >>> 15) & 255) << 0 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.index >>> 23) & 255) << 0 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.index >>> 31) & 1) << 0 - buf[offset] |= ((header.size >>> 0) & 127) << 1 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.size >>> 7) & 255) << 0 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.size >>> 15) & 255) << 0 - offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.size >>> 23) & 255) << 0 + buf[offset] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 1 + buf[offset] |= ((0 >>> 0) & 63) << 2 offset += 1 - buf[offset] = 0 - buf[offset] |= ((header.size >>> 31) & 1) << 0 + writeUint32(buf, Number(header.index), offset) + offset += 4 + writeUint32(buf, Number(header.size), offset) + offset += 4 return offset } -export const writeModifyEdgesHeaderProps = { +export const writeModifyReferencesMetaHeaderProps = { id: (buf: Uint8Array, value: number, offset: number) => { writeUint32(buf, Number(value), offset) }, - withIndex: (buf: Uint8Array, value: boolean, offset: number) => { + isTmp: (buf: Uint8Array, value: boolean, offset: number) => { buf[offset + 4] |= (((value ? 1 : 0) >>> 0) & 1) << 0 }, + withIndex: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 4] |= (((value ? 1 : 0) >>> 0) & 1) << 1 + }, index: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 4] |= ((value >>> 0) & 127) << 1 - buf[offset + 5] |= ((value >>> 7) & 255) << 0 - buf[offset + 6] |= ((value >>> 15) & 255) << 0 - buf[offset + 7] |= ((value >>> 23) & 255) << 0 - buf[offset + 8] |= ((value >>> 31) & 1) << 0 + writeUint32(buf, Number(value), offset + 5) }, size: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 8] |= ((value >>> 0) & 127) << 1 - buf[offset + 9] |= ((value >>> 7) & 255) << 0 - buf[offset + 10] |= ((value >>> 15) & 255) << 0 - buf[offset + 11] |= ((value >>> 23) & 255) << 0 - buf[offset + 12] |= ((value >>> 31) & 1) << 0 + writeUint32(buf, Number(value), offset + 9) }, } -export const readModifyEdgesHeader = ( +export const readModifyReferencesMetaHeader = ( buf: Uint8Array, offset: number, -): ModifyEdgesHeader => { - const value: ModifyEdgesHeader = { +): ModifyReferencesMetaHeader => { + const value: ModifyReferencesMetaHeader = { id: readUint32(buf, offset), - withIndex: (((buf[offset + 4] >>> 0) & 1)) === 1, - index: (((buf[offset + 4] >>> 1) & 127) | (((buf[offset + 5] >>> 0) & 255) << 7) | (((buf[offset + 6] >>> 0) & 255) << 15) | (((buf[offset + 7] >>> 0) & 255) << 23) | (((buf[offset + 8] >>> 0) & 1) << 31)), - size: (((buf[offset + 8] >>> 1) & 127) | (((buf[offset + 9] >>> 0) & 255) << 7) | (((buf[offset + 10] >>> 0) & 255) << 15) | (((buf[offset + 11] >>> 0) & 255) << 23) | (((buf[offset + 12] >>> 0) & 1) << 31)), + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (((buf[offset + 4] >>> 1) & 1)) === 1, + index: readUint32(buf, offset + 5), + size: readUint32(buf, offset + 9), } return value } -export const readModifyEdgesHeaderProps = { +export const readModifyReferencesMetaHeaderProps = { id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - index: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 127) | (((buf[offset + 5] >>> 0) & 255) << 7) | (((buf[offset + 6] >>> 0) & 255) << 15) | (((buf[offset + 7] >>> 0) & 255) << 23) | (((buf[offset + 8] >>> 0) & 1) << 31)), - size: (buf: Uint8Array, offset: number) => (((buf[offset + 8] >>> 1) & 127) | (((buf[offset + 9] >>> 0) & 255) << 7) | (((buf[offset + 10] >>> 0) & 255) << 15) | (((buf[offset + 11] >>> 0) & 255) << 23) | (((buf[offset + 12] >>> 0) & 1) << 31)), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), } -export const createModifyEdgesHeader = (header: ModifyEdgesHeader): Uint8Array => { - const buffer = new Uint8Array(ModifyEdgesHeaderByteSize) - writeModifyEdgesHeader(buffer, header, 0) +export const createModifyReferencesMetaHeader = (header: ModifyReferencesMetaHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyReferencesMetaHeaderByteSize) + writeModifyReferencesMetaHeader(buffer, header, 0) return buffer } -export const pushModifyEdgesHeader = ( +export const pushModifyReferencesMetaHeader = ( buf: AutoSizedUint8Array, - header: ModifyEdgesHeader, + header: ModifyReferencesMetaHeader, ): number => { const index = buf.length buf.pushU32(Number(header.id)) buf.pushU8(0) - buf.view[buf.length - 1] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 0 - buf.view[buf.length - 1] |= ((header.index >>> 0) & 127) << 1 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.index >>> 7) & 255) << 0 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.index >>> 15) & 255) << 0 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.index >>> 23) & 255) << 0 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.index >>> 31) & 1) << 0 - buf.view[buf.length - 1] |= ((header.size >>> 0) & 127) << 1 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.size >>> 7) & 255) << 0 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.size >>> 15) & 255) << 0 - buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.size >>> 23) & 255) << 0 + buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 1 + buf.view[buf.length - 1] |= ((0 >>> 0) & 63) << 2 + buf.pushU32(Number(header.index)) + buf.pushU32(Number(header.size)) + return index +} + +export type ModifyReferenceMetaHeader = { + id: number + isTmp: boolean + size: number +} + +export const ModifyReferenceMetaHeaderByteSize = 9 + +export const packModifyReferenceMetaHeader = (obj: ModifyReferenceMetaHeader): bigint => { + let val = 0n + val |= (BigInt(obj.id) & 4294967295n) << 0n + val |= ((obj.isTmp ? 1n : 0n) & 1n) << 32n + val |= (BigInt(obj.size) & 4294967295n) << 40n + return val +} + +export const unpackModifyReferenceMetaHeader = (val: bigint): ModifyReferenceMetaHeader => { + return { + id: Number((val >> 0n) & 4294967295n), + isTmp: ((val >> 32n) & 1n) === 1n, + size: Number((val >> 40n) & 4294967295n), + } +} + +export const writeModifyReferenceMetaHeader = ( + buf: Uint8Array, + header: ModifyReferenceMetaHeader, + offset: number, +): number => { + writeUint32(buf, Number(header.id), offset) + offset += 4 + buf[offset] = 0 + buf[offset] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= ((0 >>> 0) & 127) << 1 + offset += 1 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyReferenceMetaHeaderProps = { + id: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset) + }, + isTmp: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 4] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 5) + }, +} + +export const readModifyReferenceMetaHeader = ( + buf: Uint8Array, + offset: number, +): ModifyReferenceMetaHeader => { + const value: ModifyReferenceMetaHeader = { + id: readUint32(buf, offset), + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + size: readUint32(buf, offset + 5), + } + return value +} + +export const readModifyReferenceMetaHeaderProps = { + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), +} + +export const createModifyReferenceMetaHeader = (header: ModifyReferenceMetaHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyReferenceMetaHeaderByteSize) + writeModifyReferenceMetaHeader(buffer, header, 0) + return buffer +} + +export const pushModifyReferenceMetaHeader = ( + buf: AutoSizedUint8Array, + header: ModifyReferenceMetaHeader, +): number => { + const index = buf.length + buf.pushU32(Number(header.id)) buf.pushU8(0) - buf.view[buf.length - 1] |= ((header.size >>> 31) & 1) << 0 + buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 + buf.pushU32(Number(header.size)) return index } +export type ModifyResultItem = { + id: number + err: ModifyErrorEnum +} + +export const ModifyResultItemByteSize = 5 + +export const packModifyResultItem = (obj: ModifyResultItem): bigint => { + let val = 0n + val |= (BigInt(obj.id) & 4294967295n) << 0n + val |= (BigInt(obj.err) & 255n) << 32n + return val +} + +export const unpackModifyResultItem = (val: bigint): ModifyResultItem => { + return { + id: Number((val >> 0n) & 4294967295n), + err: (Number((val >> 32n) & 255n)) as ModifyErrorEnum, + } +} + +export const writeModifyResultItem = ( + buf: Uint8Array, + header: ModifyResultItem, + offset: number, +): number => { + writeUint32(buf, Number(header.id), offset) + offset += 4 + buf[offset] = Number(header.err) + offset += 1 + return offset +} + +export const writeModifyResultItemProps = { + id: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset) + }, + err: (buf: Uint8Array, value: ModifyErrorEnum, offset: number) => { + buf[offset + 4] = Number(value) + }, +} + +export const readModifyResultItem = ( + buf: Uint8Array, + offset: number, +): ModifyResultItem => { + const value: ModifyResultItem = { + id: readUint32(buf, offset), + err: (buf[offset + 4]) as ModifyErrorEnum, + } + return value +} + +export const readModifyResultItemProps = { + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + err: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyErrorEnum, +} + +export const createModifyResultItem = (header: ModifyResultItem): Uint8Array => { + const buffer = new Uint8Array(ModifyResultItemByteSize) + writeModifyResultItem(buffer, header, 0) + return buffer +} + +export const pushModifyResultItem = ( + buf: AutoSizedUint8Array, + header: ModifyResultItem, +): number => { + const index = buf.length + buf.pushU32(Number(header.id)) + buf.pushU8(Number(header.err)) + return index +} + +export const ModifyError = { + null: 0, + nx: 1, + unknown: 2, +} as const + +export const ModifyErrorInverse = { + 0: 'null', + 1: 'nx', + 2: 'unknown', +} as const + +/** + null, + nx, + unknown + */ +export type ModifyErrorEnum = (typeof ModifyError)[keyof typeof ModifyError] + export const PropType = { null: 0, timestamp: 1, diff --git a/test/include/thread.perf.ts b/test/include/thread.perf.ts index 64086329f3..7b7ae5a244 100644 --- a/test/include/thread.perf.ts +++ b/test/include/thread.perf.ts @@ -156,16 +156,16 @@ await test('include', async (t) => { d = Date.now() - for (let i = 0; i < 1e7; i++) { + for (let i = 0; i < 1e6; i++) { db.create('simple', { nr: 67, // name: i % 2 ? 'b' : 'a', // nr: rand(0, 10), }) } - + console.log('---------------- start drain ?????----------------') await db.drain() - + console.log('---------------- end drain ?????----------------') // {"all_attributes":{"_registerArt":"HRB","_registerNummer":"150148","additional_data":{"AD":true,"CD":true,"DK":true,"HD":false,"SI":true,"UT":true,"VÖ":false},"federal_state":"Hamburg","native_company_number":"Hamburg HRB 150148","registered_office":"Hamburg","registrar":"Hamburg"},"company_number":"K1101R_HRB150148","current_status":"currently registered","jurisdiction_code":"de","name":"olly UG (haftungsbeschränkt)","officers":[{"name":"Oliver Keunecke","other_attributes":{"city":"Hamburg","firstname":"Oliver","flag":"vertretungsberechtigt gemäß allgemeiner Vertretungsregelung","lastname":"Keunecke"},"position":"Geschäftsführer","start_date":"2018-02-06","type":"person"}],"registered_address":"Waidmannstraße 1, 22769 Hamburg.","retrieved_at":"2018-11-09T18:03:03Z"} await perf( diff --git a/test/scenarios/e-commerce.ts b/test/scenarios/e-commerce.ts index 19a3a1da6a..c5f10cfd38 100644 --- a/test/scenarios/e-commerce.ts +++ b/test/scenarios/e-commerce.ts @@ -1,4 +1,4 @@ -import { errors } from '../../src/db-client/modify/error.js' +import { errors } from '../../src/db-client/_modify/error.js' import { BasedDb } from '../../src/index.js' import { throws, equal, isSorted } from '../shared/assert.js' import test from '../shared/test.js' diff --git a/test/youzi.ts b/test/youzi.ts index df4ac8caf6..eb47f4ea59 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -1,6 +1,6 @@ import { BasedDb } from '../src/index.js' -import { AutoSizedUint8Array } from '../src/modify/AutoSizedUint8Array.js' -import { getTypeDefs, serializeCreate } from '../src/modify/index.js' +import { AutoSizedUint8Array } from '../src/db-client/modify/AutoSizedUint8Array.js' +import { getTypeDefs, serializeCreate } from '../src/db-client/modify/index.js' import { parseSchema } from '../src/schema.js' import { LangCode, Modify, pushModifyHeader } from '../src/zigTsExports.js' import test from './shared/test.js' @@ -47,41 +47,114 @@ await test.skip('schema defs', async (t) => { const defs = getTypeDefs(schema) }) -await test('modify', async (t) => { +await test.skip('modify raw', async (t) => { const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) + t.after(() => t.backup(db)) + await db.setSchema({ types: { user: { age: 'number', rating: 'uint8', + // TODO refs have to be ordered + friends: { + items: { + ref: 'user', + prop: 'friends', + $rank: 'uint8', + }, + }, + name: 'string', }, }, }) const buf = new AutoSizedUint8Array() - const index = pushModifyHeader(buf, { + pushModifyHeader(buf, { opId: 0, // is filled on server opType: 0, // is filled on server schema: 0, count: 1, }) + serializeCreate( db.client.schema!, 'user', { age: 32, rating: 5, + name: 'youzi', + }, + buf, + LangCode.nl, + ) + + serializeCreate( + db.client.schema!, + 'user', + { + age: 24, + rating: 54, + name: 'jamez', + friends: [{ id: 1, $rank: 5 }], }, buf, LangCode.nl, ) - await db.server.modify(new Uint8Array(buf.view)) + await db.server.modify(buf.view) + buf.flush() console.log('done did it!') + + const res = await db.query('user').include('*', 'friends.*').get().toObject() + console.dir(res, { depth: null }) +}) + +await test('modify client', async (t) => { + const db = new BasedDb({ path: t.tmp }) + await db.start({ clean: true }) + + t.after(() => t.backup(db)) + + await db.setSchema({ + types: { + user: { + age: 'number', + rating: 'uint8', + // TODO refs have to be ordered + friends: { + items: { + ref: 'user', + prop: 'friends', + $rank: 'uint8', + }, + }, + name: 'string', + }, + }, + }) + + const youzi = await db.create('user', { + age: 32, + rating: 5, + name: 'youzi', + }) + + const jamez = await db.create('user', { + age: 24, + rating: 54, + name: 'jamez', + friends: [{ id: 1, $rank: 5 }], + }) + + console.log('done did it!', { youzi, jamez }) + + const res = await db.query('user').include('*', 'friends.*').get().toObject() + console.dir(res, { depth: null }) }) // await test('reffies', async (t) => { diff --git a/tsconfig.build.json b/tsconfig.build.json index 5e4139c281..f3798fa952 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -4,5 +4,6 @@ "noEmit": false, "rootDir": "src" }, - "include": ["src"] + "include": ["src"], + "exclude": ["dist", "**/_*"] } diff --git a/tsconfig.json b/tsconfig.json index 97e1789593..e5f4ff1efb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,5 @@ "pretty": true }, "include": ["src", "test", "scripts"], - "exclude": ["dist"] + "exclude": ["dist", "**/_*"] } From a51fd9abdbc7fffcb91a57a6b70dbbeb80b23e5d Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 27 Jan 2026 15:46:39 +0100 Subject: [PATCH 003/449] make it better and add cardi --- native/modify/modify.zig | 27 +++++++ native/selva/fields.zig | 4 +- native/types.zig | 10 +++ src/db-client/modify/AutoSizedUint8Array.ts | 10 +++ src/db-client/modify/defs/fixed.ts | 4 ++ src/db-client/modify/defs/getTypeDefs.ts | 40 +++++++++-- src/db-client/modify/defs/references.ts | 2 + src/db-client/modify/defs/vars.ts | 45 ++++++++++-- src/zigTsExports.ts | 80 +++++++++++++++++++++ test/cardinality.ts | 5 +- 10 files changed, 215 insertions(+), 12 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 2a3894d43c..577e46a6de 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -328,6 +328,33 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u const prop = utils.readNext(t.ModifyPropHeader, data, &j); const value = data[j .. j + prop.size]; switch (prop.type) { + .cardinality => { + var k: usize = 0; + const cardinality = utils.readNext(t.ModifyCardinalityHeader, value, &k); + const hll = try Fields.ensurePropTypeString(node, propSchema); + selva.c.hll_init(hll, cardinality.precision, cardinality.sparse); + while (k < value.len) { + const hash = read(u64, value, k); + selva.c.hll_add(hll, hash); + k += 8; + } + // -------------OLD + // const hllMode = data[0] == 0; + // const hllPrecision = data[1]; + // const offset = 2; + // const len = read(u32, data, offset); + // const hll = try Fields.ensurePropTypeString(node, propSchema); + // selva.c.hll_init(hll, hllPrecision, hllMode); + // var i: usize = 4 + offset; + // while (i < (len * 8) + offset) { + // const hash = read(u64, data, i); + // selva.c.hll_add(hll, hash); + // i += 8; + // } + // const newCount = selva.c.hll_count(hll); + // addSortIndexOnCreation(ctx, newCount[0..4]) catch null; + // return len * 8 + 6; + }, .reference => { const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); const refTypeEntry = try Node.getType(db, refTypeId); diff --git a/native/selva/fields.zig b/native/selva/fields.zig index 60f28b78be..ab3b2e5a7f 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -117,10 +117,10 @@ pub fn setColvec(te: Node.Type, nodeId: selva.c.node_id_t, fieldSchema: Schema.F // TODO This is now hll specific but we might want to change it. pub fn ensurePropTypeString( - ctx: *Modify.ModifyCtx, + node: Node.Node, fieldSchema: Schema.FieldSchema, ) !*selva.c.selva_string { - return selva.c.selva_fields_ensure_string(ctx.node.?, fieldSchema, selva.c.HLL_INIT_SIZE) orelse errors.SelvaError.SELVA_EINTYPE; + return selva.c.selva_fields_ensure_string(node, fieldSchema, selva.c.HLL_INIT_SIZE) orelse errors.SelvaError.SELVA_EINTYPE; } pub fn ensureEdgePropTypeString( diff --git a/native/types.zig b/native/types.zig index b449fd08ca..38c3a984bc 100644 --- a/native/types.zig +++ b/native/types.zig @@ -156,6 +156,16 @@ pub const ModifyReferenceMetaHeader = packed struct { size: u32, }; +pub const ModifyCardinalityHeader = packed struct { + sparse: bool, + _padding: u7, + precision: u8, + // id: u32, + // isTmp: bool, + // _padding: u7, + // size: u32, +}; + pub const ModifyResultItem = packed struct { id: u32, err: ModifyError, diff --git a/src/db-client/modify/AutoSizedUint8Array.ts b/src/db-client/modify/AutoSizedUint8Array.ts index 6682fdfde1..67ad5de4cc 100644 --- a/src/db-client/modify/AutoSizedUint8Array.ts +++ b/src/db-client/modify/AutoSizedUint8Array.ts @@ -237,6 +237,16 @@ export class AutoSizedUint8Array { return index } + reserveU64(): number { + const index = this.length + const requiredEnd = index + 8 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + this.length = requiredEnd + return index + } + setSizeU32(start: number) { this.setU32(this.length - start - 4, start) } diff --git a/src/db-client/modify/defs/fixed.ts b/src/db-client/modify/defs/fixed.ts index 0ff2cb01d0..0b39ba7d7f 100644 --- a/src/db-client/modify/defs/fixed.ts +++ b/src/db-client/modify/defs/fixed.ts @@ -1,4 +1,5 @@ import type { EnumItem, SchemaEnum } from '../../../schema.js' +import { convertToTimestamp } from '../../../utils/timestamp.js' import { PropType, type PropTypeEnum } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import { BasePropDef } from './base.js' @@ -13,6 +14,9 @@ export const number = class extends BasePropDef { export const timestamp = class extends number { override type = PropType.timestamp + override pushValue(buf: AutoSizedUint8Array, value: number | string) { + buf.pushI64(convertToTimestamp(value)) + } } export const uint8 = class extends BasePropDef { diff --git a/src/db-client/modify/defs/getTypeDefs.ts b/src/db-client/modify/defs/getTypeDefs.ts index 8505a71052..9494fbad21 100644 --- a/src/db-client/modify/defs/getTypeDefs.ts +++ b/src/db-client/modify/defs/getTypeDefs.ts @@ -4,7 +4,9 @@ import { type SchemaProps, type SchemaType, } from '../../../schema.js' -import { defs, type TypeDef } from './index.js' +import { reorderProps } from '../../../schema/def/utils.js' +import { PropType } from '../../../zigTsExports.js' +import { defs, type PropDef, type PropDefClass, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 @@ -13,6 +15,31 @@ const mainSorter = (a, b) => { return 1 } +const propIndexOffset = (prop: PropDef) => { + switch (prop.type) { + case PropType.microBuffer: + case PropType.vector: + return 'default' in prop.prop ? -600 : 0 + case PropType.string: + case PropType.binary: + case PropType.json: + return 'default' in prop.prop ? -500 : 0 + case PropType.text: + return 'default' in prop.prop ? -400 : 0 + case PropType.references: + case PropType.reference: + return -300 + case PropType.alias: + case PropType.aliases: + case PropType.colVec: + return 300 + default: + return 0 + } +} + +const separateSorter = (a, b) => propIndexOffset(a) - propIndexOffset(b) + const getTypeDef = ({ props }: SchemaType): TypeDef => { const typeDef: TypeDef = { id: 0, @@ -22,8 +49,6 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { tree: new Map(), } - let propId = 1 - const walk = ( props: SchemaProps, pPath: string[], @@ -50,7 +75,6 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { if (def.size) { typeDef.main.push(def) } else { - def.id = propId++ typeDef.separate.push(def) } typeDef.props.set(path.join('.'), def) @@ -62,13 +86,19 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { // -------- finish main -------- typeDef.main.sort(mainSorter) - let start = 0 for (const prop of typeDef.main) { prop.start = start start += prop.size } + // -------- finish separate --------- + typeDef.separate.sort(separateSorter) + let propId = 1 + for (const prop of typeDef.separate) { + prop.id = propId++ + } + return typeDef } diff --git a/src/db-client/modify/defs/references.ts b/src/db-client/modify/defs/references.ts index d416494596..748acefab1 100644 --- a/src/db-client/modify/defs/references.ts +++ b/src/db-client/modify/defs/references.ts @@ -139,6 +139,7 @@ const setReferences = ( writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) continue } + if (typeof item === 'object' && item !== null) { if (item.tmpId) { const index = pushModifyReferencesHeader(buf, { @@ -154,6 +155,7 @@ const setReferences = ( ) continue } + if (typeof item.id === 'number') { // TODO can optimize, don't need whole object const index = pushModifyReferencesHeader(buf, { diff --git a/src/db-client/modify/defs/vars.ts b/src/db-client/modify/defs/vars.ts index eadcd9b55b..d3695bcacf 100644 --- a/src/db-client/modify/defs/vars.ts +++ b/src/db-client/modify/defs/vars.ts @@ -1,17 +1,24 @@ import native from '../../../native.js' import { NOT_COMPRESSED } from '../../../protocol/index.js' -import type { SchemaString, SchemaVector } from '../../../schema.js' +import type { + SchemaCardinality, + SchemaString, + SchemaVector, +} from '../../../schema.js' +import { ENCODER } from '../../../utils/uint8.js' import { + pushModifyCardinalityHeader, PropType, type LangCodeEnum, type ModifyEnum, type PropTypeEnum, } from '../../../zigTsExports.js' +import { xxHash64 } from '../../xxHash64.js' import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import { BasePropDef } from './base.js' export const string = class extends BasePropDef { - constructor(prop: SchemaString, path) { + constructor(prop: SchemaString, path: string[]) { super(prop, path) if (prop.maxBytes && prop.maxBytes < 61) { this.size = prop.maxBytes + 1 @@ -58,7 +65,6 @@ export const binary = class extends BasePropDef { override type = PropType.binary override pushValue(buf: AutoSizedUint8Array, value: Uint8Array) { buf.set(value, buf.length) - buf.length += value.length } } @@ -70,9 +76,40 @@ export const alias = class extends BasePropDef { } export const cardinality = class extends BasePropDef { + constructor(prop: SchemaCardinality, path) { + super(prop, path) + this.sparse = prop.mode === 'sparse' + this.precision = prop.precision ?? 8 + } + sparse: boolean + precision: number override type = PropType.cardinality override pushValue(buf: AutoSizedUint8Array, value: any) { - throw new Error('Serialize cardinality not implemented') + if (value instanceof Uint8Array && value.byteLength !== 8) { + // buf.set(value, buf.length) + throw new Error('unhandled error cardi') + } + + if (!Array.isArray(value)) { + value = [value] + } + + if (value.length === 0) return + + pushModifyCardinalityHeader(buf, this) + + for (const item of value) { + // validate(item, def) + if (typeof item === 'string') { + buf.reserveU64() + xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) + } else if (item instanceof Uint8Array && item.byteLength === 8) { + buf.set(item, buf.length) + } else { + throw new Error('unhandled error cardi') + // throw [def, val] + } + } } } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 5cc421d275..c3a4868c50 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1062,6 +1062,86 @@ export const pushModifyReferenceMetaHeader = ( return index } +export type ModifyCardinalityHeader = { + sparse: boolean + precision: number +} + +export const ModifyCardinalityHeaderByteSize = 2 + +export const ModifyCardinalityHeaderAlignOf = 2 + +export const packModifyCardinalityHeader = (obj: ModifyCardinalityHeader): bigint => { + let val = 0n + val |= ((obj.sparse ? 1n : 0n) & 1n) << 0n + val |= (BigInt(obj.precision) & 255n) << 8n + return val +} + +export const unpackModifyCardinalityHeader = (val: bigint): ModifyCardinalityHeader => { + return { + sparse: ((val >> 0n) & 1n) === 1n, + precision: Number((val >> 8n) & 255n), + } +} + +export const writeModifyCardinalityHeader = ( + buf: Uint8Array, + header: ModifyCardinalityHeader, + offset: number, +): number => { + buf[offset] = 0 + buf[offset] |= (((header.sparse ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= ((0 >>> 0) & 127) << 1 + offset += 1 + buf[offset] = Number(header.precision) + offset += 1 + return offset +} + +export const writeModifyCardinalityHeaderProps = { + sparse: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + }, + precision: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, +} + +export const readModifyCardinalityHeader = ( + buf: Uint8Array, + offset: number, +): ModifyCardinalityHeader => { + const value: ModifyCardinalityHeader = { + sparse: (((buf[offset] >>> 0) & 1)) === 1, + precision: buf[offset + 1], + } + return value +} + +export const readModifyCardinalityHeaderProps = { + sparse: (buf: Uint8Array, offset: number) => (((buf[offset] >>> 0) & 1)) === 1, + precision: (buf: Uint8Array, offset: number) => buf[offset + 1], +} + +export const createModifyCardinalityHeader = (header: ModifyCardinalityHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyCardinalityHeaderByteSize) + writeModifyCardinalityHeader(buffer, header, 0) + return buffer +} + +export const pushModifyCardinalityHeader = ( + buf: AutoSizedUint8Array, + header: ModifyCardinalityHeader, +): number => { + const index = buf.length + buf.pushU8(0) + buf.view[buf.length - 1] |= (((header.sparse ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 + buf.pushU8(Number(header.precision)) + return index +} + export type ModifyResultItem = { id: number err: ModifyErrorEnum diff --git a/test/cardinality.ts b/test/cardinality.ts index 5cd999e926..b8e140ebf5 100644 --- a/test/cardinality.ts +++ b/test/cardinality.ts @@ -2,6 +2,7 @@ import { BasedDb, xxHash64 } from '../src/index.js' import { ENCODER } from '../src/utils/uint8.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' +import { getTypeDefs } from '../src/db-client/modify/index.js' await test('hll', async (t) => { const db = new BasedDb({ @@ -43,6 +44,7 @@ await test('hll', async (t) => { myUniqueValuesCount: 'myCoolValue', }) + console.log('a') deepEqual( ( await db @@ -58,6 +60,7 @@ await test('hll', async (t) => { }, ], ) + console.log('b') deepEqual( ( @@ -365,7 +368,7 @@ await test('switches', async (t) => { const visits = ['Clint', 'Lee', 'Clint', 'Aldo', 'Lee'] - const store1 = db.create('store', { + const store1 = await db.create('store', { name: 'Handsome Sportsman', visitors: visits, visits: visits.length, From b1b14e740c59bcadf43702a27a8b219646ea26f8 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 27 Jan 2026 16:26:32 +0100 Subject: [PATCH 004/449] batching + tmpIds --- native/modify/modify.zig | 13 ++- src/db-client/modify/defs/references.ts | 103 ++++++++++++------------ src/db-client/modify/defs/vars.ts | 1 + src/db-client/modify/index.ts | 9 ++- test/youzi.ts | 8 +- 5 files changed, 71 insertions(+), 63 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 577e46a6de..521260ee7f 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -375,12 +375,22 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u while (k < value.len) { const references = utils.readNext(t.ModifyReferencesHeader, value, &k); const refs = value[k .. k + references.size]; + std.debug.print("ref op {any}\n", .{references.op}); switch (references.op) { .ids => { const offset = utils.alignLeft(u32, refs); const u32Ids = read([]u32, refs[4 - offset .. refs.len - offset], 0); try References.putReferences(db, node, propSchema, u32Ids); }, + .tmpIds => { + const offset = utils.alignLeft(u32, refs); + const u32Ids = read([]u32, refs[4 - offset .. refs.len - offset], 0); + for (u32Ids) |*id| { + id.* = items[id.*].id; + } + std.debug.print("hahah {any}\n", .{u32Ids}); + try References.putReferences(db, node, propSchema, u32Ids); + }, .idsWithMeta => { const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); const refTypeEntry = try Node.getType(db, refTypeId); @@ -392,8 +402,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u var refId = meta.id; if (meta.isTmp) { - // TODO handle isTmp case - refId = meta.id; + refId = items[refId].id; } if (Node.getNode(refTypeEntry, refId)) |dst| { diff --git a/src/db-client/modify/defs/references.ts b/src/db-client/modify/defs/references.ts index 748acefab1..e23073df5d 100644 --- a/src/db-client/modify/defs/references.ts +++ b/src/db-client/modify/defs/references.ts @@ -15,7 +15,7 @@ import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import type { SchemaProp } from '../../../schema.js' import { BasePropDef } from './base.js' import type { PropDef, TypeDef } from './index.js' -import { serializeProps } from '../index.js' +import { ModifyItem, serializeProps } from '../index.js' type Edges = Record<`${string}`, unknown> | undefined @@ -39,10 +39,8 @@ const serializeIds = ( // one extra for padding buf.pushU32(0) for (; i < ids.length; i++) { - const id = ids[i] - if (typeof id !== 'number') { - break - } + const id = getRealId(ids[i]) + if (!id) break buf.pushU32(id) } return i @@ -50,19 +48,16 @@ const serializeIds = ( const serializeTmpIds = ( buf: AutoSizedUint8Array, - items: { tmpId: number }[], + items: ModifyItem[], offset: number, ): undefined | any => { let i = offset // one extra for padding buf.pushU32(0) for (; i < items.length; i++) { - const item = items[i] - if (typeof item !== 'object' || item === null || !item.tmpId) { - // TODO handle async await for tmp in other batch - break - } - buf.pushU32(item.tmpId) + const tmpId = getTmpId(items[i]) + if (tmpId === undefined) break + buf.pushU32(tmpId) } return i @@ -81,18 +76,18 @@ const serializeIdsAndMeta = ( for (; i < items.length; i++) { const item = items[i] - if (item === null || typeof item !== 'object') { - throw 'error' - } - - // TODO handle tmp id - if (typeof item.id !== 'number') { + if (!isValidRefObj(item)) { break } - + const realId = getRealId(item.id) + const id = realId || getTmpId(item.id) + if (id === undefined) { + console.log(item) + throw 'not handled ref' + } const index = pushModifyReferencesMetaHeader(buf, { - id: item.id, - isTmp: false, + id: id, + isTmp: !realId, withIndex: '$index' in item, index: item.$index, size: 0, @@ -118,6 +113,19 @@ const serializeIdsAndMeta = ( return i } +const getRealId = (item: any) => + typeof item === 'number' ? item : item instanceof ModifyItem && item.id + +const getTmpId = (item: any) => { + if (item instanceof ModifyItem && !item._batch.flushed) { + return item._index + } +} + +const isValidRefObj = (item: any) => + (typeof item !== 'object' && item !== null && typeof item.id === 'number') || + item.id instanceof ModifyItem + const setReferences = ( buf: AutoSizedUint8Array, value: any[], @@ -129,7 +137,7 @@ const setReferences = ( while (offset < value.length) { const item = value[offset] - if (typeof item === 'number') { + if (getRealId(item)) { const index = pushModifyReferencesHeader(buf, { op: ModifyReferences.ids, size: 0, @@ -140,40 +148,29 @@ const setReferences = ( continue } - if (typeof item === 'object' && item !== null) { - if (item.tmpId) { - const index = pushModifyReferencesHeader(buf, { - op: ModifyReferences.tmpIds, - size: 0, - }) - const start = buf.length - offset = serializeTmpIds(buf, value, offset) - writeModifyReferencesHeaderProps.size( - buf.data, - buf.length - start, - index, - ) - continue - } + if (getTmpId(item) !== undefined) { + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.tmpIds, + size: 0, + }) + const start = buf.length + offset = serializeTmpIds(buf, value, offset) + writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) + continue + } - if (typeof item.id === 'number') { - // TODO can optimize, don't need whole object - const index = pushModifyReferencesHeader(buf, { - op: ModifyReferences.idsWithMeta, - size: 0, - }) - const start = buf.length - offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) - writeModifyReferencesHeaderProps.size( - buf.data, - buf.length - start, - index, - ) - continue - } + if (isValidRefObj(item)) { + const index = pushModifyReferencesHeader(buf, { + op: ModifyReferences.idsWithMeta, + size: 0, + }) + const start = buf.length + offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) + writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) + continue } - throw 'bad ref' + throw 'bad ref!' } } diff --git a/src/db-client/modify/defs/vars.ts b/src/db-client/modify/defs/vars.ts index d3695bcacf..db96a79e88 100644 --- a/src/db-client/modify/defs/vars.ts +++ b/src/db-client/modify/defs/vars.ts @@ -36,6 +36,7 @@ export const string = class extends BasePropDef { lang: LangCodeEnum, ) { const normalized = val.normalize('NFKD') + // make header! buf.pushU8(lang) buf.pushU8(NOT_COMPRESSED) const written = buf.pushString(normalized) diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index cdd964ca8d..37a91f349d 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -132,7 +132,7 @@ export class ModifyItem implements Promise { [Symbol.toStringTag]!: 'ModifyItem' private _p?: Promise - private _batch: ModifyBatch + private get _promise() { if (!this._p) { this._p ??= new Promise((resolve, reject) => { @@ -146,6 +146,7 @@ export class ModifyItem implements Promise { return this._p } + _batch: ModifyBatch _index: number _id?: number _err?: ModifyErrorEnum @@ -187,6 +188,7 @@ type ModifyBatch = { count: number items?: ModifyItem[] result?: Uint8Array + flushed?: true } export type ModifyCtx = { @@ -204,6 +206,7 @@ export const flush = (ctx: ModifyCtx) => { if (ctx.buf.length) { const batch = ctx.batch writeModifyHeaderProps.count(ctx.buf.data, batch.count, 0) + batch.flushed = true ctx.hooks.flushModify(ctx.buf.view).then((result) => { batch.result = result if (batch.items) { @@ -267,9 +270,7 @@ export const modify = < ;(serialize as (...args: any[]) => void)(...args) } catch (e) { if (e === AutoSizedUint8Array.ERR_OVERFLOW) { - if (isEmpty) { - throw new Error('Range error') - } + if (isEmpty) throw new Error('Range error') ctx.buf.length = initialLength flush(ctx) return modify(ctx, serialize, ...args) diff --git a/test/youzi.ts b/test/youzi.ts index eb47f4ea59..a3b2101527 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -138,22 +138,22 @@ await test('modify client', async (t) => { }, }) - const youzi = await db.create('user', { + const youzi = db.create('user', { age: 32, rating: 5, name: 'youzi', }) - const jamez = await db.create('user', { + const jamez = db.create('user', { age: 24, rating: 54, name: 'jamez', - friends: [{ id: 1, $rank: 5 }], + friends: [{ id: youzi }], }) console.log('done did it!', { youzi, jamez }) - const res = await db.query('user').include('*', 'friends.*').get().toObject() + const res = await db.query('user').include('*', 'friends').get().toObject() console.dir(res, { depth: null }) }) From 2a0333ab75165a79985235968770ce98ba85ba5c Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 27 Jan 2026 21:45:11 +0100 Subject: [PATCH 005/449] wip --- src/db-client/index.ts | 11 ++- src/db-client/modify/defs/references.ts | 39 ++++---- src/db-client/modify/index.ts | 122 ++++++++++++++++++------ test/youzi.ts | 29 +++++- 4 files changed, 144 insertions(+), 57 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 2763110fa1..cb62e97f95 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -103,7 +103,6 @@ export class DbClient extends DbShared { } create(type: string, obj = {}, opts?: ModifyOpts): Promise { - // console.log(this.modifyCtx.buf.length) return modify( this.modifyCtx, serializeCreate, @@ -197,7 +196,7 @@ export class DbClient extends DbShared { } // For more advanced / internal usage - use isModified instead for most cases - async drain() { + drain() { // if (this.upserting.size) { // await Promise.all(Array.from(this.upserting).map(([, { p }]) => p)) // } @@ -206,14 +205,16 @@ export class DbClient extends DbShared { // const t = this.writeTime // this.writeTime = 0 flush(this.modifyCtx) - await this.modifyCtx.lastModify?.catch(noop) + return this.isModified() + // await this.modifyCtx.lastModify?.catch(noop) // await this.modifyCtx.lastModify // return t } async isModified() { - schedule(this.modifyCtx) - await this.modifyCtx.lastModify?.catch(noop) + while (this.modifyCtx.batch.lastModify) { + await this.modifyCtx.batch.lastModify.catch(noop) + } } } diff --git a/src/db-client/modify/defs/references.ts b/src/db-client/modify/defs/references.ts index e23073df5d..c71efe217a 100644 --- a/src/db-client/modify/defs/references.ts +++ b/src/db-client/modify/defs/references.ts @@ -15,7 +15,7 @@ import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' import type { SchemaProp } from '../../../schema.js' import { BasePropDef } from './base.js' import type { PropDef, TypeDef } from './index.js' -import { ModifyItem, serializeProps } from '../index.js' +import { ModifyItem, QueuedItem, serializeProps } from '../index.js' type Edges = Record<`${string}`, unknown> | undefined @@ -82,8 +82,7 @@ const serializeIdsAndMeta = ( const realId = getRealId(item.id) const id = realId || getTmpId(item.id) if (id === undefined) { - console.log(item) - throw 'not handled ref' + break } const index = pushModifyReferencesMetaHeader(buf, { id: id, @@ -113,18 +112,18 @@ const serializeIdsAndMeta = ( return i } -const getRealId = (item: any) => - typeof item === 'number' ? item : item instanceof ModifyItem && item.id +const getRealId = (item: any) => { + if (typeof item === 'number') return item + if (item instanceof ModifyItem || item instanceof QueuedItem) return item.id +} const getTmpId = (item: any) => { - if (item instanceof ModifyItem && !item._batch.flushed) { - return item._index - } + if (item instanceof ModifyItem && !item._batch.flushed) return item._index } const isValidRefObj = (item: any) => - (typeof item !== 'object' && item !== null && typeof item.id === 'number') || - item.id instanceof ModifyItem + (typeof item === 'object' && item !== null && getRealId(item.id)) || + getTmpId(item.id) !== undefined const setReferences = ( buf: AutoSizedUint8Array, @@ -136,7 +135,6 @@ const setReferences = ( let offset = 0 while (offset < value.length) { const item = value[offset] - if (getRealId(item)) { const index = pushModifyReferencesHeader(buf, { op: ModifyReferences.ids, @@ -145,10 +143,7 @@ const setReferences = ( const start = buf.length offset = serializeIds(buf, value, offset) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - continue - } - - if (getTmpId(item) !== undefined) { + } else if (getTmpId(item) !== undefined) { const index = pushModifyReferencesHeader(buf, { op: ModifyReferences.tmpIds, size: 0, @@ -156,10 +151,7 @@ const setReferences = ( const start = buf.length offset = serializeTmpIds(buf, value, offset) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - continue - } - - if (isValidRefObj(item)) { + } else if (isValidRefObj(item)) { const index = pushModifyReferencesHeader(buf, { op: ModifyReferences.idsWithMeta, size: 0, @@ -167,10 +159,13 @@ const setReferences = ( const start = buf.length offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - continue + } else if (item instanceof ModifyItem || item instanceof QueuedItem) { + throw item + } else if (item.id instanceof ModifyItem || item instanceof QueuedItem) { + throw item.id + } else { + throw 'bad ref!' } - - throw 'bad ref!' } } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 37a91f349d..d7b943a58d 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -123,25 +123,80 @@ export const serializeDelete = < }) } +export class QueuedItem implements Promise { + constructor(blocker: ModifyItem | QueuedItem, args: IArguments) { + while (blocker instanceof QueuedItem) { + blocker = blocker._blocker + } + this._blocker = blocker + this._args = args + blocker._batch.queue ??= [] + blocker._batch.queue.push(this) + } + [Symbol.toStringTag]!: 'QueuedItem' + _args: IArguments + _item?: ModifyItem + + private _blocker: ModifyItem + private _p?: Promise + + _promise() { + this._p ??= + this._item || + new Promise((resolve) => { + this._resolve = resolve + }) + return this._p + } + get id(): number | undefined { + return this._item?.id + } + + get err(): ModifyErrorEnum | undefined { + return this._item?.err + } + + _resolve?: (value: number | PromiseLike) => void + + then( + onfulfilled?: ((value: number) => Res1 | PromiseLike) | null, + onrejected?: ((reason: any) => Res2 | PromiseLike) | null, + ): Promise { + return this._promise().then(onfulfilled, onrejected) + } + catch( + onrejected?: ((reason: any) => Res | PromiseLike) | null, + ): Promise { + return this._promise().catch(onrejected) + } + finally(onfinally?: (() => void) | null): Promise { + return this._promise().finally(onfinally) + } +} + export class ModifyItem implements Promise { constructor(batch: ModifyBatch) { this._batch = batch - this._index = batch.count + this._index = batch.count++ + batch.lastModify = this } [Symbol.toStringTag]!: 'ModifyItem' private _p?: Promise - - private get _promise() { - if (!this._p) { - this._p ??= new Promise((resolve, reject) => { + _promise() { + this._p ??= new Promise((resolve, reject) => { + if (this.id) { + resolve(this.id) + } else if (this.err) { + reject(this.err) + } else { this._resolve = resolve this._reject = reject this._batch.items ??= [] this._batch.items.push(this) - }) - } + } + }) return this._p } @@ -150,6 +205,7 @@ export class ModifyItem implements Promise { _index: number _id?: number _err?: ModifyErrorEnum + _args?: IArguments get id(): number | undefined { if (this._batch.result) { @@ -172,30 +228,37 @@ export class ModifyItem implements Promise { onfulfilled?: ((value: number) => Res1 | PromiseLike) | null, onrejected?: ((reason: any) => Res2 | PromiseLike) | null, ): Promise { - return this._promise.then(onfulfilled, onrejected) + return this._promise().then(onfulfilled, onrejected) } catch( onrejected?: ((reason: any) => Res | PromiseLike) | null, ): Promise { - return this._promise.catch(onrejected) + return this._promise().catch(onrejected) } finally(onfinally?: (() => void) | null): Promise { - return this._promise.finally(onfinally) + return this._promise().finally(onfinally) } } +type ModifySerializer = + | typeof serializeCreate + | typeof serializeUpdate + | typeof serializeDelete + type ModifyBatch = { count: number items?: ModifyItem[] + queue?: QueuedItem[] result?: Uint8Array flushed?: true + lastModify?: ModifyItem } export type ModifyCtx = { buf: AutoSizedUint8Array batch: ModifyBatch flushTime: number - lastModify?: ModifyItem + flushTimer?: NodeJS.Timeout | true | undefined hooks: { flushModify: (buf: Uint8Array) => Promise @@ -209,8 +272,19 @@ export const flush = (ctx: ModifyCtx) => { batch.flushed = true ctx.hooks.flushModify(ctx.buf.view).then((result) => { batch.result = result - if (batch.items) { - for (const item of batch.items) { + const items = batch.items + const queue = batch.queue + if (queue) { + batch.queue = undefined + for (const item of queue) { + const res = modify.apply(null, item._args) + item._item = res + item._resolve?.(res) + } + } + if (items) { + batch.items = undefined + for (const item of items) { const id = item.id const err = item.err if (err) { @@ -244,18 +318,12 @@ export const schedule = (ctx: ModifyCtx) => { } } -export const modify = < - S extends - | typeof serializeCreate - | typeof serializeUpdate - | typeof serializeDelete, ->( +export const modify = function ( ctx: ModifyCtx, serialize: S, ...args: Parameters -): Promise => { +): Promise { const isEmpty = ctx.buf.length === 0 - if (isEmpty) { pushModifyHeader(ctx.buf, { opId: 0, // is filled on server @@ -264,24 +332,22 @@ export const modify = < count: 0, }) } - const initialLength = ctx.buf.length try { ;(serialize as (...args: any[]) => void)(...args) } catch (e) { + ctx.buf.length = initialLength if (e === AutoSizedUint8Array.ERR_OVERFLOW) { if (isEmpty) throw new Error('Range error') - ctx.buf.length = initialLength flush(ctx) - return modify(ctx, serialize, ...args) + return modify.apply(null, arguments) + } else if (e instanceof ModifyItem || e instanceof QueuedItem) { + return new QueuedItem(e, arguments) } else { throw e } } - const item = new ModifyItem(ctx.batch) - ctx.lastModify = item - ctx.batch.count++ schedule(ctx) - return item + return new ModifyItem(ctx.batch) } diff --git a/test/youzi.ts b/test/youzi.ts index a3b2101527..6b68184630 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -1,6 +1,10 @@ import { BasedDb } from '../src/index.js' import { AutoSizedUint8Array } from '../src/db-client/modify/AutoSizedUint8Array.js' -import { getTypeDefs, serializeCreate } from '../src/db-client/modify/index.js' +import { + flush, + getTypeDefs, + serializeCreate, +} from '../src/db-client/modify/index.js' import { parseSchema } from '../src/schema.js' import { LangCode, Modify, pushModifyHeader } from '../src/zigTsExports.js' import test from './shared/test.js' @@ -144,13 +148,34 @@ await test('modify client', async (t) => { name: 'youzi', }) + // olli uses TMPID for youzi + const olli = db.create('user', { + age: 22, + rating: 256, + name: 'olli', + friends: [youzi], + }) + + // youzi is now in-flight + flush(db.client.modifyCtx) + + // james WILL BE QUEUED until youzi is done -> because we need that reference const jamez = db.create('user', { age: 24, rating: 54, name: 'jamez', - friends: [{ id: youzi }], + friends: [youzi], + }) + + // this WILL NOT BE QUEUED ----> different order + const marco = db.create('user', { + age: 28, + rating: 100, + name: 'mr marco', + friends: [jamez], }) + // await db.drain() console.log('done did it!', { youzi, jamez }) const res = await db.query('user').include('*', 'friends').get().toObject() From 075b981e67bdc6742fa296c94c4bf8639bac3086 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 07:51:48 +0100 Subject: [PATCH 006/449] ok --- src/db-client/index.ts | 4 +--- src/db-client/modify/defs/references.ts | 1 + src/db-client/modify/index.ts | 12 +++++------- test/youzi.ts | 3 --- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index cb62e97f95..2125259a52 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -212,9 +212,7 @@ export class DbClient extends DbShared { } async isModified() { - while (this.modifyCtx.batch.lastModify) { - await this.modifyCtx.batch.lastModify.catch(noop) - } + await this.modifyCtx.lastModify?.catch(noop) } } diff --git a/src/db-client/modify/defs/references.ts b/src/db-client/modify/defs/references.ts index c71efe217a..01d2684523 100644 --- a/src/db-client/modify/defs/references.ts +++ b/src/db-client/modify/defs/references.ts @@ -118,6 +118,7 @@ const getRealId = (item: any) => { } const getTmpId = (item: any) => { + if (item instanceof QueuedItem) item = item._item if (item instanceof ModifyItem && !item._batch.flushed) return item._index } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index d7b943a58d..4e095fe108 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -136,8 +136,7 @@ export class QueuedItem implements Promise { [Symbol.toStringTag]!: 'QueuedItem' _args: IArguments _item?: ModifyItem - - private _blocker: ModifyItem + _blocker: ModifyItem | QueuedItem private _p?: Promise _promise() { @@ -148,6 +147,7 @@ export class QueuedItem implements Promise { }) return this._p } + get id(): number | undefined { return this._item?.id } @@ -178,7 +178,6 @@ export class ModifyItem implements Promise { constructor(batch: ModifyBatch) { this._batch = batch this._index = batch.count++ - batch.lastModify = this } [Symbol.toStringTag]!: 'ModifyItem' @@ -251,14 +250,13 @@ type ModifyBatch = { queue?: QueuedItem[] result?: Uint8Array flushed?: true - lastModify?: ModifyItem } export type ModifyCtx = { buf: AutoSizedUint8Array batch: ModifyBatch flushTime: number - + lastModify?: ModifyItem | QueuedItem flushTimer?: NodeJS.Timeout | true | undefined hooks: { flushModify: (buf: Uint8Array) => Promise @@ -342,12 +340,12 @@ export const modify = function ( flush(ctx) return modify.apply(null, arguments) } else if (e instanceof ModifyItem || e instanceof QueuedItem) { - return new QueuedItem(e, arguments) + return (ctx.lastModify = new QueuedItem(e, arguments)) } else { throw e } } schedule(ctx) - return new ModifyItem(ctx.batch) + return (ctx.lastModify = new ModifyItem(ctx.batch)) } diff --git a/test/youzi.ts b/test/youzi.ts index 6b68184630..5249521761 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -175,9 +175,6 @@ await test('modify client', async (t) => { friends: [jamez], }) - // await db.drain() - console.log('done did it!', { youzi, jamez }) - const res = await db.query('user').include('*', 'friends').get().toObject() console.dir(res, { depth: null }) }) From c677184833b4b5db3c0241d0d8f32b7f386c158b Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 07:57:25 +0100 Subject: [PATCH 007/449] add batching --- src/db-client/index.ts | 6 +++++- test/youzi.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 2125259a52..82a7bdd971 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -212,7 +212,11 @@ export class DbClient extends DbShared { } async isModified() { - await this.modifyCtx.lastModify?.catch(noop) + let lastModify + while (lastModify !== this.modifyCtx.lastModify) { + lastModify = this.modifyCtx.lastModify + await lastModify.catch(noop) + } } } diff --git a/test/youzi.ts b/test/youzi.ts index 5249521761..810f7be031 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -172,7 +172,7 @@ await test('modify client', async (t) => { age: 28, rating: 100, name: 'mr marco', - friends: [jamez], + // friends: [jamez], }) const res = await db.query('user').include('*', 'friends').get().toObject() From f1249ff18714369a0f8d7e6d2f91e7b1cbc8bd5d Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 08:20:10 +0100 Subject: [PATCH 008/449] update test --- test/youzi.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/test/youzi.ts b/test/youzi.ts index 810f7be031..066d517741 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -127,8 +127,8 @@ await test('modify client', async (t) => { await db.setSchema({ types: { user: { - age: 'number', - rating: 'uint8', + // age: 'number', + // rating: 'uint8', // TODO refs have to be ordered friends: { items: { @@ -143,15 +143,11 @@ await test('modify client', async (t) => { }) const youzi = db.create('user', { - age: 32, - rating: 5, name: 'youzi', }) // olli uses TMPID for youzi const olli = db.create('user', { - age: 22, - rating: 256, name: 'olli', friends: [youzi], }) @@ -161,18 +157,29 @@ await test('modify client', async (t) => { // james WILL BE QUEUED until youzi is done -> because we need that reference const jamez = db.create('user', { - age: 24, - rating: 54, name: 'jamez', friends: [youzi], }) // this WILL NOT BE QUEUED ----> different order const marco = db.create('user', { - age: 28, - rating: 100, name: 'mr marco', - // friends: [jamez], + }) + + jamez.then(() => { + const fulco = db + .create('user', { + name: 'mr fulco', + friends: [jamez], + }) + .then(() => { + const tom = db + .create('user', { + name: 'mr tom', + friends: [jamez], + }) + .then() + }) }) const res = await db.query('user').include('*', 'friends').get().toObject() From f1f059bbdc2e9c5699ea639c558f2993630eda50 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 09:52:17 +0100 Subject: [PATCH 009/449] wip --- native/modify/modify.zig | 19 ++++++-- src/db-client/modify/defs/references.ts | 59 ++++++++++++++++++++++--- src/db-client/modify/index.ts | 3 +- test/youzi.ts | 15 +++++-- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 521260ee7f..55fcad1e40 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -358,10 +358,23 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u .reference => { const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); const refTypeEntry = try Node.getType(db, refTypeId); - // TODO add TMP handling - const refId = read(u32, value, 0); + var k: usize = 0; + const meta = utils.readNext(t.ModifyReferenceMetaHeader, value, &k); + var refId = meta.id; + + if (meta.isTmp) { + refId = items[refId].id; + } + if (Node.getNode(refTypeEntry, refId)) |dst| { _ = try References.writeReference(db, node, propSchema, dst); + + if (meta.size != 0) { + const edgeProps = value[k .. k + meta.size]; + const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); + const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); + try modifyProps(db, edgeType, dst, edgeProps, items); + } } }, .references => { @@ -375,7 +388,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u while (k < value.len) { const references = utils.readNext(t.ModifyReferencesHeader, value, &k); const refs = value[k .. k + references.size]; - std.debug.print("ref op {any}\n", .{references.op}); switch (references.op) { .ids => { const offset = utils.alignLeft(u32, refs); @@ -388,7 +400,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u for (u32Ids) |*id| { id.* = items[id.*].id; } - std.debug.print("hahah {any}\n", .{u32Ids}); try References.putReferences(db, node, propSchema, u32Ids); }, .idsWithMeta => { diff --git a/src/db-client/modify/defs/references.ts b/src/db-client/modify/defs/references.ts index 01d2684523..a226380905 100644 --- a/src/db-client/modify/defs/references.ts +++ b/src/db-client/modify/defs/references.ts @@ -5,6 +5,7 @@ import { pushModifyReferenceMetaHeader, pushModifyReferencesHeader, pushModifyReferencesMetaHeader, + writeModifyReferenceMetaHeaderProps, writeModifyReferencesHeaderProps, writeModifyReferencesMetaHeaderProps, type LangCodeEnum, @@ -229,17 +230,63 @@ export const references = class extends BasePropDef { export const reference = class extends BasePropDef { override type: PropTypeEnum = PropType.reference - override pushValue(buf: AutoSizedUint8Array, value: any, op: ModifyEnum) { - if (typeof value === 'number') { + override pushValue( + buf: AutoSizedUint8Array, + value: any, + op: ModifyEnum, + lang: LangCodeEnum, + ) { + const id = getRealId(value) + if (id) { pushModifyReferenceMetaHeader(buf, { - id: value, + id, isTmp: false, size: 0, }) - } else { - console.error('TODO reference ALL THE CASES') + return + } + const tmpId = getTmpId(value) + if (tmpId !== undefined) { + pushModifyReferenceMetaHeader(buf, { + id: tmpId, + isTmp: true, + size: 0, + }) + return + } + + if (value instanceof ModifyItem || value instanceof QueuedItem) { + throw value } - // buf.pushU32(value) + if (typeof value === 'object' && value !== null) { + const realId = getRealId(value.id) + const id = realId || getTmpId(value.id) + if (id !== undefined) { + const index = pushModifyReferenceMetaHeader(buf, { + id, + isTmp: !realId, + size: 0, + }) + const prop: PropDef = this + if (prop.edges) { + const edges = getEdges(value) + if (edges) { + const start = buf.length + serializeProps(prop.edges.tree, edges, buf, op, lang) + writeModifyReferenceMetaHeaderProps.size( + buf.data, + buf.length - start, + index, + ) + } + } + return + } + + if (value.id instanceof ModifyItem || value.id instanceof QueuedItem) { + throw value.id + } + } } } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 4e095fe108..f78ef5c8c6 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -16,7 +16,7 @@ import { type ModifyErrorEnum, } from '../../zigTsExports.js' import { AutoSizedUint8Array } from './AutoSizedUint8Array.js' -import type { PropDef, PropTree, TypeDef } from './defs/index.js' +import type { PropDef, PropTree } from './defs/index.js' import { InferPayload } from './types.js' import { getTypeDefs } from './defs/getTypeDefs.js' import { readUint32 } from '../../utils/uint8.js' @@ -316,6 +316,7 @@ export const schedule = (ctx: ModifyCtx) => { } } +// TODO implement single ModifyCmd export const modify = function ( ctx: ModifyCtx, serialize: S, diff --git a/test/youzi.ts b/test/youzi.ts index 066d517741..2a5079cbb5 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -137,6 +137,11 @@ await test('modify client', async (t) => { $rank: 'uint8', }, }, + friend: { + ref: 'user', + prop: 'friend', + $rating: 'uint8', + }, name: 'string', }, }, @@ -150,6 +155,7 @@ await test('modify client', async (t) => { const olli = db.create('user', { name: 'olli', friends: [youzi], + friend: youzi, }) // youzi is now in-flight @@ -158,12 +164,12 @@ await test('modify client', async (t) => { // james WILL BE QUEUED until youzi is done -> because we need that reference const jamez = db.create('user', { name: 'jamez', - friends: [youzi], + friend: { id: youzi, $rating: 10 }, }) - // this WILL NOT BE QUEUED ----> different order const marco = db.create('user', { name: 'mr marco', + friends: [youzi], }) jamez.then(() => { @@ -171,18 +177,21 @@ await test('modify client', async (t) => { .create('user', { name: 'mr fulco', friends: [jamez], + friend: jamez, }) .then(() => { const tom = db .create('user', { name: 'mr tom', friends: [jamez], + friend: jamez, }) .then() }) }) - const res = await db.query('user').include('*', 'friends').get().toObject() + // this will await all queued modifies + const res = await db.query('user').include('*', 'friend').get().toObject() console.dir(res, { depth: null }) }) From b4aad291ed279d04cb2cefa7b09d2e985c657972 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 11:00:32 +0100 Subject: [PATCH 010/449] alignit --- native/modify/modify.zig | 85 ++++++--------------------------- native/thread/worker/worker.zig | 2 +- src/db-server/index.ts | 5 +- 3 files changed, 19 insertions(+), 73 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 55fcad1e40..d4e7e8b520 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -313,18 +313,20 @@ pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { return i; } -pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []align(1) t.ModifyResultItem) !void { +pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []t.ModifyResultItem) !void { var j: usize = 0; while (j < data.len) { const propId = data[j]; const propSchema = try Schema.getFieldSchema(typeEntry, propId); if (propId == 0) { + // main handling const main = utils.readNext(t.ModifyMainHeader, data, &j); const value = data[j .. j + main.size]; const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); utils.copy(u8, current, value, main.start); j += main.size; } else { + // separate handling const prop = utils.readNext(t.ModifyPropHeader, data, &j); const value = data[j .. j + prop.size]; switch (prop.type) { @@ -338,22 +340,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u selva.c.hll_add(hll, hash); k += 8; } - // -------------OLD - // const hllMode = data[0] == 0; - // const hllPrecision = data[1]; - // const offset = 2; - // const len = read(u32, data, offset); - // const hll = try Fields.ensurePropTypeString(node, propSchema); - // selva.c.hll_init(hll, hllPrecision, hllMode); - // var i: usize = 4 + offset; - // while (i < (len * 8) + offset) { - // const hash = read(u64, data, i); - // selva.c.hll_add(hll, hash); - // i += 8; - // } - // const newCount = selva.c.hll_count(hll); - // addSortIndexOnCreation(ctx, newCount[0..4]) catch null; - // return len * 8 + 6; }, .reference => { const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); @@ -368,7 +354,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u if (Node.getNode(refTypeEntry, refId)) |dst| { _ = try References.writeReference(db, node, propSchema, dst); - if (meta.size != 0) { const edgeProps = value[k .. k + meta.size]; const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); @@ -379,12 +364,10 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u }, .references => { var k: usize = 0; - if (@as(t.ModifyReferences, @enumFromInt(value[0])) == t.ModifyReferences.clear) { References.clearReferences(db, node, propSchema); k += 1; } - while (k < value.len) { const references = utils.readNext(t.ModifyReferencesHeader, value, &k); const refs = value[k .. k + references.size]; @@ -450,22 +433,24 @@ pub fn modify( thread: *Thread.Thread, buf: []u8, db: *DbCtx, - opType: t.OpType, ) !void { var i: usize = 0; - var j: u32 = 4; + var j: u32 = 5 + @alignOf(t.ModifyResultItem); const header = utils.readNext(t.ModifyHeader, buf, &i); const size = j + header.count * 5; const result = try thread.modify.result(size, header.opId, header.opType); - const items = std.mem.bytesAsSlice(t.ModifyResultItem, result[j..]); - _ = opType; - + const alignIndex = thread.modify.index - size + j; + const offset: u8 = @truncate(alignIndex % @alignOf(t.ModifyResultItem)); + result[4] = @alignOf(t.ModifyResultItem) - offset; + j -= offset; + const items = utils.toSlice(t.ModifyResultItem, result[j..]); while (i < buf.len) { const op: t.Modify = @enumFromInt(buf[i]); switch (op) { .create => { - const create = utils.readNext(t.ModifyCreateHeader, buf, &i); + const create = utils.read(t.ModifyCreateHeader, buf, i); + i += utils.sizeOf(t.ModifyCreateHeader); const typeEntry = try Node.getType(db, create.type); const data: []u8 = buf[i .. i + create.size]; const id = db.ids[create.type - 1] + 1; @@ -475,13 +460,13 @@ pub fn modify( }; db.ids[create.type - 1] = id; utils.write(result, id, j); - utils.writeAs(u8, result, t.ModifyError.null, j + 4); i += create.size; j += 5; }, .update => { - const update = utils.readNext(t.ModifyUpdateHeader, buf, &i); + const update = utils.read(t.ModifyUpdateHeader, buf, i); + i += utils.sizeOf(t.ModifyUpdateHeader); const typeEntry = try Node.getType(db, update.type); utils.write(result, update.id, j); if (Node.getNode(typeEntry, update.id)) |node| { @@ -497,7 +482,8 @@ pub fn modify( j += 5; }, .delete => { - const delete = utils.readNext(t.ModifyDeleteHeader, buf, &i); + const delete = utils.read(t.ModifyDeleteHeader, buf, i); + i += utils.sizeOf(t.ModifyDeleteHeader); const typeEntry = try Node.getType(db, delete.type); utils.write(result, delete.id, j); if (Node.getNode(typeEntry, delete.id)) |node| { @@ -518,44 +504,3 @@ pub fn modify( if (j < size) @memset(result[j..size], 0); } - -pub fn _modify( - thread: *Thread.Thread, - batch: []u8, - dbCtx: *DbCtx, - opType: t.OpType, -) !void { - const modifyId = read(u32, batch, 0); - const nodeCount = read(u32, batch, 13); - const expectedLen = 4 + nodeCount * 5; - - var ctx: ModifyCtx = .{ - .result = try thread.modify.result(expectedLen, modifyId, opType), - .resultLen = 4, - .field = undefined, - .typeId = 0, - .id = 0, - .currentSortIndex = null, - .typeSortIndex = null, - .node = null, - .typeEntry = null, - .fieldSchema = null, - .fieldType = t.PropType.null, - .db = dbCtx, - .batch = batch, - .err = t.ModifyError.null, - .idSubs = null, - .subTypes = null, - .thread = thread, - }; - - _ = try writeData(&ctx, batch[13 + 4 ..]); - - Node.expire(&ctx); - writeoutPrevNodeId(&ctx, &ctx.resultLen, ctx.id, ctx.result); - write(ctx.result, ctx.resultLen, 0); - - if (ctx.resultLen < expectedLen) { - @memset(ctx.result[ctx.resultLen..expectedLen], 0); - } -} diff --git a/native/thread/worker/worker.zig b/native/thread/worker/worker.zig index 2f336c78c9..1373ba4d2a 100644 --- a/native/thread/worker/worker.zig +++ b/native/thread/worker/worker.zig @@ -108,7 +108,7 @@ pub fn worker(threads: *Thread.Threads, thread: *common.Thread) !void { .emptyMod => { // does nothing but does trigger flush marked subs and maybe more in the future }, - .modify => try Modify.modify(thread, m, threads.ctx, op), + .modify => try Modify.modify(thread, m, threads.ctx), .loadBlock => try dump.loadBlock(thread, threads.ctx, m, op), .unloadBlock => try dump.unloadBlock(thread, threads.ctx, m, op), .loadCommon => try dump.loadCommon(thread, threads.ctx, m, op), diff --git a/src/db-server/index.ts b/src/db-server/index.ts index c3a437ed11..50ba811326 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -193,8 +193,9 @@ export class DbServer extends DbShared { return new Promise((resolve) => { native.modify(payload, this.dbCtxExternal) this.addOpOnceListener(OpType.modify, id, (v) => { - const resultLen = readUint32(v, 0) - resolve(v.subarray(4, resultLen)) + const end = readUint32(v, 0) + const alignOffset = v[4] + resolve(v.subarray(5 + alignOffset, end)) }) }) } From d70acac904b16c82bc92b40e279bb79c4a2b9ff6 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 12:30:38 +0100 Subject: [PATCH 011/449] move files --- src/db-client/index.ts | 2 +- src/db-client/modify/index.ts | 8 +- .../modify => schema}/defs/getTypeDefs.ts | 21 +- .../modify => schema}/defs/index.ts | 14 +- .../modify/defs => schema/defs/props}/base.ts | 6 +- .../defs => schema/defs/props}/fixed.ts | 30 +- .../defs => schema/defs/props}/references.ts | 16 +- .../vars.ts => schema/defs/props/separate.ts} | 22 +- .../modify => utils}/AutoSizedUint8Array.ts | 4 +- src/zigTsExports.ts | 705 ++++++++++-------- test/youzi.ts | 5 +- 11 files changed, 472 insertions(+), 361 deletions(-) rename src/{db-client/modify => schema}/defs/getTypeDefs.ts (87%) rename src/{db-client/modify => schema}/defs/index.ts (72%) rename src/{db-client/modify/defs => schema/defs/props}/base.ts (82%) rename src/{db-client/modify/defs => schema/defs/props}/fixed.ts (70%) rename src/{db-client/modify/defs => schema/defs/props}/references.ts (95%) rename src/{db-client/modify/defs/vars.ts => schema/defs/props/separate.ts} (87%) rename src/{db-client/modify => utils}/AutoSizedUint8Array.ts (99%) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 82a7bdd971..5b974e4829 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -10,7 +10,7 @@ import { type SchemaMigrateFns, type SchemaOut, } from '../schema/index.js' -import { AutoSizedUint8Array } from './modify/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' import { LangCode } from '../zigTsExports.js' import { serializeCreate, diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index f78ef5c8c6..8b1553dd68 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -15,10 +15,10 @@ import { type ModifyEnum, type ModifyErrorEnum, } from '../../zigTsExports.js' -import { AutoSizedUint8Array } from './AutoSizedUint8Array.js' -import type { PropDef, PropTree } from './defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import type { PropDef, PropTree } from '../../schema/defs/index.js' import { InferPayload } from './types.js' -import { getTypeDefs } from './defs/getTypeDefs.js' +import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' import { readUint32 } from '../../utils/uint8.js' export { getTypeDefs } @@ -316,7 +316,7 @@ export const schedule = (ctx: ModifyCtx) => { } } -// TODO implement single ModifyCmd +// TODO implement single ModifyCmd instead of both QueuedItem and ModifyItem export const modify = function ( ctx: ModifyCtx, serialize: S, diff --git a/src/db-client/modify/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts similarity index 87% rename from src/db-client/modify/defs/getTypeDefs.ts rename to src/schema/defs/getTypeDefs.ts index 9494fbad21..e966b8ef27 100644 --- a/src/db-client/modify/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -3,10 +3,9 @@ import { type SchemaProp, type SchemaProps, type SchemaType, -} from '../../../schema.js' -import { reorderProps } from '../../../schema/def/utils.js' -import { PropType } from '../../../zigTsExports.js' -import { defs, type PropDef, type PropDefClass, type TypeDef } from './index.js' +} from '../../schema.js' +import { PropType } from '../../zigTsExports.js' +import { defs, type PropDef, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 @@ -17,20 +16,26 @@ const mainSorter = (a, b) => { const propIndexOffset = (prop: PropDef) => { switch (prop.type) { + // We pack default on the beginning, for smallest possible mem case PropType.microBuffer: case PropType.vector: + // microbuffers first return 'default' in prop.prop ? -600 : 0 case PropType.string: case PropType.binary: case PropType.json: + // then strings return 'default' in prop.prop ? -500 : 0 + // then text case PropType.text: return 'default' in prop.prop ? -400 : 0 + // References go behind the defaults case PropType.references: case PropType.reference: return -300 + // Aliases and colVec go last case PropType.alias: - case PropType.aliases: + case PropType.aliases: // TODO remove ALIASES case PropType.colVec: return 300 default: @@ -57,6 +62,7 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { for (const key in props) { const prop = props[key] const path = [...pPath, key] + if (prop.type === 'object') { const branch = new Map() walk(prop.props, path, branch) @@ -84,7 +90,7 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { walk(props, [], typeDef.tree) - // -------- finish main -------- + // -------- sort and assign main -------- typeDef.main.sort(mainSorter) let start = 0 for (const prop of typeDef.main) { @@ -92,7 +98,7 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { start += prop.size } - // -------- finish separate --------- + // -------- sort and assign separate --------- typeDef.separate.sort(separateSorter) let propId = 1 for (const prop of typeDef.separate) { @@ -146,5 +152,6 @@ export const getTypeDefs = (schema: SchemaOut): Map => { // ----------- add to cache -------- cache.set(schema, typeDefs) + console.dir(typeDefs) return typeDefs } diff --git a/src/db-client/modify/defs/index.ts b/src/schema/defs/index.ts similarity index 72% rename from src/db-client/modify/defs/index.ts rename to src/schema/defs/index.ts index 3a87c535bd..bb265ac4da 100644 --- a/src/db-client/modify/defs/index.ts +++ b/src/schema/defs/index.ts @@ -1,19 +1,17 @@ -import type { SchemaProp } from '../../../schema.js' +import type { SchemaProp } from '../../schema.js' import type { LangCodeEnum, ModifyEnum, PropTypeEnum, -} from '../../../zigTsExports.js' -import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' -import * as fixed from './fixed.js' -import * as references from './references.js' - -import * as vars from './vars.js' +} from '../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import * as fixed from './props/fixed.js' +import * as references from './props/references.js' +import * as vars from './props/separate.js' export type PropTree = Map export type TypeDef = { - // mainSize: number id: number main: PropDef[] separate: PropDef[] diff --git a/src/db-client/modify/defs/base.ts b/src/schema/defs/props/base.ts similarity index 82% rename from src/db-client/modify/defs/base.ts rename to src/schema/defs/props/base.ts index 1e8c427dda..1e102805f2 100644 --- a/src/db-client/modify/defs/base.ts +++ b/src/schema/defs/props/base.ts @@ -4,8 +4,8 @@ import type { ModifyEnum, PropTypeEnum, } from '../../../zigTsExports.js' -import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' -import type { PropDef } from './index.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import type { PropDef } from '../index.js' export class BasePropDef implements PropDef { constructor(prop: SchemaProp, path: string[]) { @@ -22,8 +22,8 @@ export class BasePropDef implements PropDef { pushValue( buf: AutoSizedUint8Array, value: unknown, - op: ModifyEnum, lang: LangCodeEnum, + op: ModifyEnum, ): void { // To be implemented by subclasses } diff --git a/src/db-client/modify/defs/fixed.ts b/src/schema/defs/props/fixed.ts similarity index 70% rename from src/db-client/modify/defs/fixed.ts rename to src/schema/defs/props/fixed.ts index 0b39ba7d7f..43482a079d 100644 --- a/src/db-client/modify/defs/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -1,10 +1,10 @@ import type { EnumItem, SchemaEnum } from '../../../schema.js' import { convertToTimestamp } from '../../../utils/timestamp.js' import { PropType, type PropTypeEnum } from '../../../zigTsExports.js' -import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' -export const number = class extends BasePropDef { +export const number = class Number extends BasePropDef { override type: PropTypeEnum = PropType.number override size = 8 override pushValue(buf: AutoSizedUint8Array, value: number) { @@ -12,14 +12,14 @@ export const number = class extends BasePropDef { } } -export const timestamp = class extends number { +export const timestamp = class Timestamp extends number { override type = PropType.timestamp override pushValue(buf: AutoSizedUint8Array, value: number | string) { buf.pushI64(convertToTimestamp(value)) } } -export const uint8 = class extends BasePropDef { +export const uint8 = class Uint8 extends BasePropDef { override type: PropTypeEnum = PropType.uint8 override size = 1 override pushValue(buf: AutoSizedUint8Array, value: number): void { @@ -27,11 +27,11 @@ export const uint8 = class extends BasePropDef { } } -export const int8 = class extends uint8 { +export const int8 = class Int8 extends uint8 { override type = PropType.int8 } -export const uint16 = class extends BasePropDef { +export const uint16 = class Uint16 extends BasePropDef { override type: PropTypeEnum = PropType.uint16 override size = 2 override pushValue(buf: AutoSizedUint8Array, value: number): void { @@ -39,11 +39,11 @@ export const uint16 = class extends BasePropDef { } } -export const int16 = class extends uint16 { +export const int16 = class Int16 extends uint16 { override type = PropType.int16 } -export const uint32 = class extends BasePropDef { +export const uint32 = class Uint32 extends BasePropDef { override type: PropTypeEnum = PropType.uint32 override size = 4 override pushValue(buf: AutoSizedUint8Array, value: number): void { @@ -51,11 +51,11 @@ export const uint32 = class extends BasePropDef { } } -export const int32 = class extends uint32 { +export const int32 = class Int32 extends uint32 { override type = PropType.int32 } -export const enum_ = class extends uint8 { +export const enum_ = class Enum extends uint8 { constructor(prop: SchemaEnum, path: string[]) { super(prop, path) prop.enum.forEach((val, i) => { @@ -73,18 +73,10 @@ export const enum_ = class extends uint8 { } } -export const boolean = class extends BasePropDef { +export const boolean = class Boolean extends BasePropDef { override type = PropType.boolean override size = 1 override pushValue(buf: AutoSizedUint8Array, value: boolean): void { buf.pushU8(value ? 1 : 0) } } - -// export const created = class extends number { -// override type = PropType.created -// } - -// export const updated = class extends number { -// override type = PropType.updated -// } diff --git a/src/db-client/modify/defs/references.ts b/src/schema/defs/props/references.ts similarity index 95% rename from src/db-client/modify/defs/references.ts rename to src/schema/defs/props/references.ts index a226380905..4855089a9f 100644 --- a/src/db-client/modify/defs/references.ts +++ b/src/schema/defs/props/references.ts @@ -12,11 +12,15 @@ import { type ModifyEnum, type PropTypeEnum, } from '../../../zigTsExports.js' -import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { SchemaProp } from '../../../schema.js' import { BasePropDef } from './base.js' -import type { PropDef, TypeDef } from './index.js' -import { ModifyItem, QueuedItem, serializeProps } from '../index.js' +import type { PropDef, TypeDef } from '../index.js' +import { + ModifyItem, + QueuedItem, + serializeProps, +} from '../../../db-client/modify/index.js' type Edges = Record<`${string}`, unknown> | undefined @@ -199,7 +203,7 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { } } -export const references = class extends BasePropDef { +export const references = class References extends BasePropDef { override type: PropTypeEnum = PropType.references override pushValue( buf: AutoSizedUint8Array, @@ -228,13 +232,13 @@ export const references = class extends BasePropDef { } } -export const reference = class extends BasePropDef { +export const reference = class Reference extends BasePropDef { override type: PropTypeEnum = PropType.reference override pushValue( buf: AutoSizedUint8Array, value: any, - op: ModifyEnum, lang: LangCodeEnum, + op: ModifyEnum, ) { const id = getRealId(value) if (id) { diff --git a/src/db-client/modify/defs/vars.ts b/src/schema/defs/props/separate.ts similarity index 87% rename from src/db-client/modify/defs/vars.ts rename to src/schema/defs/props/separate.ts index db96a79e88..f49f721429 100644 --- a/src/db-client/modify/defs/vars.ts +++ b/src/schema/defs/props/separate.ts @@ -10,11 +10,10 @@ import { pushModifyCardinalityHeader, PropType, type LangCodeEnum, - type ModifyEnum, type PropTypeEnum, } from '../../../zigTsExports.js' -import { xxHash64 } from '../../xxHash64.js' -import type { AutoSizedUint8Array } from '../AutoSizedUint8Array.js' +import { xxHash64 } from '../../../db-client/xxHash64.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' export const string = class extends BasePropDef { @@ -26,23 +25,27 @@ export const string = class extends BasePropDef { this.size = prop.max * 2 + 1 } if (this.size) { + // make it a fixed string prop! + this.type = PropType.stringFixed + this.pushValue = this.pushFixedValue } } override type: PropTypeEnum = PropType.string override pushValue( buf: AutoSizedUint8Array, val: string, - op: ModifyEnum, lang: LangCodeEnum, ) { const normalized = val.normalize('NFKD') - // make header! + // TODO make header! + // TODO compression buf.pushU8(lang) buf.pushU8(NOT_COMPRESSED) const written = buf.pushString(normalized) const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushU32(crc) } + pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} } // TODO do it nice @@ -52,13 +55,8 @@ export const text = class extends string { export const json = class extends string { override type = PropType.json - override pushValue( - buf: AutoSizedUint8Array, - value: any, - op: ModifyEnum, - lang: LangCodeEnum, - ) { - super.pushValue(buf, JSON.stringify(value), op, lang) + override pushValue(buf: AutoSizedUint8Array, value: any, lang: LangCodeEnum) { + super.pushValue(buf, JSON.stringify(value), lang) } } diff --git a/src/db-client/modify/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts similarity index 99% rename from src/db-client/modify/AutoSizedUint8Array.ts rename to src/utils/AutoSizedUint8Array.ts index 67ad5de4cc..c2e02d93f2 100644 --- a/src/db-client/modify/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -1,10 +1,10 @@ -import native from '../../native.js' +import native from '../native.js' import { writeDoubleLE, writeFloatLE, writeUint64, writeInt64, -} from '../../utils/index.js' +} from './index.js' const ENCODER = new TextEncoder() diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 62cb9c5ae6..2fdd7db70a 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1,14 +1,22 @@ -import { - writeUint16, writeInt16, - writeUint32, writeInt32, - writeUint64, writeInt64, - writeFloatLE, writeDoubleLE, - readUint16, readInt16, - readUint32, readInt32, - readUint64, readInt64, - readFloatLE, readDoubleLE +import { + writeUint16, + writeInt16, + writeUint32, + writeInt32, + writeUint64, + writeInt64, + writeFloatLE, + writeDoubleLE, + readUint16, + readInt16, + readUint32, + readInt32, + readUint64, + readInt64, + readFloatLE, + readDoubleLE, } from './utils/index.js' -import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from './utils/AutoSizedUint8Array.js' export type TypeId = number @@ -32,7 +40,8 @@ export const BridgeResponseInverse = { flushQuery, flushModify */ -export type BridgeResponseEnum = (typeof BridgeResponse)[keyof typeof BridgeResponse] +export type BridgeResponseEnum = + (typeof BridgeResponse)[keyof typeof BridgeResponse] export const OpType = { id: 0, @@ -234,7 +243,7 @@ export const packModifyHeader = (obj: ModifyHeader): bigint => { export const unpackModifyHeader = (val: bigint): ModifyHeader => { return { opId: Number((val >> 0n) & 4294967295n), - opType: (Number((val >> 32n) & 255n)) as OpTypeEnum, + opType: Number((val >> 32n) & 255n) as OpTypeEnum, schema: Number((val >> 40n) & 18446744073709551615n), count: Number((val >> 104n) & 4294967295n), } @@ -277,7 +286,7 @@ export const readModifyHeader = ( ): ModifyHeader => { const value: ModifyHeader = { opId: readUint32(buf, offset), - opType: (buf[offset + 4]) as OpTypeEnum, + opType: buf[offset + 4] as OpTypeEnum, schema: readUint64(buf, offset + 5), count: readUint32(buf, offset + 13), } @@ -285,10 +294,10 @@ export const readModifyHeader = ( } export const readModifyHeaderProps = { - opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - opType: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as OpTypeEnum, - schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), - count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), + opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + opType: (buf: Uint8Array, offset: number) => buf[offset + 4] as OpTypeEnum, + schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), + count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), } export const createModifyHeader = (header: ModifyHeader): Uint8Array => { @@ -331,7 +340,7 @@ export const packModifyUpdateHeader = (obj: ModifyUpdateHeader): bigint => { export const unpackModifyUpdateHeader = (val: bigint): ModifyUpdateHeader => { return { - op: (Number((val >> 0n) & 255n)) as ModifyEnum, + op: Number((val >> 0n) & 255n) as ModifyEnum, type: Number((val >> 8n) & 255n), id: Number((val >> 16n) & 4294967295n), size: Number((val >> 48n) & 4294967295n), @@ -374,7 +383,7 @@ export const readModifyUpdateHeader = ( offset: number, ): ModifyUpdateHeader => { const value: ModifyUpdateHeader = { - op: (buf[offset]) as ModifyEnum, + op: buf[offset] as ModifyEnum, type: buf[offset + 1], id: readUint32(buf, offset + 2), size: readUint32(buf, offset + 6), @@ -383,13 +392,15 @@ export const readModifyUpdateHeader = ( } export const readModifyUpdateHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), } -export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { +export const createModifyUpdateHeader = ( + header: ModifyUpdateHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyUpdateHeaderByteSize) writeModifyUpdateHeader(buffer, header, 0) return buffer @@ -427,7 +438,7 @@ export const packModifyDeleteHeader = (obj: ModifyDeleteHeader): bigint => { export const unpackModifyDeleteHeader = (val: bigint): ModifyDeleteHeader => { return { - op: (Number((val >> 0n) & 255n)) as ModifyEnum, + op: Number((val >> 0n) & 255n) as ModifyEnum, type: Number((val >> 8n) & 255n), id: Number((val >> 16n) & 4294967295n), } @@ -464,7 +475,7 @@ export const readModifyDeleteHeader = ( offset: number, ): ModifyDeleteHeader => { const value: ModifyDeleteHeader = { - op: (buf[offset]) as ModifyEnum, + op: buf[offset] as ModifyEnum, type: buf[offset + 1], id: readUint32(buf, offset + 2), } @@ -472,12 +483,14 @@ export const readModifyDeleteHeader = ( } export const readModifyDeleteHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { +export const createModifyDeleteHeader = ( + header: ModifyDeleteHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyDeleteHeaderByteSize) writeModifyDeleteHeader(buffer, header, 0) return buffer @@ -514,7 +527,7 @@ export const packModifyCreateHeader = (obj: ModifyCreateHeader): bigint => { export const unpackModifyCreateHeader = (val: bigint): ModifyCreateHeader => { return { - op: (Number((val >> 0n) & 255n)) as ModifyEnum, + op: Number((val >> 0n) & 255n) as ModifyEnum, type: Number((val >> 8n) & 255n), size: Number((val >> 16n) & 4294967295n), } @@ -551,7 +564,7 @@ export const readModifyCreateHeader = ( offset: number, ): ModifyCreateHeader => { const value: ModifyCreateHeader = { - op: (buf[offset]) as ModifyEnum, + op: buf[offset] as ModifyEnum, type: buf[offset + 1], size: readUint32(buf, offset + 2), } @@ -559,12 +572,14 @@ export const readModifyCreateHeader = ( } export const readModifyCreateHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { +export const createModifyCreateHeader = ( + header: ModifyCreateHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyCreateHeaderByteSize) writeModifyCreateHeader(buffer, header, 0) return buffer @@ -646,12 +661,14 @@ export const readModifyMainHeader = ( } export const readModifyMainHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + id: (buf: Uint8Array, offset: number) => buf[offset], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { +export const createModifyMainHeader = ( + header: ModifyMainHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyMainHeaderByteSize) writeModifyMainHeader(buffer, header, 0) return buffer @@ -689,7 +706,7 @@ export const packModifyPropHeader = (obj: ModifyPropHeader): bigint => { export const unpackModifyPropHeader = (val: bigint): ModifyPropHeader => { return { id: Number((val >> 0n) & 255n), - type: (Number((val >> 8n) & 255n)) as PropTypeEnum, + type: Number((val >> 8n) & 255n) as PropTypeEnum, size: Number((val >> 16n) & 4294967295n), } } @@ -726,19 +743,21 @@ export const readModifyPropHeader = ( ): ModifyPropHeader => { const value: ModifyPropHeader = { id: buf[offset], - type: (buf[offset + 1]) as PropTypeEnum, + type: buf[offset + 1] as PropTypeEnum, size: readUint32(buf, offset + 2), } return value } export const readModifyPropHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyPropHeader = (header: ModifyPropHeader): Uint8Array => { +export const createModifyPropHeader = ( + header: ModifyPropHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyPropHeaderByteSize) writeModifyPropHeader(buffer, header, 0) return buffer @@ -781,7 +800,8 @@ export const ModifyReferencesInverse = { delIds, delTmpIds */ -export type ModifyReferencesEnum = (typeof ModifyReferences)[keyof typeof ModifyReferences] +export type ModifyReferencesEnum = + (typeof ModifyReferences)[keyof typeof ModifyReferences] export type ModifyReferencesHeader = { op: ModifyReferencesEnum @@ -792,16 +812,20 @@ export const ModifyReferencesHeaderByteSize = 5 export const ModifyReferencesHeaderAlignOf = 8 -export const packModifyReferencesHeader = (obj: ModifyReferencesHeader): bigint => { +export const packModifyReferencesHeader = ( + obj: ModifyReferencesHeader, +): bigint => { let val = 0n val |= (BigInt(obj.op) & 255n) << 0n val |= (BigInt(obj.size) & 4294967295n) << 8n return val } -export const unpackModifyReferencesHeader = (val: bigint): ModifyReferencesHeader => { +export const unpackModifyReferencesHeader = ( + val: bigint, +): ModifyReferencesHeader => { return { - op: (Number((val >> 0n) & 255n)) as ModifyReferencesEnum, + op: Number((val >> 0n) & 255n) as ModifyReferencesEnum, size: Number((val >> 8n) & 4294967295n), } } @@ -832,18 +856,20 @@ export const readModifyReferencesHeader = ( offset: number, ): ModifyReferencesHeader => { const value: ModifyReferencesHeader = { - op: (buf[offset]) as ModifyReferencesEnum, + op: buf[offset] as ModifyReferencesEnum, size: readUint32(buf, offset + 1), } return value } export const readModifyReferencesHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyReferencesEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyReferencesEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } -export const createModifyReferencesHeader = (header: ModifyReferencesHeader): Uint8Array => { +export const createModifyReferencesHeader = ( + header: ModifyReferencesHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesHeaderByteSize) writeModifyReferencesHeader(buffer, header, 0) return buffer @@ -871,7 +897,9 @@ export const ModifyReferencesMetaHeaderByteSize = 13 export const ModifyReferencesMetaHeaderAlignOf = 16 -export const packModifyReferencesMetaHeader = (obj: ModifyReferencesMetaHeader): bigint => { +export const packModifyReferencesMetaHeader = ( + obj: ModifyReferencesMetaHeader, +): bigint => { let val = 0n val |= (BigInt(obj.id) & 4294967295n) << 0n val |= ((obj.isTmp ? 1n : 0n) & 1n) << 32n @@ -881,7 +909,9 @@ export const packModifyReferencesMetaHeader = (obj: ModifyReferencesMetaHeader): return val } -export const unpackModifyReferencesMetaHeader = (val: bigint): ModifyReferencesMetaHeader => { +export const unpackModifyReferencesMetaHeader = ( + val: bigint, +): ModifyReferencesMetaHeader => { return { id: Number((val >> 0n) & 4294967295n), isTmp: ((val >> 32n) & 1n) === 1n, @@ -934,8 +964,8 @@ export const readModifyReferencesMetaHeader = ( ): ModifyReferencesMetaHeader => { const value: ModifyReferencesMetaHeader = { id: readUint32(buf, offset), - isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, - withIndex: (((buf[offset + 4] >>> 1) & 1)) === 1, + isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, + withIndex: ((buf[offset + 4] >>> 1) & 1) === 1, index: readUint32(buf, offset + 5), size: readUint32(buf, offset + 9), } @@ -943,14 +973,18 @@ export const readModifyReferencesMetaHeader = ( } export const readModifyReferencesMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, - index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 0) & 1) === 1, + withIndex: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 1) & 1) === 1, + index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), } -export const createModifyReferencesMetaHeader = (header: ModifyReferencesMetaHeader): Uint8Array => { +export const createModifyReferencesMetaHeader = ( + header: ModifyReferencesMetaHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesMetaHeaderByteSize) writeModifyReferencesMetaHeader(buffer, header, 0) return buffer @@ -981,7 +1015,9 @@ export const ModifyReferenceMetaHeaderByteSize = 9 export const ModifyReferenceMetaHeaderAlignOf = 16 -export const packModifyReferenceMetaHeader = (obj: ModifyReferenceMetaHeader): bigint => { +export const packModifyReferenceMetaHeader = ( + obj: ModifyReferenceMetaHeader, +): bigint => { let val = 0n val |= (BigInt(obj.id) & 4294967295n) << 0n val |= ((obj.isTmp ? 1n : 0n) & 1n) << 32n @@ -989,7 +1025,9 @@ export const packModifyReferenceMetaHeader = (obj: ModifyReferenceMetaHeader): b return val } -export const unpackModifyReferenceMetaHeader = (val: bigint): ModifyReferenceMetaHeader => { +export const unpackModifyReferenceMetaHeader = ( + val: bigint, +): ModifyReferenceMetaHeader => { return { id: Number((val >> 0n) & 4294967295n), isTmp: ((val >> 32n) & 1n) === 1n, @@ -1031,19 +1069,22 @@ export const readModifyReferenceMetaHeader = ( ): ModifyReferenceMetaHeader => { const value: ModifyReferenceMetaHeader = { id: readUint32(buf, offset), - isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, size: readUint32(buf, offset + 5), } return value } export const readModifyReferenceMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 0) & 1) === 1, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), } -export const createModifyReferenceMetaHeader = (header: ModifyReferenceMetaHeader): Uint8Array => { +export const createModifyReferenceMetaHeader = ( + header: ModifyReferenceMetaHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyReferenceMetaHeaderByteSize) writeModifyReferenceMetaHeader(buffer, header, 0) return buffer @@ -1071,14 +1112,18 @@ export const ModifyCardinalityHeaderByteSize = 2 export const ModifyCardinalityHeaderAlignOf = 2 -export const packModifyCardinalityHeader = (obj: ModifyCardinalityHeader): bigint => { +export const packModifyCardinalityHeader = ( + obj: ModifyCardinalityHeader, +): bigint => { let val = 0n val |= ((obj.sparse ? 1n : 0n) & 1n) << 0n val |= (BigInt(obj.precision) & 255n) << 8n return val } -export const unpackModifyCardinalityHeader = (val: bigint): ModifyCardinalityHeader => { +export const unpackModifyCardinalityHeader = ( + val: bigint, +): ModifyCardinalityHeader => { return { sparse: ((val >> 0n) & 1n) === 1n, precision: Number((val >> 8n) & 255n), @@ -1113,18 +1158,20 @@ export const readModifyCardinalityHeader = ( offset: number, ): ModifyCardinalityHeader => { const value: ModifyCardinalityHeader = { - sparse: (((buf[offset] >>> 0) & 1)) === 1, + sparse: ((buf[offset] >>> 0) & 1) === 1, precision: buf[offset + 1], } return value } export const readModifyCardinalityHeaderProps = { - sparse: (buf: Uint8Array, offset: number) => (((buf[offset] >>> 0) & 1)) === 1, - precision: (buf: Uint8Array, offset: number) => buf[offset + 1], + sparse: (buf: Uint8Array, offset: number) => ((buf[offset] >>> 0) & 1) === 1, + precision: (buf: Uint8Array, offset: number) => buf[offset + 1], } -export const createModifyCardinalityHeader = (header: ModifyCardinalityHeader): Uint8Array => { +export const createModifyCardinalityHeader = ( + header: ModifyCardinalityHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyCardinalityHeaderByteSize) writeModifyCardinalityHeader(buffer, header, 0) return buffer @@ -1161,7 +1208,7 @@ export const packModifyResultItem = (obj: ModifyResultItem): bigint => { export const unpackModifyResultItem = (val: bigint): ModifyResultItem => { return { id: Number((val >> 0n) & 4294967295n), - err: (Number((val >> 32n) & 255n)) as ModifyErrorEnum, + err: Number((val >> 32n) & 255n) as ModifyErrorEnum, } } @@ -1192,17 +1239,19 @@ export const readModifyResultItem = ( ): ModifyResultItem => { const value: ModifyResultItem = { id: readUint32(buf, offset), - err: (buf[offset + 4]) as ModifyErrorEnum, + err: buf[offset + 4] as ModifyErrorEnum, } return value } export const readModifyResultItemProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - err: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyErrorEnum, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + err: (buf: Uint8Array, offset: number) => buf[offset + 4] as ModifyErrorEnum, } -export const createModifyResultItem = (header: ModifyResultItem): Uint8Array => { +export const createModifyResultItem = ( + header: ModifyResultItem, +): Uint8Array => { const buffer = new Uint8Array(ModifyResultItemByteSize) writeModifyResultItem(buffer, header, 0) return buffer @@ -1404,7 +1453,8 @@ export const ReferencesSelectInverse = { any, all */ -export type ReferencesSelectEnum = (typeof ReferencesSelect)[keyof typeof ReferencesSelect] +export type ReferencesSelectEnum = + (typeof ReferencesSelect)[keyof typeof ReferencesSelect] export const RefEdgeOp = { noEdgeNoIndexRealId: 0, @@ -2105,12 +2155,12 @@ export const packSortHeader = (obj: SortHeader): bigint => { export const unpackSortHeader = (val: bigint): SortHeader => { return { - order: (Number((val >> 0n) & 255n)) as OrderEnum, + order: Number((val >> 0n) & 255n) as OrderEnum, prop: Number((val >> 8n) & 255n), - propType: (Number((val >> 16n) & 255n)) as PropTypeEnum, + propType: Number((val >> 16n) & 255n) as PropTypeEnum, start: Number((val >> 24n) & 65535n), len: Number((val >> 40n) & 65535n), - lang: (Number((val >> 56n) & 255n)) as LangCodeEnum, + lang: Number((val >> 56n) & 255n) as LangCodeEnum, edgeType: Number((val >> 64n) & 65535n), } } @@ -2161,30 +2211,28 @@ export const writeSortHeaderProps = { }, } -export const readSortHeader = ( - buf: Uint8Array, - offset: number, -): SortHeader => { +export const readSortHeader = (buf: Uint8Array, offset: number): SortHeader => { const value: SortHeader = { - order: (buf[offset]) as OrderEnum, + order: buf[offset] as OrderEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, start: readUint16(buf, offset + 3), len: readUint16(buf, offset + 5), - lang: (buf[offset + 7]) as LangCodeEnum, + lang: buf[offset + 7] as LangCodeEnum, edgeType: readUint16(buf, offset + 8), } return value } export const readSortHeaderProps = { - order: (buf: Uint8Array, offset: number) => (buf[offset]) as OrderEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - lang: (buf: Uint8Array, offset: number) => (buf[offset + 7]) as LangCodeEnum, - edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + order: (buf: Uint8Array, offset: number) => buf[offset] as OrderEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + lang: (buf: Uint8Array, offset: number) => buf[offset + 7] as LangCodeEnum, + edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } export const createSortHeader = (header: SortHeader): Uint8Array => { @@ -2305,7 +2353,8 @@ export const QueryIteratorTypeInverse = { vec, vecFilter */ -export type QueryIteratorTypeEnum = (typeof QueryIteratorType)[keyof typeof QueryIteratorType] +export type QueryIteratorTypeEnum = + (typeof QueryIteratorType)[keyof typeof QueryIteratorType] export const QueryType = { id: 0, @@ -2413,9 +2462,9 @@ export const packIncludeHeader = (obj: IncludeHeader): bigint => { export const unpackIncludeHeader = (val: bigint): IncludeHeader => { return { - op: (Number((val >> 0n) & 255n)) as IncludeOpEnum, + op: Number((val >> 0n) & 255n) as IncludeOpEnum, prop: Number((val >> 8n) & 255n), - propType: (Number((val >> 16n) & 255n)) as PropTypeEnum, + propType: Number((val >> 16n) & 255n) as PropTypeEnum, } } @@ -2450,17 +2499,18 @@ export const readIncludeHeader = ( offset: number, ): IncludeHeader => { const value: IncludeHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, } return value } export const readIncludeHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, } export const createIncludeHeader = (header: IncludeHeader): Uint8Array => { @@ -2500,9 +2550,9 @@ export const packIncludeMetaHeader = (obj: IncludeMetaHeader): bigint => { export const unpackIncludeMetaHeader = (val: bigint): IncludeMetaHeader => { return { - op: (Number((val >> 0n) & 255n)) as IncludeOpEnum, + op: Number((val >> 0n) & 255n) as IncludeOpEnum, prop: Number((val >> 8n) & 255n), - propType: (Number((val >> 16n) & 255n)) as PropTypeEnum, + propType: Number((val >> 16n) & 255n) as PropTypeEnum, } } @@ -2537,20 +2587,23 @@ export const readIncludeMetaHeader = ( offset: number, ): IncludeMetaHeader => { const value: IncludeMetaHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, } return value } export const readIncludeMetaHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, } -export const createIncludeMetaHeader = (header: IncludeMetaHeader): Uint8Array => { +export const createIncludeMetaHeader = ( + header: IncludeMetaHeader, +): Uint8Array => { const buffer = new Uint8Array(IncludeMetaHeaderByteSize) writeIncludeMetaHeader(buffer, header, 0) return buffer @@ -2587,11 +2640,13 @@ export const packIncludePartialHeader = (obj: IncludePartialHeader): bigint => { return val } -export const unpackIncludePartialHeader = (val: bigint): IncludePartialHeader => { +export const unpackIncludePartialHeader = ( + val: bigint, +): IncludePartialHeader => { return { - op: (Number((val >> 0n) & 255n)) as IncludeOpEnum, + op: Number((val >> 0n) & 255n) as IncludeOpEnum, prop: Number((val >> 8n) & 255n), - propType: (Number((val >> 16n) & 255n)) as PropTypeEnum, + propType: Number((val >> 16n) & 255n) as PropTypeEnum, amount: Number((val >> 24n) & 65535n), } } @@ -2632,22 +2687,25 @@ export const readIncludePartialHeader = ( offset: number, ): IncludePartialHeader => { const value: IncludePartialHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, amount: readUint16(buf, offset + 3), } return value } export const readIncludePartialHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, - amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, + amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createIncludePartialHeader = (header: IncludePartialHeader): Uint8Array => { +export const createIncludePartialHeader = ( + header: IncludePartialHeader, +): Uint8Array => { const buffer = new Uint8Array(IncludePartialHeaderByteSize) writeIncludePartialHeader(buffer, header, 0) return buffer @@ -2721,11 +2779,13 @@ export const readIncludePartialProp = ( } export const readIncludePartialPropProps = { - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), } -export const createIncludePartialProp = (header: IncludePartialProp): Uint8Array => { +export const createIncludePartialProp = ( + header: IncludePartialProp, +): Uint8Array => { const buffer = new Uint8Array(IncludePartialPropByteSize) writeIncludePartialProp(buffer, header, 0) return buffer @@ -2769,7 +2829,7 @@ export const unpackIncludeOpts = (val: bigint): IncludeOpts => { isChars: ((val >> 32n) & 1n) === 1n, hasOpts: ((val >> 33n) & 1n) === 1n, langFallbackSize: Number((val >> 40n) & 255n), - lang: (Number((val >> 48n) & 255n)) as LangCodeEnum, + lang: Number((val >> 48n) & 255n) as LangCodeEnum, } } @@ -2816,20 +2876,22 @@ export const readIncludeOpts = ( ): IncludeOpts => { const value: IncludeOpts = { end: readUint32(buf, offset), - isChars: (((buf[offset + 4] >>> 0) & 1)) === 1, - hasOpts: (((buf[offset + 4] >>> 1) & 1)) === 1, + isChars: ((buf[offset + 4] >>> 0) & 1) === 1, + hasOpts: ((buf[offset + 4] >>> 1) & 1) === 1, langFallbackSize: buf[offset + 5], - lang: (buf[offset + 6]) as LangCodeEnum, + lang: buf[offset + 6] as LangCodeEnum, } return value } export const readIncludeOptsProps = { - end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isChars: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - hasOpts: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, - langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], - lang: (buf: Uint8Array, offset: number) => (buf[offset + 6]) as LangCodeEnum, + end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isChars: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 0) & 1) === 1, + hasOpts: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 1) & 1) === 1, + langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], + lang: (buf: Uint8Array, offset: number) => buf[offset + 6] as LangCodeEnum, } export const createIncludeOpts = (header: IncludeOpts): Uint8Array => { @@ -2909,8 +2971,8 @@ export const readIncludeResponse = ( } export const readIncludeResponseProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + prop: (buf: Uint8Array, offset: number) => buf[offset], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } export const createIncludeResponse = (header: IncludeResponse): Uint8Array => { @@ -2955,9 +3017,9 @@ export const packIncludeResponseMeta = (obj: IncludeResponseMeta): bigint => { export const unpackIncludeResponseMeta = (val: bigint): IncludeResponseMeta => { return { - op: (Number((val >> 0n) & 255n)) as ReadOpEnum, + op: Number((val >> 0n) & 255n) as ReadOpEnum, prop: Number((val >> 8n) & 255n), - lang: (Number((val >> 16n) & 255n)) as LangCodeEnum, + lang: Number((val >> 16n) & 255n) as LangCodeEnum, compressed: ((val >> 24n) & 1n) === 1n, crc32: Number((val >> 32n) & 4294967295n), size: Number((val >> 64n) & 4294967295n), @@ -3012,10 +3074,10 @@ export const readIncludeResponseMeta = ( offset: number, ): IncludeResponseMeta => { const value: IncludeResponseMeta = { - op: (buf[offset]) as ReadOpEnum, + op: buf[offset] as ReadOpEnum, prop: buf[offset + 1], - lang: (buf[offset + 2]) as LangCodeEnum, - compressed: (((buf[offset + 3] >>> 0) & 1)) === 1, + lang: buf[offset + 2] as LangCodeEnum, + compressed: ((buf[offset + 3] >>> 0) & 1) === 1, crc32: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -3023,15 +3085,18 @@ export const readIncludeResponseMeta = ( } export const readIncludeResponseMetaProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ReadOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - lang: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as LangCodeEnum, - compressed: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, - crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => buf[offset] as ReadOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + lang: (buf: Uint8Array, offset: number) => buf[offset + 2] as LangCodeEnum, + compressed: (buf: Uint8Array, offset: number) => + ((buf[offset + 3] >>> 0) & 1) === 1, + crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createIncludeResponseMeta = (header: IncludeResponseMeta): Uint8Array => { +export const createIncludeResponseMeta = ( + header: IncludeResponseMeta, +): Uint8Array => { const buffer = new Uint8Array(IncludeResponseMetaByteSize) writeIncludeResponseMeta(buffer, header, 0) return buffer @@ -3075,8 +3140,8 @@ export const packSubscriptionHeader = (obj: SubscriptionHeader): bigint => { export const unpackSubscriptionHeader = (val: bigint): SubscriptionHeader => { return { - op: (Number((val >> 0n) & 255n)) as OpTypeEnum, - typeId: (Number((val >> 8n) & 65535n)) as TypeId, + op: Number((val >> 0n) & 255n) as OpTypeEnum, + typeId: Number((val >> 8n) & 65535n) as TypeId, fieldsLen: Number((val >> 24n) & 255n), partialLen: Number((val >> 32n) & 255n), } @@ -3118,8 +3183,8 @@ export const readSubscriptionHeader = ( offset: number, ): SubscriptionHeader => { const value: SubscriptionHeader = { - op: (buf[offset]) as OpTypeEnum, - typeId: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as OpTypeEnum, + typeId: readUint16(buf, offset + 1) as TypeId, fieldsLen: buf[offset + 3], partialLen: buf[offset + 4], } @@ -3127,13 +3192,16 @@ export const readSubscriptionHeader = ( } export const readSubscriptionHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as OpTypeEnum, - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], - partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], + op: (buf: Uint8Array, offset: number) => buf[offset] as OpTypeEnum, + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], + partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], } -export const createSubscriptionHeader = (header: SubscriptionHeader): Uint8Array => { +export const createSubscriptionHeader = ( + header: SubscriptionHeader, +): Uint8Array => { const buffer = new Uint8Array(SubscriptionHeaderByteSize) writeSubscriptionHeader(buffer, header, 0) return buffer @@ -3193,10 +3261,10 @@ export const packQueryHeader = (obj: QueryHeader): bigint => { export const unpackQueryHeader = (val: bigint): QueryHeader => { return { - op: (Number((val >> 0n) & 255n)) as QueryTypeEnum, + op: Number((val >> 0n) & 255n) as QueryTypeEnum, prop: Number((val >> 8n) & 255n), - typeId: (Number((val >> 16n) & 65535n)) as TypeId, - edgeTypeId: (Number((val >> 32n) & 65535n)) as TypeId, + typeId: Number((val >> 16n) & 65535n) as TypeId, + edgeTypeId: Number((val >> 32n) & 65535n) as TypeId, offset: Number((val >> 48n) & 4294967295n), limit: Number((val >> 80n) & 4294967295n), filterSize: Number((val >> 112n) & 65535n), @@ -3204,7 +3272,7 @@ export const unpackQueryHeader = (val: bigint): QueryHeader => { edgeSize: Number((val >> 144n) & 65535n), edgeFilterSize: Number((val >> 160n) & 65535n), includeSize: Number((val >> 176n) & 65535n), - iteratorType: (Number((val >> 192n) & 255n)) as QueryIteratorTypeEnum, + iteratorType: Number((val >> 192n) & 255n) as QueryIteratorTypeEnum, size: Number((val >> 200n) & 65535n), sort: ((val >> 216n) & 1n) === 1n, } @@ -3282,7 +3350,11 @@ export const writeQueryHeaderProps = { includeSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 22) }, - iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { + iteratorType: ( + buf: Uint8Array, + value: QueryIteratorTypeEnum, + offset: number, + ) => { buf[offset + 24] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3298,10 +3370,10 @@ export const readQueryHeader = ( offset: number, ): QueryHeader => { const value: QueryHeader = { - op: (buf[offset]) as QueryTypeEnum, + op: buf[offset] as QueryTypeEnum, prop: buf[offset + 1], - typeId: (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, + typeId: readUint16(buf, offset + 2) as TypeId, + edgeTypeId: readUint16(buf, offset + 4) as TypeId, offset: readUint32(buf, offset + 6), limit: readUint32(buf, offset + 10), filterSize: readUint16(buf, offset + 14), @@ -3309,28 +3381,34 @@ export const readQueryHeader = ( edgeSize: readUint16(buf, offset + 18), edgeFilterSize: readUint16(buf, offset + 20), includeSize: readUint16(buf, offset + 22), - iteratorType: (buf[offset + 24]) as QueryIteratorTypeEnum, + iteratorType: buf[offset + 24] as QueryIteratorTypeEnum, size: readUint16(buf, offset + 25), - sort: (((buf[offset + 27] >>> 0) & 1)) === 1, + sort: ((buf[offset + 27] >>> 0) & 1) === 1, } return value } export const readQueryHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), - edgeFilterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 20), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 22), - iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 24]) as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), - sort: (buf: Uint8Array, offset: number) => (((buf[offset + 27] >>> 0) & 1)) === 1, + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 4) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), + edgeFilterSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 20), + includeSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 22), + iteratorType: (buf: Uint8Array, offset: number) => + buf[offset + 24] as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), + sort: (buf: Uint8Array, offset: number) => + ((buf[offset + 27] >>> 0) & 1) === 1, } export const createQueryHeader = (header: QueryHeader): Uint8Array => { @@ -3391,8 +3469,8 @@ export const packQueryHeaderSingle = (obj: QueryHeaderSingle): bigint => { export const unpackQueryHeaderSingle = (val: bigint): QueryHeaderSingle => { return { - op: (Number((val >> 0n) & 255n)) as QueryTypeEnum, - typeId: (Number((val >> 8n) & 65535n)) as TypeId, + op: Number((val >> 0n) & 255n) as QueryTypeEnum, + typeId: Number((val >> 8n) & 65535n) as TypeId, prop: Number((val >> 24n) & 255n), id: Number((val >> 32n) & 4294967295n), filterSize: Number((val >> 64n) & 65535n), @@ -3452,8 +3530,8 @@ export const readQueryHeaderSingle = ( offset: number, ): QueryHeaderSingle => { const value: QueryHeaderSingle = { - op: (buf[offset]) as QueryTypeEnum, - typeId: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as QueryTypeEnum, + typeId: readUint16(buf, offset + 1) as TypeId, prop: buf[offset + 3], id: readUint32(buf, offset + 4), filterSize: readUint16(buf, offset + 8), @@ -3464,16 +3542,20 @@ export const readQueryHeaderSingle = ( } export const readQueryHeaderSingleProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - prop: (buf: Uint8Array, offset: number) => buf[offset + 3], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), - aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), -} - -export const createQueryHeaderSingle = (header: QueryHeaderSingle): Uint8Array => { + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + prop: (buf: Uint8Array, offset: number) => buf[offset + 3], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + includeSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 10), + aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), +} + +export const createQueryHeaderSingle = ( + header: QueryHeaderSingle, +): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleByteSize) writeQueryHeaderSingle(buffer, header, 0) return buffer @@ -3507,7 +3589,9 @@ export const QueryHeaderSingleReferenceByteSize = 10 export const QueryHeaderSingleReferenceAlignOf = 16 -export const packQueryHeaderSingleReference = (obj: QueryHeaderSingleReference): bigint => { +export const packQueryHeaderSingleReference = ( + obj: QueryHeaderSingleReference, +): bigint => { let val = 0n val |= (BigInt(obj.op) & 255n) << 0n val |= (BigInt(obj.prop) & 255n) << 8n @@ -3518,12 +3602,14 @@ export const packQueryHeaderSingleReference = (obj: QueryHeaderSingleReference): return val } -export const unpackQueryHeaderSingleReference = (val: bigint): QueryHeaderSingleReference => { +export const unpackQueryHeaderSingleReference = ( + val: bigint, +): QueryHeaderSingleReference => { return { - op: (Number((val >> 0n) & 255n)) as QueryTypeEnum, + op: Number((val >> 0n) & 255n) as QueryTypeEnum, prop: Number((val >> 8n) & 255n), - typeId: (Number((val >> 16n) & 65535n)) as TypeId, - edgeTypeId: (Number((val >> 32n) & 65535n)) as TypeId, + typeId: Number((val >> 16n) & 65535n) as TypeId, + edgeTypeId: Number((val >> 32n) & 65535n) as TypeId, edgeSize: Number((val >> 48n) & 65535n), includeSize: Number((val >> 64n) & 65535n), } @@ -3575,10 +3661,10 @@ export const readQueryHeaderSingleReference = ( offset: number, ): QueryHeaderSingleReference => { const value: QueryHeaderSingleReference = { - op: (buf[offset]) as QueryTypeEnum, + op: buf[offset] as QueryTypeEnum, prop: buf[offset + 1], - typeId: (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, + typeId: readUint16(buf, offset + 2) as TypeId, + edgeTypeId: readUint16(buf, offset + 4) as TypeId, edgeSize: readUint16(buf, offset + 6), includeSize: readUint16(buf, offset + 8), } @@ -3586,15 +3672,19 @@ export const readQueryHeaderSingleReference = ( } export const readQueryHeaderSingleReferenceProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 4) as TypeId, + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } -export const createQueryHeaderSingleReference = (header: QueryHeaderSingleReference): Uint8Array => { +export const createQueryHeaderSingleReference = ( + header: QueryHeaderSingleReference, +): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleReferenceByteSize) writeQueryHeaderSingleReference(buffer, header, 0) return buffer @@ -3646,7 +3736,8 @@ export const VectorBaseTypeInverse = { float32, float64 */ -export type VectorBaseTypeEnum = (typeof VectorBaseType)[keyof typeof VectorBaseType] +export type VectorBaseTypeEnum = + (typeof VectorBaseType)[keyof typeof VectorBaseType] export type AggHeader = { op: QueryTypeEnum @@ -3686,12 +3777,12 @@ export const packAggHeader = (obj: AggHeader): bigint => { export const unpackAggHeader = (val: bigint): AggHeader => { return { - op: (Number((val >> 0n) & 255n)) as QueryTypeEnum, - typeId: (Number((val >> 8n) & 65535n)) as TypeId, + op: Number((val >> 0n) & 255n) as QueryTypeEnum, + typeId: Number((val >> 8n) & 65535n) as TypeId, offset: Number((val >> 24n) & 4294967295n), limit: Number((val >> 56n) & 4294967295n), filterSize: Number((val >> 88n) & 65535n), - iteratorType: (Number((val >> 104n) & 255n)) as QueryIteratorTypeEnum, + iteratorType: Number((val >> 104n) & 255n) as QueryIteratorTypeEnum, size: Number((val >> 112n) & 65535n), resultsSize: Number((val >> 128n) & 65535n), accumulatorSize: Number((val >> 144n) & 65535n), @@ -3749,7 +3840,11 @@ export const writeAggHeaderProps = { filterSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 11) }, - iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { + iteratorType: ( + buf: Uint8Array, + value: QueryIteratorTypeEnum, + offset: number, + ) => { buf[offset + 13] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3772,40 +3867,44 @@ export const writeAggHeaderProps = { }, } -export const readAggHeader = ( - buf: Uint8Array, - offset: number, -): AggHeader => { +export const readAggHeader = (buf: Uint8Array, offset: number): AggHeader => { const value: AggHeader = { - op: (buf[offset]) as QueryTypeEnum, - typeId: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as QueryTypeEnum, + typeId: readUint16(buf, offset + 1) as TypeId, offset: readUint32(buf, offset + 3), limit: readUint32(buf, offset + 7), filterSize: readUint16(buf, offset + 11), - iteratorType: (buf[offset + 13]) as QueryIteratorTypeEnum, + iteratorType: buf[offset + 13] as QueryIteratorTypeEnum, size: readUint16(buf, offset + 14), resultsSize: readUint16(buf, offset + 16), accumulatorSize: readUint16(buf, offset + 18), - sort: (((buf[offset + 20] >>> 0) & 1)) === 1, - hasGroupBy: (((buf[offset + 20] >>> 1) & 1)) === 1, - isSamplingSet: (((buf[offset + 20] >>> 2) & 1)) === 1, + sort: ((buf[offset + 20] >>> 0) & 1) === 1, + hasGroupBy: ((buf[offset + 20] >>> 1) & 1) === 1, + isSamplingSet: ((buf[offset + 20] >>> 2) & 1) === 1, } return value } export const readAggHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), - iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 13]) as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), - sort: (buf: Uint8Array, offset: number) => (((buf[offset + 20] >>> 0) & 1)) === 1, - hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 20] >>> 1) & 1)) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 20] >>> 2) & 1)) === 1, + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), + iteratorType: (buf: Uint8Array, offset: number) => + buf[offset + 13] as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + resultsSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 16), + accumulatorSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 18), + sort: (buf: Uint8Array, offset: number) => + ((buf[offset + 20] >>> 0) & 1) === 1, + hasGroupBy: (buf: Uint8Array, offset: number) => + ((buf[offset + 20] >>> 1) & 1) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => + ((buf[offset + 20] >>> 2) & 1) === 1, } export const createAggHeader = (header: AggHeader): Uint8Array => { @@ -3844,13 +3943,17 @@ export const addMultiSubscriptionHeaderByteSize = 2 export const addMultiSubscriptionHeaderAlignOf = 2 -export const packaddMultiSubscriptionHeader = (obj: addMultiSubscriptionHeader): bigint => { +export const packaddMultiSubscriptionHeader = ( + obj: addMultiSubscriptionHeader, +): bigint => { let val = 0n val |= (BigInt(obj.typeId) & 65535n) << 0n return val } -export const unpackaddMultiSubscriptionHeader = (val: bigint): addMultiSubscriptionHeader => { +export const unpackaddMultiSubscriptionHeader = ( + val: bigint, +): addMultiSubscriptionHeader => { return { typeId: Number((val >> 0n) & 65535n), } @@ -3883,10 +3986,12 @@ export const readaddMultiSubscriptionHeader = ( } export const readaddMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createaddMultiSubscriptionHeader = (header: addMultiSubscriptionHeader): Uint8Array => { +export const createaddMultiSubscriptionHeader = ( + header: addMultiSubscriptionHeader, +): Uint8Array => { const buffer = new Uint8Array(addMultiSubscriptionHeaderByteSize) writeaddMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -3909,13 +4014,17 @@ export const removeMultiSubscriptionHeaderByteSize = 2 export const removeMultiSubscriptionHeaderAlignOf = 2 -export const packremoveMultiSubscriptionHeader = (obj: removeMultiSubscriptionHeader): bigint => { +export const packremoveMultiSubscriptionHeader = ( + obj: removeMultiSubscriptionHeader, +): bigint => { let val = 0n val |= (BigInt(obj.typeId) & 65535n) << 0n return val } -export const unpackremoveMultiSubscriptionHeader = (val: bigint): removeMultiSubscriptionHeader => { +export const unpackremoveMultiSubscriptionHeader = ( + val: bigint, +): removeMultiSubscriptionHeader => { return { typeId: Number((val >> 0n) & 65535n), } @@ -3948,10 +4057,12 @@ export const readremoveMultiSubscriptionHeader = ( } export const readremoveMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createremoveMultiSubscriptionHeader = (header: removeMultiSubscriptionHeader): Uint8Array => { +export const createremoveMultiSubscriptionHeader = ( + header: removeMultiSubscriptionHeader, +): Uint8Array => { const buffer = new Uint8Array(removeMultiSubscriptionHeaderByteSize) writeremoveMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -3993,9 +4104,9 @@ export const packAggProp = (obj: AggProp): bigint => { export const unpackAggProp = (val: bigint): AggProp => { return { propId: Number((val >> 0n) & 255n), - propType: (Number((val >> 8n) & 255n)) as PropTypeEnum, + propType: Number((val >> 8n) & 255n) as PropTypeEnum, propDefStart: Number((val >> 16n) & 65535n), - aggFunction: (Number((val >> 32n) & 255n)) as AggFunctionEnum, + aggFunction: Number((val >> 32n) & 255n) as AggFunctionEnum, resultPos: Number((val >> 40n) & 65535n), accumulatorPos: Number((val >> 56n) & 65535n), } @@ -4042,15 +4153,12 @@ export const writeAggPropProps = { }, } -export const readAggProp = ( - buf: Uint8Array, - offset: number, -): AggProp => { +export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { const value: AggProp = { propId: buf[offset], - propType: (buf[offset + 1]) as PropTypeEnum, + propType: buf[offset + 1] as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), - aggFunction: (buf[offset + 4]) as AggFunctionEnum, + aggFunction: buf[offset + 4] as AggFunctionEnum, resultPos: readUint16(buf, offset + 5), accumulatorPos: readUint16(buf, offset + 7), } @@ -4058,12 +4166,16 @@ export const readAggProp = ( } export const readAggPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), - aggFunction: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as AggFunctionEnum, - resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - accumulatorPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 1] as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2), + aggFunction: (buf: Uint8Array, offset: number) => + buf[offset + 4] as AggFunctionEnum, + resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + accumulatorPos: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 7), } export const createAggProp = (header: AggProp): Uint8Array => { @@ -4113,7 +4225,7 @@ export const packGroupByKeyProp = (obj: GroupByKeyProp): bigint => { export const unpackGroupByKeyProp = (val: bigint): GroupByKeyProp => { return { propId: Number((val >> 0n) & 255n), - propType: (Number((val >> 8n) & 255n)) as PropTypeEnum, + propType: Number((val >> 8n) & 255n) as PropTypeEnum, propDefStart: Number((val >> 16n) & 65535n), stepType: Number((val >> 32n) & 255n), stepRange: Number((val >> 40n) & 4294967295n), @@ -4168,7 +4280,7 @@ export const readGroupByKeyProp = ( ): GroupByKeyProp => { const value: GroupByKeyProp = { propId: buf[offset], - propType: (buf[offset + 1]) as PropTypeEnum, + propType: buf[offset + 1] as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), stepType: buf[offset + 4], stepRange: readUint32(buf, offset + 5), @@ -4178,12 +4290,14 @@ export const readGroupByKeyProp = ( } export const readGroupByKeyPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), - stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], - stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 1] as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2), + stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], + stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), } export const createGroupByKeyProp = (header: GroupByKeyProp): Uint8Array => { @@ -4292,7 +4406,8 @@ export const FilterOpCompareInverse = { selectLargeRefsEdge, nextOrIndex */ -export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] +export type FilterOpCompareEnum = + (typeof FilterOpCompare)[keyof typeof FilterOpCompare] export type FilterOp = { prop: PropTypeEnum @@ -4312,8 +4427,8 @@ export const packFilterOp = (obj: FilterOp): bigint => { export const unpackFilterOp = (val: bigint): FilterOp => { return { - prop: (Number((val >> 0n) & 255n)) as PropTypeEnum, - compare: (Number((val >> 8n) & 255n)) as FilterOpCompareEnum, + prop: Number((val >> 0n) & 255n) as PropTypeEnum, + compare: Number((val >> 8n) & 255n) as FilterOpCompareEnum, } } @@ -4338,20 +4453,18 @@ export const writeFilterOpProps = { }, } -export const readFilterOp = ( - buf: Uint8Array, - offset: number, -): FilterOp => { +export const readFilterOp = (buf: Uint8Array, offset: number): FilterOp => { const value: FilterOp = { - prop: (buf[offset]) as PropTypeEnum, - compare: (buf[offset + 1]) as FilterOpCompareEnum, + prop: buf[offset] as PropTypeEnum, + compare: buf[offset + 1] as FilterOpCompareEnum, } return value } export const readFilterOpProps = { - prop: (buf: Uint8Array, offset: number) => (buf[offset]) as PropTypeEnum, - compare: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as FilterOpCompareEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset] as PropTypeEnum, + compare: (buf: Uint8Array, offset: number) => + buf[offset + 1] as FilterOpCompareEnum, } export const createFilterOp = (header: FilterOp): Uint8Array => { @@ -4471,13 +4584,15 @@ export const readFilterCondition = ( } export const readFilterConditionProps = { - op: (buf: Uint8Array, offset: number) => unpackFilterOp(BigInt(readUint16(buf, offset))), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - prop: (buf: Uint8Array, offset: number) => buf[offset + 6], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), - len: (buf: Uint8Array, offset: number) => buf[offset + 9], - fieldSchema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 10), - offset: (buf: Uint8Array, offset: number) => buf[offset + 18], + op: (buf: Uint8Array, offset: number) => + unpackFilterOp(BigInt(readUint16(buf, offset))), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + prop: (buf: Uint8Array, offset: number) => buf[offset + 6], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + len: (buf: Uint8Array, offset: number) => buf[offset + 9], + fieldSchema: (buf: Uint8Array, offset: number) => + readUint64(buf, offset + 10), + offset: (buf: Uint8Array, offset: number) => buf[offset + 18], } export const createFilterCondition = (header: FilterCondition): Uint8Array => { @@ -4523,7 +4638,7 @@ export const unpackFilterSelect = (val: bigint): FilterSelect => { return { size: Number((val >> 0n) & 4294967295n), typeEntry: Number((val >> 32n) & 18446744073709551615n), - typeId: (Number((val >> 96n) & 65535n)) as TypeId, + typeId: Number((val >> 96n) & 65535n) as TypeId, } } @@ -4560,15 +4675,16 @@ export const readFilterSelect = ( const value: FilterSelect = { size: readUint32(buf, offset), typeEntry: readUint64(buf, offset + 4), - typeId: (readUint16(buf, offset + 12)) as TypeId, + typeId: readUint16(buf, offset + 12) as TypeId, } return value } export const readFilterSelectProps = { - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 12)) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 12) as TypeId, } export const createFilterSelect = (header: FilterSelect): Uint8Array => { @@ -4587,4 +4703,3 @@ export const pushFilterSelect = ( buf.pushU16(Number(header.typeId)) return index } - diff --git a/test/youzi.ts b/test/youzi.ts index 2a5079cbb5..682ff95c7d 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -1,5 +1,5 @@ import { BasedDb } from '../src/index.js' -import { AutoSizedUint8Array } from '../src/db-client/modify/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from '../src/utils/AutoSizedUint8Array.js' import { flush, getTypeDefs, @@ -12,9 +12,6 @@ import test from './shared/test.js' await test.skip('schema defs', async (t) => { const schema = parseSchema({ types: { - // role: { - // name: 'string', - // }, user: { age: 'number', rating: 'uint8', From a5eb6573c23555ee5c5dd944c87aa18de8c6651a Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 28 Jan 2026 13:09:10 +0100 Subject: [PATCH 012/449] snurp --- test/include/thread.perf.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/include/thread.perf.ts b/test/include/thread.perf.ts index e7ed1ffc5f..dc4a703cfd 100644 --- a/test/include/thread.perf.ts +++ b/test/include/thread.perf.ts @@ -158,6 +158,25 @@ await test('include', async (t) => { 'OPS per second', ) + await perf( + async () => { + const q: any[] = [] + for (let i = 0; i < 5; i++) { + q.push( + db + .query('simple') + .include('id', 'nr') + // .range(0, 10e6 + i) + .filter('nr', '=', 100 + i) + .get(), + ) + } + await Promise.all(q) + }, + '1M Nodes include', + { repeat: 100 }, + ) + await perf( async () => { const q: any[] = [] From ff6a669d3cd5fe94f9284cde7da6e5b3e8b8c27b Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 28 Jan 2026 15:05:41 +0100 Subject: [PATCH 013/449] Cleanup selva schema parsing --- clibs/lib/selva/schema.c | 154 +++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 79 deletions(-) diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index 9a84ea3237..871d6262e5 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 SAULX + * Copyright (c) 2024-2026 SAULX * SPDX-License-Identifier: MIT */ #include @@ -38,6 +38,8 @@ struct schemabuf_parser_ctx { unsigned version; }; +typedef uint8_t __attribute__((__hardbool__(0, 1))) schema_bool_t; +static_assert(sizeof(schema_bool_t) == 1); static inline uint32_t calc_default_off(struct schemabuf_parser_ctx *ctx, size_t off) { @@ -51,37 +53,38 @@ static int type2fs_reserved(struct schemabuf_parser_ctx *, struct SelvaFieldsSch static int type2fs_micro_buffer(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) { - uint16_t len; - size_t off = 1; - const size_t min_buf_len = 1 + sizeof(len) + (ctx->version >= 6); + struct { + enum SelvaFieldType type; + uint16_t len; + schema_bool_t has_default; + } __packed head; + size_t off = 0; struct SelvaFieldSchema *fs = &schema->field_schemas[field]; - if (ctx->len < min_buf_len) { + if (ctx->len < sizeof(head)) { return SELVA_EINVAL; } - memcpy(&len, ctx->buf + off, sizeof(len)); - off += sizeof(len); + memcpy(&head, ctx->buf + off, sizeof(head)); + off += sizeof(head); *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_MICRO_BUFFER, .smb = { - .len = len, + .len = head.len, .default_off = 0, }, }; - if (ctx->version >= 6) { - if (ctx->buf[off++]) { /* has default */ - if (ctx->len < off + len) { - return SELVA_EINVAL; - } - - /* * Default is copied straight from the schema buffer. */ - fs->smb.default_off = calc_default_off(ctx, off); - off += len; + if (head.has_default) { + if (ctx->len < off + head.len) { + return SELVA_EINVAL; } + + /* * Default is copied straight from the schema buffer. */ + fs->smb.default_off = calc_default_off(ctx, off); + off += head.len; } return off; @@ -89,17 +92,20 @@ static int type2fs_micro_buffer(struct schemabuf_parser_ctx *ctx, struct SelvaFi static int type2fs_string(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) { - size_t off = 1; - uint8_t fixed_len; - const size_t min_buf_len = 1 + sizeof(fixed_len) + (ctx->version >= 7); + struct { + enum SelvaFieldType type; + uint8_t fixed_len; + uint32_t default_len; + } __packed head; + size_t off = 0; struct SelvaFieldSchema *fs = &schema->field_schemas[field]; - if (ctx->len < min_buf_len) { + if (ctx->len < sizeof(head)) { return SELVA_EINVAL; } - memcpy(&fixed_len, ctx->buf + off, sizeof(fixed_len)); - off += sizeof(fixed_len); + memcpy(&head, ctx->buf + off, sizeof(head)); + off += sizeof(head); *fs = (struct SelvaFieldSchema){ .field = field, @@ -109,26 +115,19 @@ static int type2fs_string(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc * We only allow very short strings to be stored as fixed embedded * strings. This is best to be aligned to 64-bit boundaries */ - .fixed_len = fixed_len <= 48 ? fixed_len : 0, + .fixed_len = head.fixed_len <= 48 ? head.fixed_len : 0, + .default_len = head.default_len, }, }; - if (ctx->version >= 8) { - uint32_t default_len; - - memcpy(&default_len, ctx->buf + off, sizeof(default_len)); - off += sizeof(default_len); - fs->string.default_len = default_len; - - if (default_len > 0) { /* has default */ - if (ctx->len < off + default_len) { - return SELVA_EINVAL; - } - - /* default is copied straight from the schema buffer. */ - fs->string.default_off = calc_default_off(ctx, off); - off += default_len; + if (head.default_len > 0) { /* has default */ + if (ctx->len < off + head.default_len) { + return SELVA_EINVAL; } + + /* default is copied straight from the schema buffer. */ + fs->string.default_off = calc_default_off(ctx, off); + off += head.default_len; } return off; @@ -136,37 +135,41 @@ static int type2fs_string(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc static int type2fs_text(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) { + struct { + enum SelvaFieldType type; + uint8_t nr_defaults; + } __packed head; + size_t off = 0; struct SelvaFieldSchema *fs = &schema->field_schemas[field]; - size_t off = 1; + + if (ctx->len < sizeof(head)) { + return SELVA_EINVAL; + } *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_TEXT, }; - if (ctx->version >= 8) { - uint8_t nr_defaults; - - memcpy(&nr_defaults, ctx->buf + off, sizeof(nr_defaults)); - off += sizeof(nr_defaults); - fs->text.nr_defaults = nr_defaults; - - if (nr_defaults > 0) { /* has defaults */ - fs->text.defaults_off = (uint32_t)((ptrdiff_t)(ctx->buf - ctx->schema_buf) + off); + memcpy(&head, ctx->buf + off, sizeof(head)); + off += sizeof(head); + fs->text.nr_defaults = head.nr_defaults; - /* - * Iterate over the defaults and skip them. - */ - for (size_t i = 0; i < nr_defaults; i++) { - uint32_t len; + if (head.nr_defaults > 0) { /* has defaults */ + fs->text.defaults_off = (uint32_t)((ptrdiff_t)(ctx->buf - ctx->schema_buf) + off); - if (ctx->len < off + sizeof(len)) { - return SELVA_EINVAL; - } + /* + * Iterate over the defaults and skip them. + */ + for (size_t i = 0; i < head.nr_defaults; i++) { + uint32_t len; - memcpy(&len, ctx->buf + off, sizeof(len)); - off += sizeof(len) + len; + if (ctx->len < off + sizeof(len)) { + return SELVA_EINVAL; } + + memcpy(&len, ctx->buf + off, sizeof(len)); + off += sizeof(len) + len; } } @@ -176,8 +179,6 @@ static int type2fs_text(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSche static int type2fs_refs(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field, enum SelvaFieldType type) { const uint8_t *buf = ctx->buf; - size_t len = ctx->len; - size_t orig_len = ctx->len; struct SelvaFieldSchema *fs = &schema->field_schemas[field]; struct { enum SelvaFieldType type; @@ -186,35 +187,31 @@ static int type2fs_refs(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSche field_t inverse_field; node_type_t edge_node_type; uint32_t capped; - } __packed constraints; + } __packed spec; - static_assert(sizeof(constraints) == 11); + static_assert(sizeof(spec) == 11); - size_t copy_len = sizeof(constraints) - (ctx->version < 7) * sizeof_field(typeof(constraints), capped); - if (len < copy_len) { + if (ctx->len < sizeof(spec)) { return SELVA_EINVAL; } - constraints.capped = 0; - memcpy(&constraints, buf, copy_len); - buf += copy_len; - len -= copy_len; + memcpy(&spec, buf, sizeof(spec)); - enum EdgeFieldConstraintFlag flags = constraints.flags & (EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT); + enum EdgeFieldConstraintFlag flags = spec.flags & (EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT); *fs = (struct SelvaFieldSchema){ .field = field, .type = type, .edge_constraint = { .flags = flags, - .inverse_field = constraints.inverse_field, - .dst_node_type = constraints.dst_node_type, - .edge_node_type = constraints.edge_node_type, - .limit = constraints.capped, + .inverse_field = spec.inverse_field, + .dst_node_type = spec.dst_node_type, + .edge_node_type = spec.edge_node_type, + .limit = spec.capped, }, }; - return orig_len - len; + return sizeof(spec); } static int type2fs_reference(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) @@ -261,14 +258,13 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc uint16_t vec_len; /*!< Length of a single vector. */ uint16_t comp_size; /*!< Component size in the vector. */ uint8_t has_default; - } __packed spec = {}; - size_t copy_len = sizeof(spec) + (ctx->version < 8) * -sizeof_field(typeof(spec), has_default); + } __packed spec; if (ctx->len < sizeof(spec)) { return SELVA_EINVAL; } - memcpy(&spec, ctx->buf, copy_len); + memcpy(&spec, ctx->buf, sizeof(spec)); *fs = (struct SelvaFieldSchema){ .field = field, @@ -281,7 +277,7 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc }, }; - return copy_len; + return sizeof(spec); } static struct schemabuf_parser { From e9929824c276a35f619c4fdfc48ce09eb3e3844b Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 17:58:30 +0100 Subject: [PATCH 014/449] make single modifyCmd response --- src/db-client/index.ts | 50 +++-- src/db-client/modify/index.ts | 336 ++++++++++++---------------- src/schema/defs/getTypeDefs.ts | 2 +- src/schema/defs/index.ts | 6 +- src/schema/defs/props/base.ts | 19 +- src/schema/defs/props/fixed.ts | 5 +- src/schema/defs/props/references.ts | 21 +- src/schema/defs/props/separate.ts | 34 +-- 8 files changed, 230 insertions(+), 243 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 5b974e4829..aa38f91de6 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -17,10 +17,8 @@ import { serializeDelete, serializeUpdate, ModifyCtx, - modify, - schedule, flush, - ModifyItem, + ModifyCmd, } from './modify/index.js' type DbClientOpts = { @@ -103,7 +101,7 @@ export class DbClient extends DbShared { } create(type: string, obj = {}, opts?: ModifyOpts): Promise { - return modify( + return new ModifyCmd( this.modifyCtx, serializeCreate, this.schema!, @@ -112,6 +110,15 @@ export class DbClient extends DbShared { this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, ) + // return modify( + // this.modifyCtx, + // serializeCreate, + // this.schema!, + // type, + // obj, + // this.modifyCtx.buf, + // opts?.locale ? LangCode[opts.locale] : LangCode.none, + // ) } update( @@ -123,7 +130,8 @@ export class DbClient extends DbShared { // if (id instanceof Promise) { // id = await id // } - return modify( + + return new ModifyCmd( this.modifyCtx, serializeUpdate, this.schema!, @@ -133,6 +141,16 @@ export class DbClient extends DbShared { this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, ) + // return modify( + // this.modifyCtx, + // serializeUpdate, + // this.schema!, + // type, + // id, + // obj, + // this.modifyCtx.buf, + // opts?.locale ? LangCode[opts.locale] : LangCode.none, + // ) } delete( @@ -142,7 +160,7 @@ export class DbClient extends DbShared { // if (id instanceof Promise) { // id = await id // } - return modify( + return new ModifyCmd( this.modifyCtx, serializeDelete, this.schema!, @@ -151,6 +169,16 @@ export class DbClient extends DbShared { id, this.modifyCtx.buf, ) + + // return modify( + // this.modifyCtx, + // serializeDelete, + // this.schema!, + // type, + // // TODO make it perf + // id, + // this.modifyCtx.buf, + // ) } query( @@ -197,18 +225,8 @@ export class DbClient extends DbShared { // For more advanced / internal usage - use isModified instead for most cases drain() { - // if (this.upserting.size) { - // await Promise.all(Array.from(this.upserting).map(([, { p }]) => p)) - // } - // await drain(this, this.modifyCtx) - - // const t = this.writeTime - // this.writeTime = 0 flush(this.modifyCtx) return this.isModified() - // await this.modifyCtx.lastModify?.catch(noop) - // await this.modifyCtx.lastModify - // return t } async isModified() { diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 8b1553dd68..dfbeb90705 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -123,122 +123,6 @@ export const serializeDelete = < }) } -export class QueuedItem implements Promise { - constructor(blocker: ModifyItem | QueuedItem, args: IArguments) { - while (blocker instanceof QueuedItem) { - blocker = blocker._blocker - } - this._blocker = blocker - this._args = args - blocker._batch.queue ??= [] - blocker._batch.queue.push(this) - } - [Symbol.toStringTag]!: 'QueuedItem' - _args: IArguments - _item?: ModifyItem - _blocker: ModifyItem | QueuedItem - private _p?: Promise - - _promise() { - this._p ??= - this._item || - new Promise((resolve) => { - this._resolve = resolve - }) - return this._p - } - - get id(): number | undefined { - return this._item?.id - } - - get err(): ModifyErrorEnum | undefined { - return this._item?.err - } - - _resolve?: (value: number | PromiseLike) => void - - then( - onfulfilled?: ((value: number) => Res1 | PromiseLike) | null, - onrejected?: ((reason: any) => Res2 | PromiseLike) | null, - ): Promise { - return this._promise().then(onfulfilled, onrejected) - } - catch( - onrejected?: ((reason: any) => Res | PromiseLike) | null, - ): Promise { - return this._promise().catch(onrejected) - } - finally(onfinally?: (() => void) | null): Promise { - return this._promise().finally(onfinally) - } -} - -export class ModifyItem implements Promise { - constructor(batch: ModifyBatch) { - this._batch = batch - this._index = batch.count++ - } - - [Symbol.toStringTag]!: 'ModifyItem' - - private _p?: Promise - _promise() { - this._p ??= new Promise((resolve, reject) => { - if (this.id) { - resolve(this.id) - } else if (this.err) { - reject(this.err) - } else { - this._resolve = resolve - this._reject = reject - this._batch.items ??= [] - this._batch.items.push(this) - } - }) - - return this._p - } - - _batch: ModifyBatch - _index: number - _id?: number - _err?: ModifyErrorEnum - _args?: IArguments - - get id(): number | undefined { - if (this._batch.result) { - this._id = readUint32(this._batch.result, this._index * 5) - } - return this._id - } - - get err(): ModifyErrorEnum | undefined { - if (this._batch.result) { - this._err = this._batch.result[this._index * 5 + 4] as ModifyErrorEnum - } - return this._err - } - - _resolve?: (value: number | PromiseLike) => void - _reject?: (reason?: any) => void - - then( - onfulfilled?: ((value: number) => Res1 | PromiseLike) | null, - onrejected?: ((reason: any) => Res2 | PromiseLike) | null, - ): Promise { - return this._promise().then(onfulfilled, onrejected) - } - catch( - onrejected?: ((reason: any) => Res | PromiseLike) | null, - ): Promise { - return this._promise().catch(onrejected) - } - finally(onfinally?: (() => void) | null): Promise { - return this._promise().finally(onfinally) - } -} - type ModifySerializer = | typeof serializeCreate | typeof serializeUpdate @@ -246,8 +130,8 @@ type ModifySerializer = type ModifyBatch = { count: number - items?: ModifyItem[] - queue?: QueuedItem[] + items?: ModifyCmd[] + queue?: ModifyCmd[] result?: Uint8Array flushed?: true } @@ -256,7 +140,7 @@ export type ModifyCtx = { buf: AutoSizedUint8Array batch: ModifyBatch flushTime: number - lastModify?: ModifyItem | QueuedItem + lastModify?: ModifyCmd flushTimer?: NodeJS.Timeout | true | undefined hooks: { flushModify: (buf: Uint8Array) => Promise @@ -264,89 +148,163 @@ export type ModifyCtx = { } export const flush = (ctx: ModifyCtx) => { - if (ctx.buf.length) { - const batch = ctx.batch - writeModifyHeaderProps.count(ctx.buf.data, batch.count, 0) - batch.flushed = true - ctx.hooks.flushModify(ctx.buf.view).then((result) => { - batch.result = result - const items = batch.items - const queue = batch.queue - if (queue) { - batch.queue = undefined - for (const item of queue) { - const res = modify.apply(null, item._args) - item._item = res - item._resolve?.(res) + if (ctx.buf.length === 0) return + const batch = ctx.batch + writeModifyHeaderProps.count(ctx.buf.data, batch.count, 0) + batch.flushed = true + ctx.hooks.flushModify(ctx.buf.view).then((result) => { + batch.result = result + const items = batch.items + const queue = batch.queue + if (queue) { + batch.queue = undefined + for (const item of queue) { + item._exec.apply(item, item._arguments) + if (item._resolve) { + item._await() } } - if (items) { - batch.items = undefined - for (const item of items) { - const id = item.id - const err = item.err - if (err) { - item._reject!(err) - } else { - item._resolve!(id!) - } + } + if (items) { + batch.items = undefined + for (const item of items) { + const id = item.id + const err = item.error + if (err) { + item._reject!(err) + } else { + item._resolve!(id!) } } - }) + } + }) - ctx.buf.flush() - ctx.batch = { count: 0 } - } + ctx.buf.flush() + ctx.batch = { count: 0 } } -export const schedule = (ctx: ModifyCtx) => { - if (!ctx.flushTimer) { - if (ctx.flushTime === 0) { - ctx.flushTimer = true - process.nextTick(() => { - ctx.flushTimer = undefined - flush(ctx) - }) - } else { - ctx.flushTimer = setTimeout(() => { - ctx.flushTimer = undefined - flush(ctx) - }, ctx.flushTime) - } +const schedule = (ctx: ModifyCtx) => { + if (ctx.flushTimer) return + if (ctx.flushTime === 0) { + ctx.flushTimer = true + process.nextTick(() => { + ctx.flushTimer = undefined + flush(ctx) + }) + } else { + ctx.flushTimer = setTimeout(() => { + ctx.flushTimer = undefined + flush(ctx) + }, ctx.flushTime) } } -// TODO implement single ModifyCmd instead of both QueuedItem and ModifyItem -export const modify = function ( - ctx: ModifyCtx, - serialize: S, - ...args: Parameters -): Promise { - const isEmpty = ctx.buf.length === 0 - if (isEmpty) { - pushModifyHeader(ctx.buf, { - opId: 0, // is filled on server - opType: 0, // is filled on server - schema: args[0].hash, - count: 0, +export class ModifyCmd + implements Promise +{ + [Symbol.toStringTag]!: 'ModifyCmd' + constructor(ctx: ModifyCtx, serialize: S, ...args: Parameters) { + this._exec(ctx, serialize, ...args) + } + private _result() { + if (this._batch?.result) { + this._id = readUint32(this._batch.result, this._index! * 5) + this._error = this._batch.result[this._index! * 5 + 4] as ModifyErrorEnum + } + } + get id(): number | undefined { + this._result() + return this._id + } + get error(): ModifyErrorEnum | undefined { + this._result() + return this._error + } + get tmpId(): number | undefined { + if (this._batch && !this._batch.flushed) { + return this._index + } + } + get promise(): Promise { + this._promise ??= new Promise((resolve, reject) => { + if (this.id) { + resolve(this.id) + } else if (this.error) { + reject(this.error) + } else { + this._resolve = resolve + this._reject = reject + this._await() + } }) + return this._promise } - const initialLength = ctx.buf.length - try { - ;(serialize as (...args: any[]) => void)(...args) - } catch (e) { - ctx.buf.length = initialLength - if (e === AutoSizedUint8Array.ERR_OVERFLOW) { - if (isEmpty) throw new Error('Range error') - flush(ctx) - return modify.apply(null, arguments) - } else if (e instanceof ModifyItem || e instanceof QueuedItem) { - return (ctx.lastModify = new QueuedItem(e, arguments)) - } else { - throw e + + private _id?: number + private _error?: ModifyErrorEnum + private _blocker?: ModifyCmd + private _index?: number + private _batch?: ModifyBatch + private _promise?: Promise + + _arguments?: IArguments + _resolve?: (value: number | PromiseLike) => void + _reject?: (reason?: any) => void + _await() { + if (this._batch) { + this._batch.items ??= [] + this._batch.items.push(this) } } + _exec(ctx: ModifyCtx, serialize: S, ...args: Parameters) { + const isEmpty = ctx.buf.length === 0 + if (isEmpty) { + pushModifyHeader(ctx.buf, { + opId: 0, // is filled on server + opType: 0, // is filled on server + schema: args[0].hash, + count: 0, + }) + } + const initialLength = ctx.buf.length + try { + ;(serialize as any)(...args) + } catch (e) { + ctx.buf.length = initialLength + if (e === AutoSizedUint8Array.ERR_OVERFLOW) { + if (isEmpty) throw new Error('Range error') + flush(ctx) + return this._exec.apply(this, arguments) + } else if (e instanceof ModifyCmd) { + let blocker: ModifyCmd = e + while (blocker._blocker) blocker = blocker._blocker + blocker._batch!.queue ??= [] + blocker._batch!.queue.push(this) + this._blocker = blocker + this._arguments = arguments + } else { + throw e + } + } + + schedule(ctx) + this._batch = ctx.batch + this._index = ctx.batch.count++ + ctx.lastModify = this + } - schedule(ctx) - return (ctx.lastModify = new ModifyItem(ctx.batch)) + then( + onfulfilled?: ((value: number) => Res1 | PromiseLike) | null, + onrejected?: ((reason: any) => Res2 | PromiseLike) | null, + ): Promise { + return this.promise.then(onfulfilled, onrejected) + } + catch( + onrejected?: ((reason: any) => Res | PromiseLike) | null, + ): Promise { + return this.promise.catch(onrejected) + } + finally(onfinally?: (() => void) | null): Promise { + return this.promise.finally(onfinally) + } } diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index e966b8ef27..9dae75b781 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -77,7 +77,7 @@ const getTypeDef = ({ props }: SchemaType): TypeDef => { continue } - const def = new Def(prop, path) + const def = new Def(prop, path, typeDef) if (def.size) { typeDef.main.push(def) } else { diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index bb265ac4da..6a81a7d191 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -5,8 +5,8 @@ import type { PropTypeEnum, } from '../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import * as fixed from './props/fixed.js' import * as references from './props/references.js' +import * as fixed from './props/fixed.js' import * as vars from './props/separate.js' export type PropTree = Map @@ -29,6 +29,8 @@ export type PropDef = { edges?: TypeDef ref?: TypeDef refProp?: PropDef + typeDef: TypeDef + isEdge: boolean pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -38,7 +40,7 @@ export type PropDef = { } export type PropDefClass = { - new (prop: SchemaProp, path: string[]): PropDef + new (prop: SchemaProp, path: string[], typeDef: TypeDef): PropDef } export const defs: Record< diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 1e102805f2..bb3af5f223 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -5,12 +5,13 @@ import type { PropTypeEnum, } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' -import type { PropDef } from '../index.js' +import type { PropDef, TypeDef } from '../index.js' export class BasePropDef implements PropDef { - constructor(prop: SchemaProp, path: string[]) { + constructor(prop: SchemaProp, path: string[], typeDef: TypeDef) { this.prop = prop this.path = path + this.typeDef = typeDef } id = 0 start = 0 @@ -18,7 +19,8 @@ export class BasePropDef implements PropDef { type: PropTypeEnum = 0 as PropTypeEnum prop: SchemaProp path: string[] - + isEdge: boolean = false + typeDef: TypeDef pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -28,3 +30,14 @@ export class BasePropDef implements PropDef { // To be implemented by subclasses } } + +const filter = [ + { + field: 'nr', + op: '<', + value: 10, + and: { + field: 'nr', + }, + }, +] diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 43482a079d..7d987957a8 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -3,6 +3,7 @@ import { convertToTimestamp } from '../../../utils/timestamp.js' import { PropType, type PropTypeEnum } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' +import type { TypeDef } from '../index.js' export const number = class Number extends BasePropDef { override type: PropTypeEnum = PropType.number @@ -56,8 +57,8 @@ export const int32 = class Int32 extends uint32 { } export const enum_ = class Enum extends uint8 { - constructor(prop: SchemaEnum, path: string[]) { - super(prop, path) + constructor(prop: SchemaEnum, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) prop.enum.forEach((val, i) => { const byte = i + 1 this.enum[byte] = val diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 4855089a9f..d5b004b556 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -16,11 +16,7 @@ import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { SchemaProp } from '../../../schema.js' import { BasePropDef } from './base.js' import type { PropDef, TypeDef } from '../index.js' -import { - ModifyItem, - QueuedItem, - serializeProps, -} from '../../../db-client/modify/index.js' +import { ModifyCmd, serializeProps } from '../../../db-client/modify/index.js' type Edges = Record<`${string}`, unknown> | undefined @@ -53,7 +49,7 @@ const serializeIds = ( const serializeTmpIds = ( buf: AutoSizedUint8Array, - items: ModifyItem[], + items: ModifyCmd[], offset: number, ): undefined | any => { let i = offset @@ -119,12 +115,11 @@ const serializeIdsAndMeta = ( const getRealId = (item: any) => { if (typeof item === 'number') return item - if (item instanceof ModifyItem || item instanceof QueuedItem) return item.id + if (item instanceof ModifyCmd) return item.id } const getTmpId = (item: any) => { - if (item instanceof QueuedItem) item = item._item - if (item instanceof ModifyItem && !item._batch.flushed) return item._index + if (item instanceof ModifyCmd) return item.tmpId } const isValidRefObj = (item: any) => @@ -165,9 +160,9 @@ const setReferences = ( const start = buf.length offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - } else if (item instanceof ModifyItem || item instanceof QueuedItem) { + } else if (item instanceof ModifyCmd) { throw item - } else if (item.id instanceof ModifyItem || item instanceof QueuedItem) { + } else if (item.id instanceof ModifyCmd) { throw item.id } else { throw 'bad ref!' @@ -259,7 +254,7 @@ export const reference = class Reference extends BasePropDef { return } - if (value instanceof ModifyItem || value instanceof QueuedItem) { + if (value instanceof ModifyCmd) { throw value } @@ -288,7 +283,7 @@ export const reference = class Reference extends BasePropDef { return } - if (value.id instanceof ModifyItem || value.id instanceof QueuedItem) { + if (value.id instanceof ModifyCmd) { throw value.id } } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index f49f721429..f3698c1154 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -15,17 +15,17 @@ import { import { xxHash64 } from '../../../db-client/xxHash64.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' +import type { TypeDef } from '../index.js' -export const string = class extends BasePropDef { - constructor(prop: SchemaString, path: string[]) { - super(prop, path) +export const string = class String extends BasePropDef { + constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) if (prop.maxBytes && prop.maxBytes < 61) { this.size = prop.maxBytes + 1 } else if (prop.max && prop.max < 31) { this.size = prop.max * 2 + 1 } if (this.size) { - // make it a fixed string prop! this.type = PropType.stringFixed this.pushValue = this.pushFixedValue } @@ -49,34 +49,34 @@ export const string = class extends BasePropDef { } // TODO do it nice -export const text = class extends string { +export const text = class Text extends string { override type = PropType.text } -export const json = class extends string { +export const json = class Json extends string { override type = PropType.json override pushValue(buf: AutoSizedUint8Array, value: any, lang: LangCodeEnum) { super.pushValue(buf, JSON.stringify(value), lang) } } -export const binary = class extends BasePropDef { +export const binary = class Binary extends BasePropDef { override type = PropType.binary override pushValue(buf: AutoSizedUint8Array, value: Uint8Array) { buf.set(value, buf.length) } } -export const alias = class extends BasePropDef { +export const alias = class Alias extends BasePropDef { override type = PropType.alias override pushValue(buf: AutoSizedUint8Array, value: any) { throw new Error('Serialize alias not implemented') } } -export const cardinality = class extends BasePropDef { - constructor(prop: SchemaCardinality, path) { - super(prop, path) +export const cardinality = class Cardinality extends BasePropDef { + constructor(prop: SchemaCardinality, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) this.sparse = prop.mode === 'sparse' this.precision = prop.precision ?? 8 } @@ -112,9 +112,9 @@ export const cardinality = class extends BasePropDef { } } -export const vector = class extends BasePropDef { - constructor(prop: SchemaVector, path) { - super(prop, path) +export const vector = class Vector extends BasePropDef { + constructor(prop: SchemaVector, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) this.vectorSize = prop.size * 4 } vectorSize: number @@ -124,9 +124,9 @@ export const vector = class extends BasePropDef { } } -export const colvec = class extends BasePropDef { - constructor(prop: SchemaVector, path) { - super(prop, path) +export const colvec = class ColVec extends BasePropDef { + constructor(prop: SchemaVector, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) this.colvecSize = prop.size * getByteSize(prop.baseType) } colvecSize: number From 8a5441acbfaa87e54f700f29750a91c8d1b884d2 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 18:15:41 +0100 Subject: [PATCH 015/449] make it nicer --- src/db-client/modify/index.ts | 42 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index dfbeb90705..a11db21ea6 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -130,8 +130,8 @@ type ModifySerializer = type ModifyBatch = { count: number - items?: ModifyCmd[] - queue?: ModifyCmd[] + promises?: ModifyCmd[] + dependents?: ModifyCmd[] result?: Uint8Array flushed?: true } @@ -154,20 +154,20 @@ export const flush = (ctx: ModifyCtx) => { batch.flushed = true ctx.hooks.flushModify(ctx.buf.view).then((result) => { batch.result = result - const items = batch.items - const queue = batch.queue - if (queue) { - batch.queue = undefined - for (const item of queue) { + const promises = batch.promises + const dependents = batch.dependents + if (dependents) { + batch.dependents = undefined + for (const item of dependents) { item._exec.apply(item, item._arguments) if (item._resolve) { item._await() } } } - if (items) { - batch.items = undefined - for (const item of items) { + if (promises) { + batch.promises = undefined + for (const item of promises) { const id = item.id const err = item.error if (err) { @@ -209,14 +209,16 @@ export class ModifyCmd private _result() { if (this._batch?.result) { this._id = readUint32(this._batch.result, this._index! * 5) - this._error = this._batch.result[this._index! * 5 + 4] as ModifyErrorEnum + const errCode = this._batch.result[this._index! * 5 + 4] + if (errCode) this._error = new Error('ModifyError: ' + errCode) + this._batch = undefined } } get id(): number | undefined { this._result() return this._id } - get error(): ModifyErrorEnum | undefined { + get error(): Error | undefined { this._result() return this._error } @@ -241,7 +243,7 @@ export class ModifyCmd } private _id?: number - private _error?: ModifyErrorEnum + private _error?: Error private _blocker?: ModifyCmd private _index?: number private _batch?: ModifyBatch @@ -252,8 +254,8 @@ export class ModifyCmd _reject?: (reason?: any) => void _await() { if (this._batch) { - this._batch.items ??= [] - this._batch.items.push(this) + this._batch.promises ??= [] + this._batch.promises.push(this) } } _exec(ctx: ModifyCtx, serialize: S, ...args: Parameters) { @@ -278,11 +280,17 @@ export class ModifyCmd } else if (e instanceof ModifyCmd) { let blocker: ModifyCmd = e while (blocker._blocker) blocker = blocker._blocker - blocker._batch!.queue ??= [] - blocker._batch!.queue.push(this) + blocker._batch!.dependents ??= [] + blocker._batch!.dependents.push(this) this._blocker = blocker this._arguments = arguments + } else if (this._arguments) { + // its in async mode + this._error = e + this._reject?.(e) + return } else { + this._error = e throw e } } From 75834da74d28cb1915b2930cc1827a216b9be88c Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 20:03:34 +0100 Subject: [PATCH 016/449] make it nicer --- scripts/zigTsExports.ts | 36 ++-- src/schema/defs/props/fixed.ts | 14 +- src/schema/defs/props/references.ts | 12 +- src/schema/defs/props/separate.ts | 8 +- src/utils/AutoSizedUint8Array.ts | 166 ++++++++---------- src/zigTsExports.ts | 262 ++++++++++++++-------------- 6 files changed, 239 insertions(+), 259 deletions(-) diff --git a/scripts/zigTsExports.ts b/scripts/zigTsExports.ts index 72bd7b8c70..0856413b77 100644 --- a/scripts/zigTsExports.ts +++ b/scripts/zigTsExports.ts @@ -1051,44 +1051,44 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ switch (prim) { case 'u8': case 'LangCode': - output += ` buf.pushU8(${valRef})\n` + output += ` buf.pushUint8(${valRef})\n` break case 'bool': - output += ` buf.pushU8(${valRef} ? 1 : 0)\n` + output += ` buf.pushUint8(${valRef} ? 1 : 0)\n` break case 'i8': - output += ` buf.pushU8(${valRef})\n` + output += ` buf.pushUint8(${valRef})\n` break case 'u16': - output += ` buf.pushU16(${valRef})\n` + output += ` buf.pushUint16(${valRef})\n` break case 'i16': - output += ` buf.pushU16(${valRef})\n` + output += ` buf.pushUint16(${valRef})\n` break case 'u32': - output += ` buf.pushU32(${valRef})\n` + output += ` buf.pushUint32(${valRef})\n` break case 'i32': - output += ` buf.pushU32(${valRef})\n` + output += ` buf.pushUint32(${valRef})\n` break case 'f32': - output += ` buf.pushF32(${valRef})\n` + output += ` buf.pushFloatLE(${valRef})\n` break case 'u64': case 'usize': - output += ` buf.pushU64(${valRef})\n` + output += ` buf.pushUint64(${valRef})\n` break case 'i64': - output += ` buf.pushI64(${valRef})\n` + output += ` buf.pushInt64(${valRef})\n` break case 'f64': - output += ` buf.pushDouble(${valRef})\n` + output += ` buf.pushDoubleLE(${valRef})\n` break default: // Fallback for unknown types or padding greater than handled above const byteCount = Math.ceil(f.bitSize / 8) for (let k = 0; k < byteCount; k++) { - output += ` buf.pushU8(0)\n` + output += ` buf.pushUint8(0)\n` } } }) @@ -1109,13 +1109,13 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ } if (f.bitSize === 8) { - output += ` buf.pushU8(Number(${valWithTernary}))\n` + output += ` buf.pushUint8(Number(${valWithTernary}))\n` } else if (f.bitSize === 16) { - output += ` buf.pushU16(Number(${valWithTernary}))\n` + output += ` buf.pushUint16(Number(${valWithTernary}))\n` } else if (f.bitSize === 32) { - output += ` buf.pushU32(Number(${valWithTernary}))\n` + output += ` buf.pushUint32(Number(${valWithTernary}))\n` } else if (f.bitSize === 64) { - output += ` buf.pushU64(${valWithTernary})\n` + output += ` buf.pushUint64(${valWithTernary})\n` } currentBitGlobal += f.bitSize } else { @@ -1138,10 +1138,10 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ if (bitInByte === 0) { // New byte started - output += ` buf.pushU8(0)\n` + output += ` buf.pushUint8(0)\n` } - // Access the last byte using view directly OR ensure pushU8(0) initialized it + // Access the last byte using view directly OR ensure pushUint8(0) initialized it // We know we just pushed a byte if bitInByte == 0. // If bitInByte > 0, the byte exists at buf.length - 1 // But we need to be careful about not relying on `buf.view` if possible? diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 7d987957a8..1ff79320d5 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -9,14 +9,14 @@ export const number = class Number extends BasePropDef { override type: PropTypeEnum = PropType.number override size = 8 override pushValue(buf: AutoSizedUint8Array, value: number) { - buf.pushDouble(value) + buf.pushDoubleLE(value) } } export const timestamp = class Timestamp extends number { override type = PropType.timestamp override pushValue(buf: AutoSizedUint8Array, value: number | string) { - buf.pushI64(convertToTimestamp(value)) + buf.pushInt64(convertToTimestamp(value)) } } @@ -24,7 +24,7 @@ export const uint8 = class Uint8 extends BasePropDef { override type: PropTypeEnum = PropType.uint8 override size = 1 override pushValue(buf: AutoSizedUint8Array, value: number): void { - buf.pushU8(value) + buf.pushUint8(value) } } @@ -36,7 +36,7 @@ export const uint16 = class Uint16 extends BasePropDef { override type: PropTypeEnum = PropType.uint16 override size = 2 override pushValue(buf: AutoSizedUint8Array, value: number): void { - buf.pushU16(value) + buf.pushUint16(value) } } @@ -48,7 +48,7 @@ export const uint32 = class Uint32 extends BasePropDef { override type: PropTypeEnum = PropType.uint32 override size = 4 override pushValue(buf: AutoSizedUint8Array, value: number): void { - buf.pushU32(value) + buf.pushUint32(value) } } @@ -70,7 +70,7 @@ export const enum_ = class Enum extends uint8 { vals = new Map() override pushValue(buf: AutoSizedUint8Array, value: EnumItem): void { - buf.pushU8(this.vals.get(value) ?? 0) + buf.pushUint8(this.vals.get(value) ?? 0) } } @@ -78,6 +78,6 @@ export const boolean = class Boolean extends BasePropDef { override type = PropType.boolean override size = 1 override pushValue(buf: AutoSizedUint8Array, value: boolean): void { - buf.pushU8(value ? 1 : 0) + buf.pushUint8(value ? 1 : 0) } } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index d5b004b556..08036655ef 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -38,11 +38,11 @@ const serializeIds = ( ): number => { let i = offset // one extra for padding - buf.pushU32(0) + buf.pushUint32(0) for (; i < ids.length; i++) { const id = getRealId(ids[i]) if (!id) break - buf.pushU32(id) + buf.pushUint32(id) } return i } @@ -54,11 +54,11 @@ const serializeTmpIds = ( ): undefined | any => { let i = offset // one extra for padding - buf.pushU32(0) + buf.pushUint32(0) for (; i < items.length; i++) { const tmpId = getTmpId(items[i]) if (tmpId === undefined) break - buf.pushU32(tmpId) + buf.pushUint32(tmpId) } return i @@ -73,7 +73,7 @@ const serializeIdsAndMeta = ( edgesType?: TypeDef, ): number => { let i = offset - const start = buf.reserveU32() + const start = buf.reserveUint32() for (; i < items.length; i++) { const item = items[i] @@ -108,7 +108,7 @@ const serializeIdsAndMeta = ( } // store the amount of refs (for prealloc) - buf.setU32(i - offset, start) + buf.writeUint32(i - offset, start) return i } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index f3698c1154..13b352188e 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -39,11 +39,11 @@ export const string = class String extends BasePropDef { const normalized = val.normalize('NFKD') // TODO make header! // TODO compression - buf.pushU8(lang) - buf.pushU8(NOT_COMPRESSED) + buf.pushUint8(lang) + buf.pushUint8(NOT_COMPRESSED) const written = buf.pushString(normalized) const crc = native.crc32(buf.subarray(buf.length - written)) - buf.pushU32(crc) + buf.pushUint32(crc) } pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} } @@ -100,7 +100,7 @@ export const cardinality = class Cardinality extends BasePropDef { for (const item of value) { // validate(item, def) if (typeof item === 'string') { - buf.reserveU64() + buf.reserveUint64() xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) } else if (item instanceof Uint8Array && item.byteLength === 8) { buf.set(item, buf.length) diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index c2e02d93f2..d41504ad25 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -49,7 +49,7 @@ export class AutoSizedUint8Array { this._maxCapacity = maxCapacity } - private ensureCapacity(requiredCapacity: number): void { + private ensure(requiredCapacity: number): void { const currentCapacity = this.data.byteLength if (currentCapacity >= requiredCapacity) return if (requiredCapacity > this._maxCapacity) { @@ -72,12 +72,12 @@ export class AutoSizedUint8Array { } set(array: ArrayLike, offset: number = 0): void { - const requiredEnd = offset + array.length - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + const end = offset + array.length + if (end > this.data.length) { + this.ensure(end) } this.data.set(array, offset) - if (requiredEnd > this.length) this.length = requiredEnd + if (end > this.length) this.length = end } flush(): void { @@ -86,8 +86,8 @@ export class AutoSizedUint8Array { fill(value: number, start: number = 0, end: number = this.length): this { if (end > this.length) { - if (end > this.data.byteLength) { - this.ensureCapacity(end) + if (end > this.data.length) { + this.ensure(end) } this.length = end } @@ -95,165 +95,145 @@ export class AutoSizedUint8Array { return this } - copyWithin(target: number, start: number, end: number = this.length): this { - // Calculate required size conservatively if indices are positive - if (target >= 0 && start >= 0 && end >= 0 && end > start) { - const count = end - start - const requiredEnd = target + count - if (requiredEnd > this.data.byteLength) { - this.ensureCapacity(requiredEnd) - } - if (requiredEnd > this.length) { - this.length = requiredEnd - } - } - this.data.copyWithin(target, start, end) - return this - } - get(index: number): number | undefined { - return index >= 0 && index < this.length ? this.data[index] : undefined + return index < this.length ? this.data[index] : undefined } - pushU8(value: number): void { - const requiredEnd = this.length + 1 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushUint8(value: number): void { + const end = this.length + 1 + if (end > this.data.length) { + this.ensure(end) } this.data[this.length] = value - this.length = requiredEnd + this.length = end } - setU8(value: number, offset: number): void { - const requiredEnd = offset + 1 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + writeUint8(value: number, offset: number): void { + const end = offset + 1 + if (end > this.data.length) { + this.ensure(end) } this.data[offset] = value - if (requiredEnd > this.length) this.length = requiredEnd + if (end > this.length) this.length = end } - pushU16(value: number): void { - const requiredEnd = this.length + 2 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushUint16(value: number): void { + const end = this.length + 2 + if (end > this.data.length) { + this.ensure(end) } this.data[this.length] = value this.data[this.length + 1] = value >> 8 - this.length = requiredEnd + this.length = end } - setU16(value: number, offset: number): void { - const requiredEnd = offset + 2 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + writeUint16(value: number, offset: number): void { + const end = offset + 2 + if (end > this.data.length) { + this.ensure(end) } this.data[offset] = value this.data[offset + 1] = value >> 8 - if (requiredEnd > this.length) this.length = requiredEnd + if (end > this.length) this.length = end } - pushU32(value: number): void { - const requiredEnd = this.length + 4 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushUint32(value: number): void { + const end = this.length + 4 + if (end > this.data.length) { + this.ensure(end) } this.data[this.length] = value this.data[this.length + 1] = value >> 8 this.data[this.length + 2] = value >> 16 this.data[this.length + 3] = value >> 24 - this.length = requiredEnd + this.length = end } - pushDouble(value: number): void { - const requiredEnd = this.length + 8 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushDoubleLE(value: number): void { + const end = this.length + 8 + if (end > this.data.length) { + this.ensure(end) } writeDoubleLE(this.data, value, this.length) - this.length = requiredEnd + this.length = end } - pushF32(value: number): void { - const requiredEnd = this.length + 4 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushFloatLE(value: number): void { + const end = this.length + 4 + if (end > this.data.length) { + this.ensure(end) } writeFloatLE(this.data, value, this.length) - this.length = requiredEnd + this.length = end } - pushU64(value: number): void { - const requiredEnd = this.length + 8 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushUint64(value: number): void { + const end = this.length + 8 + if (end > this.data.length) { + this.ensure(end) } writeUint64(this.data, value, this.length) - this.length = requiredEnd + this.length = end } - pushI64(value: number): void { - const requiredEnd = this.length + 8 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + pushInt64(value: number): void { + const end = this.length + 8 + if (end > this.data.length) { + this.ensure(end) } writeInt64(this.data, value, this.length) - this.length = requiredEnd + this.length = end } pushString(value: string): number { const maxBytes = native.stringByteLength(value) - const requiredEnd = this.length + maxBytes - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + const end = this.length + maxBytes + if (end > this.data.length) { + this.ensure(end) } const { written } = ENCODER.encodeInto( value, this.data.subarray(this.length), ) - this.length += written! + this.length += written return written } - setU32(value: number, offset: number): void { - const requiredEnd = offset + 4 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + writeUint32(value: number, offset: number): void { + const end = offset + 4 + if (end > this.data.length) { + this.ensure(end) } this.data[offset] = value this.data[offset + 1] = value >> 8 this.data[offset + 2] = value >> 16 this.data[offset + 3] = value >> 24 - if (requiredEnd > this.length) this.length = requiredEnd + if (end > this.length) this.length = end } - reserveU32(): number { + reserveUint32(): number { const index = this.length - const requiredEnd = index + 4 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + const end = index + 4 + if (end > this.data.length) { + this.ensure(end) } - this.length = requiredEnd + this.length = end return index } - reserveU64(): number { + reserveUint64(): number { const index = this.length - const requiredEnd = index + 8 - if (requiredEnd > this.data.length) { - this.ensureCapacity(requiredEnd) + const end = index + 8 + if (end > this.data.length) { + this.ensure(end) } - this.length = requiredEnd + this.length = end return index } - setSizeU32(start: number) { - this.setU32(this.length - start - 4, start) - } - // Core array methods restored for type safety and performance push(byte: number): void { - return this.pushU8(byte) + return this.pushUint8(byte) } subarray(begin: number = 0, end: number = this.length): Uint8Array { diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 2fdd7db70a..929631cbc8 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -311,10 +311,10 @@ export const pushModifyHeader = ( header: ModifyHeader, ): number => { const index = buf.length - buf.pushU32(Number(header.opId)) - buf.pushU8(Number(header.opType)) - buf.pushU64(header.schema) - buf.pushU32(Number(header.count)) + buf.pushUint32(Number(header.opId)) + buf.pushUint8(Number(header.opType)) + buf.pushUint64(header.schema) + buf.pushUint32(Number(header.count)) return index } @@ -411,10 +411,10 @@ export const pushModifyUpdateHeader = ( header: ModifyUpdateHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.type)) - buf.pushU32(Number(header.id)) - buf.pushU32(Number(header.size)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.type)) + buf.pushUint32(Number(header.id)) + buf.pushUint32(Number(header.size)) return index } @@ -501,9 +501,9 @@ export const pushModifyDeleteHeader = ( header: ModifyDeleteHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.type)) - buf.pushU32(Number(header.id)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.type)) + buf.pushUint32(Number(header.id)) return index } @@ -590,9 +590,9 @@ export const pushModifyCreateHeader = ( header: ModifyCreateHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.type)) - buf.pushU32(Number(header.size)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.type)) + buf.pushUint32(Number(header.size)) return index } @@ -679,9 +679,9 @@ export const pushModifyMainHeader = ( header: ModifyMainHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.id)) - buf.pushU16(Number(header.start)) - buf.pushU16(Number(header.size)) + buf.pushUint8(Number(header.id)) + buf.pushUint16(Number(header.start)) + buf.pushUint16(Number(header.size)) return index } @@ -768,9 +768,9 @@ export const pushModifyPropHeader = ( header: ModifyPropHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.id)) - buf.pushU8(Number(header.type)) - buf.pushU32(Number(header.size)) + buf.pushUint8(Number(header.id)) + buf.pushUint8(Number(header.type)) + buf.pushUint32(Number(header.size)) return index } @@ -880,8 +880,8 @@ export const pushModifyReferencesHeader = ( header: ModifyReferencesHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU32(Number(header.size)) + buf.pushUint8(Number(header.op)) + buf.pushUint32(Number(header.size)) return index } @@ -995,13 +995,13 @@ export const pushModifyReferencesMetaHeader = ( header: ModifyReferencesMetaHeader, ): number => { const index = buf.length - buf.pushU32(Number(header.id)) - buf.pushU8(0) + buf.pushUint32(Number(header.id)) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= (((header.withIndex ? 1 : 0) >>> 0) & 1) << 1 buf.view[buf.length - 1] |= ((0 >>> 0) & 63) << 2 - buf.pushU32(Number(header.index)) - buf.pushU32(Number(header.size)) + buf.pushUint32(Number(header.index)) + buf.pushUint32(Number(header.size)) return index } @@ -1095,11 +1095,11 @@ export const pushModifyReferenceMetaHeader = ( header: ModifyReferenceMetaHeader, ): number => { const index = buf.length - buf.pushU32(Number(header.id)) - buf.pushU8(0) + buf.pushUint32(Number(header.id)) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 - buf.pushU32(Number(header.size)) + buf.pushUint32(Number(header.size)) return index } @@ -1182,10 +1182,10 @@ export const pushModifyCardinalityHeader = ( header: ModifyCardinalityHeader, ): number => { const index = buf.length - buf.pushU8(0) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.sparse ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 - buf.pushU8(Number(header.precision)) + buf.pushUint8(Number(header.precision)) return index } @@ -1262,8 +1262,8 @@ export const pushModifyResultItem = ( header: ModifyResultItem, ): number => { const index = buf.length - buf.pushU32(Number(header.id)) - buf.pushU8(Number(header.err)) + buf.pushUint32(Number(header.id)) + buf.pushUint8(Number(header.err)) return index } @@ -2246,13 +2246,13 @@ export const pushSortHeader = ( header: SortHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.order)) - buf.pushU8(Number(header.prop)) - buf.pushU8(Number(header.propType)) - buf.pushU16(Number(header.start)) - buf.pushU16(Number(header.len)) - buf.pushU8(Number(header.lang)) - buf.pushU16(Number(header.edgeType)) + buf.pushUint8(Number(header.order)) + buf.pushUint8(Number(header.prop)) + buf.pushUint8(Number(header.propType)) + buf.pushUint16(Number(header.start)) + buf.pushUint16(Number(header.len)) + buf.pushUint8(Number(header.lang)) + buf.pushUint16(Number(header.edgeType)) return index } @@ -2524,9 +2524,9 @@ export const pushIncludeHeader = ( header: IncludeHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.prop)) - buf.pushU8(Number(header.propType)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.prop)) + buf.pushUint8(Number(header.propType)) return index } @@ -2614,9 +2614,9 @@ export const pushIncludeMetaHeader = ( header: IncludeMetaHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.prop)) - buf.pushU8(Number(header.propType)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.prop)) + buf.pushUint8(Number(header.propType)) return index } @@ -2716,10 +2716,10 @@ export const pushIncludePartialHeader = ( header: IncludePartialHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.prop)) - buf.pushU8(Number(header.propType)) - buf.pushU16(Number(header.amount)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.prop)) + buf.pushUint8(Number(header.propType)) + buf.pushUint16(Number(header.amount)) return index } @@ -2796,8 +2796,8 @@ export const pushIncludePartialProp = ( header: IncludePartialProp, ): number => { const index = buf.length - buf.pushU16(Number(header.start)) - buf.pushU16(Number(header.size)) + buf.pushUint16(Number(header.start)) + buf.pushUint16(Number(header.size)) return index } @@ -2905,13 +2905,13 @@ export const pushIncludeOpts = ( header: IncludeOpts, ): number => { const index = buf.length - buf.pushU32(Number(header.end)) - buf.pushU8(0) + buf.pushUint32(Number(header.end)) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.isChars ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= (((header.hasOpts ? 1 : 0) >>> 0) & 1) << 1 buf.view[buf.length - 1] |= ((0 >>> 0) & 63) << 2 - buf.pushU8(Number(header.langFallbackSize)) - buf.pushU8(Number(header.lang)) + buf.pushUint8(Number(header.langFallbackSize)) + buf.pushUint8(Number(header.lang)) return index } @@ -2986,8 +2986,8 @@ export const pushIncludeResponse = ( header: IncludeResponse, ): number => { const index = buf.length - buf.pushU8(Number(header.prop)) - buf.pushU32(Number(header.size)) + buf.pushUint8(Number(header.prop)) + buf.pushUint32(Number(header.size)) return index } @@ -3107,14 +3107,14 @@ export const pushIncludeResponseMeta = ( header: IncludeResponseMeta, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.prop)) - buf.pushU8(Number(header.lang)) - buf.pushU8(0) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.prop)) + buf.pushUint8(Number(header.lang)) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.compressed ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 - buf.pushU32(Number(header.crc32)) - buf.pushU32(Number(header.size)) + buf.pushUint32(Number(header.crc32)) + buf.pushUint32(Number(header.size)) return index } @@ -3212,10 +3212,10 @@ export const pushSubscriptionHeader = ( header: SubscriptionHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU16(Number(header.typeId)) - buf.pushU8(Number(header.fieldsLen)) - buf.pushU8(Number(header.partialLen)) + buf.pushUint8(Number(header.op)) + buf.pushUint16(Number(header.typeId)) + buf.pushUint8(Number(header.fieldsLen)) + buf.pushUint8(Number(header.partialLen)) return index } @@ -3422,20 +3422,20 @@ export const pushQueryHeader = ( header: QueryHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.prop)) - buf.pushU16(Number(header.typeId)) - buf.pushU16(Number(header.edgeTypeId)) - buf.pushU32(Number(header.offset)) - buf.pushU32(Number(header.limit)) - buf.pushU16(Number(header.filterSize)) - buf.pushU16(Number(header.searchSize)) - buf.pushU16(Number(header.edgeSize)) - buf.pushU16(Number(header.edgeFilterSize)) - buf.pushU16(Number(header.includeSize)) - buf.pushU8(Number(header.iteratorType)) - buf.pushU16(Number(header.size)) - buf.pushU8(0) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.prop)) + buf.pushUint16(Number(header.typeId)) + buf.pushUint16(Number(header.edgeTypeId)) + buf.pushUint32(Number(header.offset)) + buf.pushUint32(Number(header.limit)) + buf.pushUint16(Number(header.filterSize)) + buf.pushUint16(Number(header.searchSize)) + buf.pushUint16(Number(header.edgeSize)) + buf.pushUint16(Number(header.edgeFilterSize)) + buf.pushUint16(Number(header.includeSize)) + buf.pushUint8(Number(header.iteratorType)) + buf.pushUint16(Number(header.size)) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.sort ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 return index @@ -3566,13 +3566,13 @@ export const pushQueryHeaderSingle = ( header: QueryHeaderSingle, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU16(Number(header.typeId)) - buf.pushU8(Number(header.prop)) - buf.pushU32(Number(header.id)) - buf.pushU16(Number(header.filterSize)) - buf.pushU16(Number(header.includeSize)) - buf.pushU16(Number(header.aliasSize)) + buf.pushUint8(Number(header.op)) + buf.pushUint16(Number(header.typeId)) + buf.pushUint8(Number(header.prop)) + buf.pushUint32(Number(header.id)) + buf.pushUint16(Number(header.filterSize)) + buf.pushUint16(Number(header.includeSize)) + buf.pushUint16(Number(header.aliasSize)) return index } @@ -3695,12 +3695,12 @@ export const pushQueryHeaderSingleReference = ( header: QueryHeaderSingleReference, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU8(Number(header.prop)) - buf.pushU16(Number(header.typeId)) - buf.pushU16(Number(header.edgeTypeId)) - buf.pushU16(Number(header.edgeSize)) - buf.pushU16(Number(header.includeSize)) + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.prop)) + buf.pushUint16(Number(header.typeId)) + buf.pushUint16(Number(header.edgeTypeId)) + buf.pushUint16(Number(header.edgeSize)) + buf.pushUint16(Number(header.includeSize)) return index } @@ -3918,16 +3918,16 @@ export const pushAggHeader = ( header: AggHeader, ): number => { const index = buf.length - buf.pushU8(Number(header.op)) - buf.pushU16(Number(header.typeId)) - buf.pushU32(Number(header.offset)) - buf.pushU32(Number(header.limit)) - buf.pushU16(Number(header.filterSize)) - buf.pushU8(Number(header.iteratorType)) - buf.pushU16(Number(header.size)) - buf.pushU16(Number(header.resultsSize)) - buf.pushU16(Number(header.accumulatorSize)) - buf.pushU8(0) + buf.pushUint8(Number(header.op)) + buf.pushUint16(Number(header.typeId)) + buf.pushUint32(Number(header.offset)) + buf.pushUint32(Number(header.limit)) + buf.pushUint16(Number(header.filterSize)) + buf.pushUint8(Number(header.iteratorType)) + buf.pushUint16(Number(header.size)) + buf.pushUint16(Number(header.resultsSize)) + buf.pushUint16(Number(header.accumulatorSize)) + buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.sort ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= (((header.hasGroupBy ? 1 : 0) >>> 0) & 1) << 1 buf.view[buf.length - 1] |= (((header.isSamplingSet ? 1 : 0) >>> 0) & 1) << 2 @@ -4002,7 +4002,7 @@ export const pushaddMultiSubscriptionHeader = ( header: addMultiSubscriptionHeader, ): number => { const index = buf.length - buf.pushU16(Number(header.typeId)) + buf.pushUint16(Number(header.typeId)) return index } @@ -4073,7 +4073,7 @@ export const pushremoveMultiSubscriptionHeader = ( header: removeMultiSubscriptionHeader, ): number => { const index = buf.length - buf.pushU16(Number(header.typeId)) + buf.pushUint16(Number(header.typeId)) return index } @@ -4189,12 +4189,12 @@ export const pushAggProp = ( header: AggProp, ): number => { const index = buf.length - buf.pushU8(Number(header.propId)) - buf.pushU8(Number(header.propType)) - buf.pushU16(Number(header.propDefStart)) - buf.pushU8(Number(header.aggFunction)) - buf.pushU16(Number(header.resultPos)) - buf.pushU16(Number(header.accumulatorPos)) + buf.pushUint8(Number(header.propId)) + buf.pushUint8(Number(header.propType)) + buf.pushUint16(Number(header.propDefStart)) + buf.pushUint8(Number(header.aggFunction)) + buf.pushUint16(Number(header.resultPos)) + buf.pushUint16(Number(header.accumulatorPos)) return index } @@ -4311,12 +4311,12 @@ export const pushGroupByKeyProp = ( header: GroupByKeyProp, ): number => { const index = buf.length - buf.pushU8(Number(header.propId)) - buf.pushU8(Number(header.propType)) - buf.pushU16(Number(header.propDefStart)) - buf.pushU8(Number(header.stepType)) - buf.pushU32(Number(header.stepRange)) - buf.pushU16(Number(header.timezone)) + buf.pushUint8(Number(header.propId)) + buf.pushUint8(Number(header.propType)) + buf.pushUint16(Number(header.propDefStart)) + buf.pushUint8(Number(header.stepType)) + buf.pushUint32(Number(header.stepRange)) + buf.pushUint16(Number(header.timezone)) return index } @@ -4478,8 +4478,8 @@ export const pushFilterOp = ( header: FilterOp, ): number => { const index = buf.length - buf.pushU8(Number(header.prop)) - buf.pushU8(Number(header.compare)) + buf.pushUint8(Number(header.prop)) + buf.pushUint8(Number(header.compare)) return index } @@ -4606,13 +4606,13 @@ export const pushFilterCondition = ( header: FilterCondition, ): number => { const index = buf.length - buf.pushU16(Number(packFilterOp(header.op))) - buf.pushU32(Number(header.size)) - buf.pushU8(Number(header.prop)) - buf.pushU16(Number(header.start)) - buf.pushU8(Number(header.len)) - buf.pushU64(header.fieldSchema) - buf.pushU8(Number(header.offset)) + buf.pushUint16(Number(packFilterOp(header.op))) + buf.pushUint32(Number(header.size)) + buf.pushUint8(Number(header.prop)) + buf.pushUint16(Number(header.start)) + buf.pushUint8(Number(header.len)) + buf.pushUint64(header.fieldSchema) + buf.pushUint8(Number(header.offset)) return index } @@ -4698,8 +4698,8 @@ export const pushFilterSelect = ( header: FilterSelect, ): number => { const index = buf.length - buf.pushU32(Number(header.size)) - buf.pushU64(header.typeEntry) - buf.pushU16(Number(header.typeId)) + buf.pushUint32(Number(header.size)) + buf.pushUint64(header.typeEntry) + buf.pushUint16(Number(header.typeId)) return index } From 27bc5ee3438b6f0e7a1b47e464e75258944014da Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 20:10:25 +0100 Subject: [PATCH 017/449] more --- src/db-client/index.ts | 59 ++---------------------------------------- 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index aa38f91de6..d8c2d0fca5 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -42,7 +42,6 @@ export class DbClient extends DbShared { }: DbClientOpts) { super() this.hooks = hooks - // this.maxModifySize = maxModifySize this.modifyCtx = { buf: new AutoSizedUint8Array(256, maxModifySize), flushTime, @@ -61,19 +60,8 @@ export class DbClient extends DbShared { subs = new Map() stopped!: boolean hooks: DbClientHooks - - // modify modifyCtx: ModifyCtx - // modify - // flushTime: number - // writeTime: number = 0 - // isDraining = false - // // maxModifySize: number - // modifyCtx: ModifyCtx - // upserting: Map; p: Promise }> = - // new Map() - async schemaIsSet() { if (!this.schema) { await this.once('schema') @@ -110,27 +98,14 @@ export class DbClient extends DbShared { this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, ) - // return modify( - // this.modifyCtx, - // serializeCreate, - // this.schema!, - // type, - // obj, - // this.modifyCtx.buf, - // opts?.locale ? LangCode[opts.locale] : LangCode.none, - // ) } update( type: string, - id: number, // | Promise, + id: number, obj = {}, opts?: ModifyOpts, ): Promise { - // if (id instanceof Promise) { - // id = await id - // } - return new ModifyCmd( this.modifyCtx, serializeUpdate, @@ -141,25 +116,9 @@ export class DbClient extends DbShared { this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, ) - // return modify( - // this.modifyCtx, - // serializeUpdate, - // this.schema!, - // type, - // id, - // obj, - // this.modifyCtx.buf, - // opts?.locale ? LangCode[opts.locale] : LangCode.none, - // ) } - delete( - type: string, - id: number, // | Promise - ) { - // if (id instanceof Promise) { - // id = await id - // } + delete(type: string, id: number) { return new ModifyCmd( this.modifyCtx, serializeDelete, @@ -169,16 +128,6 @@ export class DbClient extends DbShared { id, this.modifyCtx.buf, ) - - // return modify( - // this.modifyCtx, - // serializeDelete, - // this.schema!, - // type, - // // TODO make it perf - // id, - // this.modifyCtx.buf, - // ) } query( @@ -205,10 +154,6 @@ export class DbClient extends DbShared { return new BasedDbQuery(this, type, id as number | number[] | Uint32Array) } - // expire(type: string, id: number, seconds: number) { - // return expire(this, type, id, seconds) - // } - destroy() { this.stop() this.listeners = {} From dd1321cb5db55d8bf3b1e50b690e3fc872e37f33 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 21:19:57 +0100 Subject: [PATCH 018/449] db.setSchema = typedClient --- src/db-client/index.ts | 37 ++++++++++++++++++----------- src/db-client/modify/index.ts | 5 ++-- src/db-client/modify/types.ts | 29 +++++++++++++++++++--- src/schema/defs/props/references.ts | 11 +++++---- src/schema/schema/schema.ts | 16 +++++++++---- test/youzi.ts | 35 +++++++++++++-------------- 6 files changed, 86 insertions(+), 47 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index d8c2d0fca5..c743d44720 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -9,6 +9,8 @@ import { type SchemaIn, type SchemaMigrateFns, type SchemaOut, + type ResolveSchema, + type Schema, } from '../schema/index.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' import { LangCode } from '../zigTsExports.js' @@ -20,6 +22,7 @@ import { flush, ModifyCmd, } from './modify/index.js' +import type { InferPayload } from './modify/types.js' type DbClientOpts = { hooks: DbClientHooks @@ -33,7 +36,9 @@ export type ModifyOpts = { locale?: keyof typeof LangCode } -export class DbClient extends DbShared { +// ... imports + +export class DbClient = SchemaOut> extends DbShared { constructor({ hooks, maxModifySize = 100 * 1e3 * 1e3, @@ -68,27 +73,31 @@ export class DbClient extends DbShared { } } - async setSchema( - schema: SchemaIn, + async setSchema( + schema: T, transformFns?: SchemaMigrateFns, - ): Promise { - const strictSchema = parse(schema).schema + ): Promise>> { + const strictSchema = parse(schema as unknown as SchemaIn).schema await this.drain() const schemaChecksum = await this.hooks.setSchema( strictSchema as SchemaOut, transformFns, ) if (this.stopped) { - return this.schema?.hash ?? 0 + return this as unknown as DbClient> } if (schemaChecksum !== this.schema?.hash) { await this.once('schema') - return this.schema?.hash ?? 0 + return this as unknown as DbClient> } - return schemaChecksum + return this as unknown as DbClient> } - create(type: string, obj = {}, opts?: ModifyOpts): Promise { + create( + type: T, + obj: InferPayload[T], + opts?: ModifyOpts, + ): ModifyCmd { return new ModifyCmd( this.modifyCtx, serializeCreate, @@ -100,12 +109,12 @@ export class DbClient extends DbShared { ) } - update( - type: string, + update( + type: T, id: number, - obj = {}, + obj: InferPayload[T], opts?: ModifyOpts, - ): Promise { + ): ModifyCmd { return new ModifyCmd( this.modifyCtx, serializeUpdate, @@ -118,7 +127,7 @@ export class DbClient extends DbShared { ) } - delete(type: string, id: number) { + delete(type: keyof S['types'] & string, id: number): ModifyCmd { return new ModifyCmd( this.modifyCtx, serializeDelete, diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index a11db21ea6..e63583e33d 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -13,7 +13,6 @@ import { writeModifyUpdateHeaderProps, type LangCodeEnum, type ModifyEnum, - type ModifyErrorEnum, } from '../../zigTsExports.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import type { PropDef, PropTree } from '../../schema/defs/index.js' @@ -276,7 +275,8 @@ export class ModifyCmd if (e === AutoSizedUint8Array.ERR_OVERFLOW) { if (isEmpty) throw new Error('Range error') flush(ctx) - return this._exec.apply(this, arguments) + this._exec.apply(this, arguments) + return } else if (e instanceof ModifyCmd) { let blocker: ModifyCmd = e while (blocker._blocker) blocker = blocker._blocker @@ -284,6 +284,7 @@ export class ModifyCmd blocker._batch!.dependents.push(this) this._blocker = blocker this._arguments = arguments + return } else if (this._arguments) { // its in async mode this._error = e diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index c39a943f8e..1765fcaa7a 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -1,5 +1,7 @@ import { type SchemaTypes } from '../../schema.js' +import type { ModifyCmd } from './index.js' + type TypedArray = | Uint8Array | Float32Array @@ -31,6 +33,27 @@ type TypeMap = { cardinality: string | string[] } +type InferEdgeProps = { + [K in keyof Prop as K extends `$${string}` + ? K + : never]?: Prop[K] extends keyof TypeMap + ? TypeMap[Prop[K]] + : InferProp +} + +type InferRefValue = + | number + | ModifyCmd + | ({ id: number | ModifyCmd } & InferEdgeProps) + +type InferReferences = + | InferRefValue[] + | { + add?: InferRefValue[] + update?: InferRefValue[] + delete?: (number | ModifyCmd)[] + } + type InferProp = Prop extends { type: 'object'; props: infer P } ? InferType : Prop extends { type: infer T extends keyof TypeMap } @@ -38,9 +61,9 @@ type InferProp = Prop extends { type: 'object'; props: infer P } : Prop extends { enum: infer E extends readonly any[] } ? E[number] : Prop extends { ref: string } - ? string | number + ? InferRefValue : Prop extends { items: { ref: string } } - ? (string | number)[] + ? InferReferences : never type InferType = { @@ -53,6 +76,6 @@ type InferType = { : K]?: InferProp } -export type InferPayload> = { +export type InferPayload> = { [K in keyof Types]: InferType } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 08036655ef..1eefebb830 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -122,9 +122,11 @@ const getTmpId = (item: any) => { if (item instanceof ModifyCmd) return item.tmpId } -const isValidRefObj = (item: any) => - (typeof item === 'object' && item !== null && getRealId(item.id)) || - getTmpId(item.id) !== undefined +const isValidRefObj = (item: any) => { + if (typeof item === 'object' && item !== null) { + return getRealId(item.id) || getTmpId(item.id) !== undefined + } +} const setReferences = ( buf: AutoSizedUint8Array, @@ -162,9 +164,10 @@ const setReferences = ( writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) } else if (item instanceof ModifyCmd) { throw item - } else if (item.id instanceof ModifyCmd) { + } else if (typeof item === 'object' && item?.id instanceof ModifyCmd) { throw item.id } else { + console.log('??', item, value) throw 'bad ref!' } } diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index 51efc4a3bc..cc4450f4bf 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -49,7 +49,11 @@ type NormalizeProp = T extends string : T extends { items: infer I } ? Omit & { type: 'references'; items: NormalizeProp } : T extends { ref: string } - ? T & { type: 'reference' } + ? { + [K in keyof T]: K extends `$${string}` + ? NormalizeProp + : T[K] + } & { type: 'reference' } : T extends { enum: any[] } ? T & { type: 'enum' } : T @@ -58,13 +62,15 @@ type NormalizeType = T extends { props: infer P } ? Omit & { props: { [K in keyof P]: NormalizeProp } } : { props: { [K in keyof T]: NormalizeProp } } -type ResolveSchema = Omit & { - hash: number - locales: SchemaLocales +export type ResolveSchema = Omit< + SchemaOut, + 'types' | 'locales' +> & { types: { [K in keyof S['types']]: NormalizeType } -} & SchemaOut + locales: SchemaLocales +} const isMigrations = (v: unknown): v is SchemaMigrations => isRecord(v) && diff --git a/test/youzi.ts b/test/youzi.ts index 682ff95c7d..2766a36984 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -9,7 +9,7 @@ import { parseSchema } from '../src/schema.js' import { LangCode, Modify, pushModifyHeader } from '../src/zigTsExports.js' import test from './shared/test.js' -await test.skip('schema defs', async (t) => { +await test('schema defs', async (t) => { const schema = parseSchema({ types: { user: { @@ -121,12 +121,10 @@ await test('modify client', async (t) => { t.after(() => t.backup(db)) - await db.setSchema({ + // When using setSchema, the return value is a typed client + const client = await db.setSchema({ types: { user: { - // age: 'number', - // rating: 'uint8', - // TODO refs have to be ordered friends: { items: { ref: 'user', @@ -144,44 +142,43 @@ await test('modify client', async (t) => { }, }) - const youzi = db.create('user', { + const youzi = client.create('user', { name: 'youzi', }) - // olli uses TMPID for youzi - const olli = db.create('user', { + // olli uses ModifyCmd for youzi + const olli = client.create('user', { name: 'olli', - friends: [youzi], + friends: { add: [youzi] }, friend: youzi, }) // youzi is now in-flight flush(db.client.modifyCtx) - // james WILL BE QUEUED until youzi is done -> because we need that reference - const jamez = db.create('user', { + const jamez = client.create('user', { name: 'jamez', friend: { id: youzi, $rating: 10 }, }) - const marco = db.create('user', { + const marco = client.create('user', { name: 'mr marco', friends: [youzi], }) - jamez.then(() => { - const fulco = db + jamez.then((jamezId) => { + const fulco = client .create('user', { name: 'mr fulco', - friends: [jamez], - friend: jamez, + friends: [jamezId], + friend: jamezId, }) .then(() => { - const tom = db + const tom = client .create('user', { name: 'mr tom', - friends: [jamez], - friend: jamez, + friends: [jamezId], + friend: jamezId, }) .then() }) From bffa37b10dd5ea43ea957fd230e7b89c1dff56a5 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 28 Jan 2026 21:24:10 +0100 Subject: [PATCH 019/449] wip --- test/include/thread.perf.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/include/thread.perf.ts b/test/include/thread.perf.ts index dc4a703cfd..e3760c07d3 100644 --- a/test/include/thread.perf.ts +++ b/test/include/thread.perf.ts @@ -18,7 +18,7 @@ await test('include', async (t) => { // single ref + edge - await db.setSchema({ + const client = await db.setSchema({ locales: { en: true, de: { fallback: ['en'] }, @@ -46,28 +46,28 @@ await test('include', async (t) => { let d = Date.now() for (let i = 0; i < 5e6; i++) { - db.create('simple', { + client.create('simple', { nr: 67, // name: i % 2 ? 'b' : 'a', // nr: rand(0, 10), }) } - await db.drain() + await client.drain() let time = Date.now() - d console.log('create 5M', time, 'ms', (1000 / time) * 5e6, 'OPS per second') d = Date.now() for (let i = 0; i < 5e6; i++) { - db.update('simple', i + 1, { + client.update('simple', i + 1, { nr: 67, // name: i % 2 ? 'b' : 'a', // nr: rand(0, 10), }) } - await db.drain() + await client.drain() time = Date.now() - d @@ -75,7 +75,7 @@ await test('include', async (t) => { let q: any = [] - const x = db.query('simple', 1) + const x = client.query('simple', 1) registerQuery(x) @@ -95,7 +95,7 @@ await test('include', async (t) => { for (let i = 0; i < 9; i++) { //.range(0, 1) q.push( - db + client .query('simple') .range(0, 5e6 + i) // .include('id') @@ -117,27 +117,27 @@ await test('include', async (t) => { d = Date.now() for (let i = 0; i < 5e6; i++) { - db.delete('simple', i + 1) + client.delete('simple', i + 1) } - await db.drain() + await client.drain() time = Date.now() - d console.log('DEL 5M', time, 'ms', (1000 / time) * 5e6, 'OPS per second') - const simple = await db.create('simple', { + const simple = await client.create('simple', { nr: 1001, }) - const simple2 = await db.create('simple', { + const simple2 = await client.create('simple', { nr: 1002, }) d = Date.now() for (let i = 0; i < 10e6; i++) { - db.create('simple', { + client.create('simple', { nr: 67, start: d + i * 1e3, end: d + i * 1e3 + 10e3, @@ -163,7 +163,7 @@ await test('include', async (t) => { const q: any[] = [] for (let i = 0; i < 5; i++) { q.push( - db + client .query('simple') .include('id', 'nr') // .range(0, 10e6 + i) @@ -182,7 +182,7 @@ await test('include', async (t) => { const q: any[] = [] for (let i = 0; i < 5; i++) { q.push( - db + client .query('simple') .include('id', 'nr') // .range(0, 10e6 + i) @@ -196,13 +196,13 @@ await test('include', async (t) => { { repeat: 100 }, ) - db.create('simple', { + client.create('simple', { nr: 100, // name: i % 2 ? 'b' : 'a', // nr: rand(0, 10), }) - await db + await client .query('simple') .include('nr') // 'start', 'end', 'target' .filter('target.nr', '>', 1001) From 3d2440607f5a2d60ecde1ffb83c6539e0dc70dec Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 08:07:19 +0100 Subject: [PATCH 020/449] fix --- scripts/zigTsExports.ts | 36 +- src/db-client/query/BasedDbQuery.ts | 1 + src/db-client/query/toByteCode/toByteCode.ts | 3 +- src/db-query/BasedDbQuery.ts | 27 + src/db-query/BasedDbQueryResult.ts | 19 + src/db-query/ast.ts | 108 ++ src/db-query/toByteCode/iteratorType.ts | 79 ++ src/db-query/toByteCode/multiple.ts | 33 + src/db-query/toByteCode/toByteCode.ts | 42 + src/schema/utils.ts | 100 ++ src/utils/AutoSizedUint8Array.ts | 9 + src/zigTsExports.ts | 1179 +++++------------- test/query-ast/include.ts | 27 + 13 files changed, 755 insertions(+), 908 deletions(-) create mode 100644 src/db-query/BasedDbQuery.ts create mode 100644 src/db-query/BasedDbQueryResult.ts create mode 100644 src/db-query/ast.ts create mode 100644 src/db-query/toByteCode/iteratorType.ts create mode 100644 src/db-query/toByteCode/multiple.ts create mode 100644 src/db-query/toByteCode/toByteCode.ts create mode 100644 src/schema/utils.ts create mode 100644 test/query-ast/include.ts diff --git a/scripts/zigTsExports.ts b/scripts/zigTsExports.ts index 72bd7b8c70..d36412e5ae 100644 --- a/scripts/zigTsExports.ts +++ b/scripts/zigTsExports.ts @@ -386,14 +386,10 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ structs[name] = { isPacked, bitSize: totalBits } - if (isPacked) { - // Generate pack/unpack helpers for this packed struct (BigInt based for >32 bit support) - // Pack: takes Object -> returns bigint - // Unpack: takes bigint -> returns Object - + if (isPacked && totalBits <= 32) { // Pack - output += `export const pack${name} = (obj: ${name}): bigint => {\n` - output += ` let val = 0n\n` + output += `export const pack${name} = (obj: ${name}): number => {\n` + output += ` let val = 0\n` let currentBit = 0 fields.forEach((f) => { if (f.isPadding) { @@ -402,22 +398,22 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ } let valExpr = `obj.${f.name}` if (f.isBoolean) { - valExpr = `(${valExpr} ? 1n : 0n)` + valExpr = `(${valExpr} ? 1 : 0)` } else if (f.isStruct) { valExpr = `pack${f.type}(${valExpr})` } else { - // Cast to BigInt - valExpr = `BigInt(${valExpr})` + valExpr = `Number(${valExpr})` } - output += ` val |= (${valExpr} & ${(1n << BigInt(f.bitSize)) - 1n}n) << ${currentBit}n\n` + const mask = f.bitSize === 32 ? -1 : (1 << f.bitSize) - 1 + output += ` val |= (${valExpr} & ${mask}) << ${currentBit}\n` currentBit += f.bitSize }) output += ` return val\n` output += `}\n\n` // Unpack - output += `export const unpack${name} = (val: bigint): ${name} => {\n` + output += `export const unpack${name} = (val: number): ${name} => {\n` output += ` return {\n` currentBit = 0 fields.forEach((f) => { @@ -426,18 +422,20 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ return } - let readExpr = `(val >> ${currentBit}n) & ${(1n << BigInt(f.bitSize)) - 1n}n` + const mask = f.bitSize === 32 ? -1 : (1 << f.bitSize) - 1 + let readExpr = `(val >>> ${currentBit}) & ${mask}` if (f.isBoolean) { - readExpr = `(${readExpr}) === 1n` + readExpr = `(${readExpr}) === 1` } else if (f.isStruct) { readExpr = `unpack${f.type}(${readExpr})` } else { - readExpr = `Number(${readExpr})` if (f.type.endsWith('Enum')) { readExpr = `(${readExpr}) as ${f.type}` } else if (f.type === 'TypeId') { readExpr = `(${readExpr}) as TypeId` + } else { + readExpr = `Number(${readExpr})` } } @@ -750,7 +748,7 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ else if (sBits <= 32) readExpr = `readUint32(buf, ${offStr})` else readExpr = `readUint64(buf, ${offStr})` - readExpr = `unpack${prim}(BigInt(${readExpr}))` + readExpr = `unpack${prim}(${readExpr})` } else { switch (prim) { case 'u8': @@ -860,7 +858,7 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ } if (f.isStruct) { - readExpr = `unpack${f.type}(BigInt(${readExpr}))` + readExpr = `unpack${f.type}(${readExpr})` } else if (f.type.endsWith('Enum')) { readExpr = `(${readExpr}) as ${f.type}` } else if (f.type === 'TypeId') { @@ -904,7 +902,7 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ else if (sBits <= 32) readExpr = `readUint32(buf, ${offStr})` else readExpr = `readUint64(buf, ${offStr})` - readExpr = `unpack${prim}(BigInt(${readExpr}))` + readExpr = `unpack${prim}(${readExpr})` } else { switch (prim) { case 'u8': @@ -1012,7 +1010,7 @@ import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js'\ } if (f.isStruct) { - readExpr = `unpack${f.type}(BigInt(${readExpr}))` + readExpr = `unpack${f.type}(${readExpr})` } else if (f.type.endsWith('Enum')) { readExpr = `(${readExpr}) as ${f.type}` } else if (f.type === 'TypeId') { diff --git a/src/db-client/query/BasedDbQuery.ts b/src/db-client/query/BasedDbQuery.ts index 0304c43b07..0a24e93b14 100644 --- a/src/db-client/query/BasedDbQuery.ts +++ b/src/db-client/query/BasedDbQuery.ts @@ -264,6 +264,7 @@ export class QueryBranch { groupBy(field: string, step?: StepInput): T { if (this.queryCommands) { + // query def this.queryCommands.push({ method: 'groupBy', args: [field, step], diff --git a/src/db-client/query/toByteCode/toByteCode.ts b/src/db-client/query/toByteCode/toByteCode.ts index 9b548e1c59..9a9b59d041 100644 --- a/src/db-client/query/toByteCode/toByteCode.ts +++ b/src/db-client/query/toByteCode/toByteCode.ts @@ -232,12 +232,13 @@ export const queryToBuffer = (query: BasedDbQuery) => { const def = query.def! const bufs = defToBuffer(query.db, def) bufs.push(schemaChecksum(def)) - const queryIdSize = 4 + const queryIdSize = 4 // prob want 8 here... const totalByteLength = byteSize(bufs) + queryIdSize const res = new Uint8Array(totalByteLength) const queryIdTarget = new Uint8Array(4) bufs.unshift(queryIdTarget) combineIntermediateResults(res, 0, bufs) + // maybe make these ids 8 bytes seems to short... const queryId = crc32(res) writeUint32(res, queryId, 0) // debugBuffer(res) diff --git a/src/db-query/BasedDbQuery.ts b/src/db-query/BasedDbQuery.ts new file mode 100644 index 0000000000..ed3044bd8e --- /dev/null +++ b/src/db-query/BasedDbQuery.ts @@ -0,0 +1,27 @@ +// QUERY TIME +// you need to pass hooks + +// basedClient.db +// toJSON() based DB query -> .ast +// toObject() +// new BasedDbQuery(type, target?, { +//. subscribeSchema +// subscribe +// get +// }?) + +// const b = new BasedDbQuery('user').include('name') +// console.log(b.ast) + +// { +// query () { + +//} +//} query(type, ddf) +// db.query('type', target?) => new BasedDbQuery(type, target, this.hooks) + +// client.query('ui-query', dbQuery('user', 1).include('x').filter('x', '>', 10)) <-- ast +// db.query(DBQUERY-AST).subscribe() +// .include('name')) + +// const x = await db.query('user').include('name').get() diff --git a/src/db-query/BasedDbQueryResult.ts b/src/db-query/BasedDbQueryResult.ts new file mode 100644 index 0000000000..ed12a7cb72 --- /dev/null +++ b/src/db-query/BasedDbQueryResult.ts @@ -0,0 +1,19 @@ +// const r = new BasedQueryResult(READ_SCHEMA, ?result) +// r.update(result) +// r.inspect() + +// node getter +// BASED-RESPONSE +// { data, error, checksum } +// .data -> +// for (const node of response) {} +// .map +// .id + +// length +// map +// iterator + +/* +

{useQuery().map(v =>
{v.title}
)}
+*/ diff --git a/src/db-query/ast.ts b/src/db-query/ast.ts new file mode 100644 index 0000000000..34ce8f1190 --- /dev/null +++ b/src/db-query/ast.ts @@ -0,0 +1,108 @@ +export type FilterOp = { + op: '=' | '<' | '>' | '..' | 'includes' | 'exists' | 'exist' + val?: any +} + +export type FilterAst = { + props?: { + [key: string]: FilterAst & { + ops?: FilterOp[] + select?: { start: number; end: number } + } + } + or?: FilterAst[] + and?: FilterAst[] +} + +export type QueryAst = { + locale?: string + type?: string + target?: string | number | (number | string)[] // '[id]' + filter?: FilterAst + sort?: { prop: string; order: 'asc' | 'desc' } + props?: Record< + string, + QueryAst & { + include?: { + glob?: '*' | '**' + meta?: true | 'only' | false + maxChars?: number + maxBytes?: number + raw?: boolean + } + select?: { start: number; end: number } + } + > +} + +type QueryAstNode = FilterAst | QueryAst + +// const x: QueryAst = { +// type: 'user', +// locale: 'nl', +// filter: { +// props: { +// articlesWritten: { +// ops: [{ op: '>', val: 5 }], +// }, +// }, +// }, +// props: { +// articles: { +// props: { +// readBy: { +// filter: { +// props: { +// $rating: { +// ops: [{ val: 4, op: '>' }], +// }, +// }, +// }, +// props: { +// name: { include: { meta: 'only' } }, +// email: { include: {} }, +// }, +// }, +// }, +// }, +// }, +// } + +/* +db.query('user') + .locale('nl') + .filter('articlesWritten', '>', 5) + .include((select) => + select('articles.readBy') + .filter('$rating', '>', 4) + .include('name', 'email', { meta: 'only' }), + ) + */ + +// query('user').include('address', 'address.city.**') + +// { +// props: { +// address: { +// opts: { include: '*' }, +// props: { +// city: { +// include: '**' +// } +// } +// } +// } +// } + +// { +// props: { +// address: { +// include: '*', +// props: { +// city: { +// include: '*' +// } +// } +// } +// } +// } diff --git a/src/db-query/toByteCode/iteratorType.ts b/src/db-query/toByteCode/iteratorType.ts new file mode 100644 index 0000000000..42e245c761 --- /dev/null +++ b/src/db-query/toByteCode/iteratorType.ts @@ -0,0 +1,79 @@ +import { + ID_PROP, + QUERY_ITERATOR_DEFAULT, + QUERY_ITERATOR_EDGE, + QUERY_ITERATOR_EDGE_INCLUDE, + QUERY_ITERATOR_SEARCH, + QUERY_ITERATOR_SEARCH_VEC, + QueryIteratorTypeEnum, + Order, +} from '../../zigTsExports.js' +import { QueryDef, QueryDefType } from '../../db-client/query/types.js' + +export const getIteratorType = (): QueryIteratorTypeEnum => { + const hasFilter: boolean = false + const hasSearch = false //def.search?.size && def.search.size > 0 + const isVector = false // hasSearch && def.search!.isVector + // const hasFilter = def.filter.size > 0 + const isDesc = false // def.order === Order.desc + const edgeInclude = false // def.edges + const hasSort = false // + // def.sort && + // (def.sort.prop !== ID_PROP || def.type === QueryDefType.References) + const hasEdges = false + // def.type === QueryDefType.References && + // @ts-ignore + // def.target.propDef.edgeNodeTypeId > 0 + + let base = QUERY_ITERATOR_DEFAULT + + if (hasEdges && !edgeInclude) { + base = QUERY_ITERATOR_EDGE + } + + if (edgeInclude) { + base = QUERY_ITERATOR_EDGE_INCLUDE + } + + if (hasSearch && !isVector) { + base = QUERY_ITERATOR_SEARCH + } + + if (hasSearch && isVector) { + base = QUERY_ITERATOR_SEARCH_VEC + } + + if (hasSearch) { + if (hasFilter) { + base += 1 + } + } else { + if (hasSort) { + if (hasFilter) { + if (isDesc) { + base += 7 + } else { + base += 3 + } + } else if (isDesc) { + base += 5 + } else { + base += 1 + } + } else { + if (hasFilter) { + if (isDesc) { + base += 6 + } else { + base += 2 + } + } else if (isDesc) { + base += 4 + } else { + base += 0 + } + } + } + + return base as QueryIteratorTypeEnum +} diff --git a/src/db-query/toByteCode/multiple.ts b/src/db-query/toByteCode/multiple.ts new file mode 100644 index 0000000000..8089581c8e --- /dev/null +++ b/src/db-query/toByteCode/multiple.ts @@ -0,0 +1,33 @@ +import { PropDef, TypeDef } from '../../schema/defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { pushQueryHeader, QueryType, ID_PROP } from '../../zigTsExports.js' +import { QueryAst } from '../ast.js' +import { getIteratorType } from './iteratorType.js' + +export const multiple = ( + ast: QueryAst, + buf: AutoSizedUint8Array, + typeDef: TypeDef, + prop?: PropDef, +) => { + let startOffset = buf.length + + console.log('start here', startOffset) + + // const queryHeaderOffset = pushQueryHeader(buf, { + // op, + // prop: isReferences ? def.target.propDef!.prop : ID_PROP, + // includeSize, + // typeId, + // offset: def.range.offset, + // limit: def.range.limit, + // sort: hasSort, + // filterSize, + // searchSize, + // iteratorType: getIteratorType(), + // edgeTypeId, + // edgeSize, + // edgeFilterSize: 0, // this is nice + // size: buffer.byteLength + includeSize, + // }) +} diff --git a/src/db-query/toByteCode/toByteCode.ts b/src/db-query/toByteCode/toByteCode.ts new file mode 100644 index 0000000000..2c690eb2f7 --- /dev/null +++ b/src/db-query/toByteCode/toByteCode.ts @@ -0,0 +1,42 @@ +import { crc32 } from '../../db-client/crc32.js' +import { SchemaOut } from '../../schema.js' +import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { QueryAst } from '../ast.js' +import { multiple } from './multiple.js' + +export const queryAstToByteCode = ( + schema: SchemaOut, + ast: QueryAst, +): Uint8Array => { + // 8 for schema checksum, 4 for query id, and QueryHeader + + if (!ast.type) { + throw new Error('Query requires type') + } + + const typeDefs = getTypeDefs(schema) + const typeDef = typeDefs.get(ast.type) + + if (!typeDef) { + throw new Error('Type does not exist') + } + + const buf = new AutoSizedUint8Array(100) + + const queryIdPos = buf.reserveU32() + + if (!ast.target) { + multiple(ast, buf, typeDef) + } + + // const checksumPos = buf.reserveU64() + buf.pushU64(schema.hash) + // buf.setU64(schema.hash, checksumPos) + const queryId = crc32(buf.view) + buf.setU32(queryId, queryIdPos) + + // buf.pack() + + return buf.view +} diff --git a/src/schema/utils.ts b/src/schema/utils.ts new file mode 100644 index 0000000000..bc033ee978 --- /dev/null +++ b/src/schema/utils.ts @@ -0,0 +1,100 @@ +// writer.$role +// snurf +// writer.* +// writer.** +// writer.flap[0] +// writer.flap[-1] +// writer. + +// writer + opts +// * +// ** + +// getPropsFromPaths(['writer.description.nl'], schema, type, langCode) +// { +// def: PropDef +// resultPath: ['writer', 'description', 'nl'] +// lang: langcode +// select: false +// }[] + +// getPropsFromPaths(['writer.flap[0..5]'], schema, type, langCode) +// { +// def: PropDef +// resultPath: ['writer', 'flap'] +// lang: langcode +// opts: { start: 10, end: 14 } +// }[] + +/* + + + .query('user') + .filter('nr', '>', 8) + .and('nr' '<', 10) + .and(filter => filter('nr', >, 5).or('nr', <, 10) ) + .and('flap', '>', 10) + .and('writer.$role', '=', 'admin') + .or(() => { + filter('nr', '<', 1).or('nr', '=', 5) + }) + +{ + type: 'user', + // target: 21 // [21,21,23] + + and: [ + + props: { + nr: { ops: [ + { val: 8, op: '>' }, + { val: 10, op: '<' } + ]}, + writer: { + props: { + $role: { + ops: [ + {val: 'admin', op: ''} +]} + }, + ops: [{ val: 23, op: '='}] + }, +} + { + props: { nr: { ops: [{val: 1, op: '<}] } }, + or: [{ nr: { val: 5, op: '='}}] + }], + or: [{ + props: { nr: [{ val: 1, op: '<'}] } + or: [{ + nr: [{ val: 5, op: '='}] + }], + }] + } + locale: 'en', + sort: { prop: 'nr', order: 'asc' }, + include: { + props: { + writer: { + props: { + '*': { opts: {} }, + $rating: { opts: {} }, + + } + }, + likes: { + select: { start: 2, end: 10 } + }, + flap: { + props: {}, + filter: ... + } + } + } + // agg? +} + +// validateQuery() + + +*/ diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index c2e02d93f2..b77fa2e3c7 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -227,6 +227,15 @@ export class AutoSizedUint8Array { if (requiredEnd > this.length) this.length = requiredEnd } + setU64(value: number, offset: number): void { + const requiredEnd = offset + 8 + if (requiredEnd > this.data.length) { + this.ensureCapacity(requiredEnd) + } + writeUint64(this.data, value, offset) + if (requiredEnd > this.length) this.length = requiredEnd + } + reserveU32(): number { const index = this.length const requiredEnd = index + 4 diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 2fdd7db70a..a5d5688eba 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1,22 +1,14 @@ -import { - writeUint16, - writeInt16, - writeUint32, - writeInt32, - writeUint64, - writeInt64, - writeFloatLE, - writeDoubleLE, - readUint16, - readInt16, - readUint32, - readInt32, - readUint64, - readInt64, - readFloatLE, - readDoubleLE, +import { + writeUint16, writeInt16, + writeUint32, writeInt32, + writeUint64, writeInt64, + writeFloatLE, writeDoubleLE, + readUint16, readInt16, + readUint32, readInt32, + readUint64, readInt64, + readFloatLE, readDoubleLE } from './utils/index.js' -import { AutoSizedUint8Array } from './utils/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from './db-client/modify/AutoSizedUint8Array.js' export type TypeId = number @@ -40,8 +32,7 @@ export const BridgeResponseInverse = { flushQuery, flushModify */ -export type BridgeResponseEnum = - (typeof BridgeResponse)[keyof typeof BridgeResponse] +export type BridgeResponseEnum = (typeof BridgeResponse)[keyof typeof BridgeResponse] export const OpType = { id: 0, @@ -231,24 +222,6 @@ export const ModifyHeaderByteSize = 17 export const ModifyHeaderAlignOf = 16 -export const packModifyHeader = (obj: ModifyHeader): bigint => { - let val = 0n - val |= (BigInt(obj.opId) & 4294967295n) << 0n - val |= (BigInt(obj.opType) & 255n) << 32n - val |= (BigInt(obj.schema) & 18446744073709551615n) << 40n - val |= (BigInt(obj.count) & 4294967295n) << 104n - return val -} - -export const unpackModifyHeader = (val: bigint): ModifyHeader => { - return { - opId: Number((val >> 0n) & 4294967295n), - opType: Number((val >> 32n) & 255n) as OpTypeEnum, - schema: Number((val >> 40n) & 18446744073709551615n), - count: Number((val >> 104n) & 4294967295n), - } -} - export const writeModifyHeader = ( buf: Uint8Array, header: ModifyHeader, @@ -286,7 +259,7 @@ export const readModifyHeader = ( ): ModifyHeader => { const value: ModifyHeader = { opId: readUint32(buf, offset), - opType: buf[offset + 4] as OpTypeEnum, + opType: (buf[offset + 4]) as OpTypeEnum, schema: readUint64(buf, offset + 5), count: readUint32(buf, offset + 13), } @@ -294,10 +267,10 @@ export const readModifyHeader = ( } export const readModifyHeaderProps = { - opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - opType: (buf: Uint8Array, offset: number) => buf[offset + 4] as OpTypeEnum, - schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), - count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), + opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + opType: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as OpTypeEnum, + schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), + count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), } export const createModifyHeader = (header: ModifyHeader): Uint8Array => { @@ -329,24 +302,6 @@ export const ModifyUpdateHeaderByteSize = 10 export const ModifyUpdateHeaderAlignOf = 16 -export const packModifyUpdateHeader = (obj: ModifyUpdateHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.type) & 255n) << 8n - val |= (BigInt(obj.id) & 4294967295n) << 16n - val |= (BigInt(obj.size) & 4294967295n) << 48n - return val -} - -export const unpackModifyUpdateHeader = (val: bigint): ModifyUpdateHeader => { - return { - op: Number((val >> 0n) & 255n) as ModifyEnum, - type: Number((val >> 8n) & 255n), - id: Number((val >> 16n) & 4294967295n), - size: Number((val >> 48n) & 4294967295n), - } -} - export const writeModifyUpdateHeader = ( buf: Uint8Array, header: ModifyUpdateHeader, @@ -383,7 +338,7 @@ export const readModifyUpdateHeader = ( offset: number, ): ModifyUpdateHeader => { const value: ModifyUpdateHeader = { - op: buf[offset] as ModifyEnum, + op: (buf[offset]) as ModifyEnum, type: buf[offset + 1], id: readUint32(buf, offset + 2), size: readUint32(buf, offset + 6), @@ -392,15 +347,13 @@ export const readModifyUpdateHeader = ( } export const readModifyUpdateHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), } -export const createModifyUpdateHeader = ( - header: ModifyUpdateHeader, -): Uint8Array => { +export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { const buffer = new Uint8Array(ModifyUpdateHeaderByteSize) writeModifyUpdateHeader(buffer, header, 0) return buffer @@ -428,22 +381,6 @@ export const ModifyDeleteHeaderByteSize = 6 export const ModifyDeleteHeaderAlignOf = 8 -export const packModifyDeleteHeader = (obj: ModifyDeleteHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.type) & 255n) << 8n - val |= (BigInt(obj.id) & 4294967295n) << 16n - return val -} - -export const unpackModifyDeleteHeader = (val: bigint): ModifyDeleteHeader => { - return { - op: Number((val >> 0n) & 255n) as ModifyEnum, - type: Number((val >> 8n) & 255n), - id: Number((val >> 16n) & 4294967295n), - } -} - export const writeModifyDeleteHeader = ( buf: Uint8Array, header: ModifyDeleteHeader, @@ -475,7 +412,7 @@ export const readModifyDeleteHeader = ( offset: number, ): ModifyDeleteHeader => { const value: ModifyDeleteHeader = { - op: buf[offset] as ModifyEnum, + op: (buf[offset]) as ModifyEnum, type: buf[offset + 1], id: readUint32(buf, offset + 2), } @@ -483,14 +420,12 @@ export const readModifyDeleteHeader = ( } export const readModifyDeleteHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyDeleteHeader = ( - header: ModifyDeleteHeader, -): Uint8Array => { +export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { const buffer = new Uint8Array(ModifyDeleteHeaderByteSize) writeModifyDeleteHeader(buffer, header, 0) return buffer @@ -517,22 +452,6 @@ export const ModifyCreateHeaderByteSize = 6 export const ModifyCreateHeaderAlignOf = 8 -export const packModifyCreateHeader = (obj: ModifyCreateHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.type) & 255n) << 8n - val |= (BigInt(obj.size) & 4294967295n) << 16n - return val -} - -export const unpackModifyCreateHeader = (val: bigint): ModifyCreateHeader => { - return { - op: Number((val >> 0n) & 255n) as ModifyEnum, - type: Number((val >> 8n) & 255n), - size: Number((val >> 16n) & 4294967295n), - } -} - export const writeModifyCreateHeader = ( buf: Uint8Array, header: ModifyCreateHeader, @@ -564,7 +483,7 @@ export const readModifyCreateHeader = ( offset: number, ): ModifyCreateHeader => { const value: ModifyCreateHeader = { - op: buf[offset] as ModifyEnum, + op: (buf[offset]) as ModifyEnum, type: buf[offset + 1], size: readUint32(buf, offset + 2), } @@ -572,14 +491,12 @@ export const readModifyCreateHeader = ( } export const readModifyCreateHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyCreateHeader = ( - header: ModifyCreateHeader, -): Uint8Array => { +export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCreateHeaderByteSize) writeModifyCreateHeader(buffer, header, 0) return buffer @@ -606,22 +523,6 @@ export const ModifyMainHeaderByteSize = 5 export const ModifyMainHeaderAlignOf = 8 -export const packModifyMainHeader = (obj: ModifyMainHeader): bigint => { - let val = 0n - val |= (BigInt(obj.id) & 255n) << 0n - val |= (BigInt(obj.start) & 65535n) << 8n - val |= (BigInt(obj.size) & 65535n) << 24n - return val -} - -export const unpackModifyMainHeader = (val: bigint): ModifyMainHeader => { - return { - id: Number((val >> 0n) & 255n), - start: Number((val >> 8n) & 65535n), - size: Number((val >> 24n) & 65535n), - } -} - export const writeModifyMainHeader = ( buf: Uint8Array, header: ModifyMainHeader, @@ -661,14 +562,12 @@ export const readModifyMainHeader = ( } export const readModifyMainHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + id: (buf: Uint8Array, offset: number) => buf[offset], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createModifyMainHeader = ( - header: ModifyMainHeader, -): Uint8Array => { +export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { const buffer = new Uint8Array(ModifyMainHeaderByteSize) writeModifyMainHeader(buffer, header, 0) return buffer @@ -695,22 +594,6 @@ export const ModifyPropHeaderByteSize = 6 export const ModifyPropHeaderAlignOf = 8 -export const packModifyPropHeader = (obj: ModifyPropHeader): bigint => { - let val = 0n - val |= (BigInt(obj.id) & 255n) << 0n - val |= (BigInt(obj.type) & 255n) << 8n - val |= (BigInt(obj.size) & 4294967295n) << 16n - return val -} - -export const unpackModifyPropHeader = (val: bigint): ModifyPropHeader => { - return { - id: Number((val >> 0n) & 255n), - type: Number((val >> 8n) & 255n) as PropTypeEnum, - size: Number((val >> 16n) & 4294967295n), - } -} - export const writeModifyPropHeader = ( buf: Uint8Array, header: ModifyPropHeader, @@ -743,21 +626,19 @@ export const readModifyPropHeader = ( ): ModifyPropHeader => { const value: ModifyPropHeader = { id: buf[offset], - type: buf[offset + 1] as PropTypeEnum, + type: (buf[offset + 1]) as PropTypeEnum, size: readUint32(buf, offset + 2), } return value } export const readModifyPropHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyPropHeader = ( - header: ModifyPropHeader, -): Uint8Array => { +export const createModifyPropHeader = (header: ModifyPropHeader): Uint8Array => { const buffer = new Uint8Array(ModifyPropHeaderByteSize) writeModifyPropHeader(buffer, header, 0) return buffer @@ -800,8 +681,7 @@ export const ModifyReferencesInverse = { delIds, delTmpIds */ -export type ModifyReferencesEnum = - (typeof ModifyReferences)[keyof typeof ModifyReferences] +export type ModifyReferencesEnum = (typeof ModifyReferences)[keyof typeof ModifyReferences] export type ModifyReferencesHeader = { op: ModifyReferencesEnum @@ -812,24 +692,6 @@ export const ModifyReferencesHeaderByteSize = 5 export const ModifyReferencesHeaderAlignOf = 8 -export const packModifyReferencesHeader = ( - obj: ModifyReferencesHeader, -): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.size) & 4294967295n) << 8n - return val -} - -export const unpackModifyReferencesHeader = ( - val: bigint, -): ModifyReferencesHeader => { - return { - op: Number((val >> 0n) & 255n) as ModifyReferencesEnum, - size: Number((val >> 8n) & 4294967295n), - } -} - export const writeModifyReferencesHeader = ( buf: Uint8Array, header: ModifyReferencesHeader, @@ -856,20 +718,18 @@ export const readModifyReferencesHeader = ( offset: number, ): ModifyReferencesHeader => { const value: ModifyReferencesHeader = { - op: buf[offset] as ModifyReferencesEnum, + op: (buf[offset]) as ModifyReferencesEnum, size: readUint32(buf, offset + 1), } return value } export const readModifyReferencesHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyReferencesEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyReferencesEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } -export const createModifyReferencesHeader = ( - header: ModifyReferencesHeader, -): Uint8Array => { +export const createModifyReferencesHeader = (header: ModifyReferencesHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesHeaderByteSize) writeModifyReferencesHeader(buffer, header, 0) return buffer @@ -897,30 +757,6 @@ export const ModifyReferencesMetaHeaderByteSize = 13 export const ModifyReferencesMetaHeaderAlignOf = 16 -export const packModifyReferencesMetaHeader = ( - obj: ModifyReferencesMetaHeader, -): bigint => { - let val = 0n - val |= (BigInt(obj.id) & 4294967295n) << 0n - val |= ((obj.isTmp ? 1n : 0n) & 1n) << 32n - val |= ((obj.withIndex ? 1n : 0n) & 1n) << 33n - val |= (BigInt(obj.index) & 4294967295n) << 40n - val |= (BigInt(obj.size) & 4294967295n) << 72n - return val -} - -export const unpackModifyReferencesMetaHeader = ( - val: bigint, -): ModifyReferencesMetaHeader => { - return { - id: Number((val >> 0n) & 4294967295n), - isTmp: ((val >> 32n) & 1n) === 1n, - withIndex: ((val >> 33n) & 1n) === 1n, - index: Number((val >> 40n) & 4294967295n), - size: Number((val >> 72n) & 4294967295n), - } -} - export const writeModifyReferencesMetaHeader = ( buf: Uint8Array, header: ModifyReferencesMetaHeader, @@ -964,8 +800,8 @@ export const readModifyReferencesMetaHeader = ( ): ModifyReferencesMetaHeader => { const value: ModifyReferencesMetaHeader = { id: readUint32(buf, offset), - isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, - withIndex: ((buf[offset + 4] >>> 1) & 1) === 1, + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (((buf[offset + 4] >>> 1) & 1)) === 1, index: readUint32(buf, offset + 5), size: readUint32(buf, offset + 9), } @@ -973,18 +809,14 @@ export const readModifyReferencesMetaHeader = ( } export const readModifyReferencesMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - withIndex: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 1) & 1) === 1, - index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), } -export const createModifyReferencesMetaHeader = ( - header: ModifyReferencesMetaHeader, -): Uint8Array => { +export const createModifyReferencesMetaHeader = (header: ModifyReferencesMetaHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesMetaHeaderByteSize) writeModifyReferencesMetaHeader(buffer, header, 0) return buffer @@ -1015,26 +847,6 @@ export const ModifyReferenceMetaHeaderByteSize = 9 export const ModifyReferenceMetaHeaderAlignOf = 16 -export const packModifyReferenceMetaHeader = ( - obj: ModifyReferenceMetaHeader, -): bigint => { - let val = 0n - val |= (BigInt(obj.id) & 4294967295n) << 0n - val |= ((obj.isTmp ? 1n : 0n) & 1n) << 32n - val |= (BigInt(obj.size) & 4294967295n) << 40n - return val -} - -export const unpackModifyReferenceMetaHeader = ( - val: bigint, -): ModifyReferenceMetaHeader => { - return { - id: Number((val >> 0n) & 4294967295n), - isTmp: ((val >> 32n) & 1n) === 1n, - size: Number((val >> 40n) & 4294967295n), - } -} - export const writeModifyReferenceMetaHeader = ( buf: Uint8Array, header: ModifyReferenceMetaHeader, @@ -1069,22 +881,19 @@ export const readModifyReferenceMetaHeader = ( ): ModifyReferenceMetaHeader => { const value: ModifyReferenceMetaHeader = { id: readUint32(buf, offset), - isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, size: readUint32(buf, offset + 5), } return value } export const readModifyReferenceMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), } -export const createModifyReferenceMetaHeader = ( - header: ModifyReferenceMetaHeader, -): Uint8Array => { +export const createModifyReferenceMetaHeader = (header: ModifyReferenceMetaHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferenceMetaHeaderByteSize) writeModifyReferenceMetaHeader(buffer, header, 0) return buffer @@ -1112,21 +921,17 @@ export const ModifyCardinalityHeaderByteSize = 2 export const ModifyCardinalityHeaderAlignOf = 2 -export const packModifyCardinalityHeader = ( - obj: ModifyCardinalityHeader, -): bigint => { - let val = 0n - val |= ((obj.sparse ? 1n : 0n) & 1n) << 0n - val |= (BigInt(obj.precision) & 255n) << 8n +export const packModifyCardinalityHeader = (obj: ModifyCardinalityHeader): number => { + let val = 0 + val |= ((obj.sparse ? 1 : 0) & 1) << 0 + val |= (Number(obj.precision) & 255) << 8 return val } -export const unpackModifyCardinalityHeader = ( - val: bigint, -): ModifyCardinalityHeader => { +export const unpackModifyCardinalityHeader = (val: number): ModifyCardinalityHeader => { return { - sparse: ((val >> 0n) & 1n) === 1n, - precision: Number((val >> 8n) & 255n), + sparse: ((val >>> 0) & 1) === 1, + precision: Number((val >>> 8) & 255), } } @@ -1158,20 +963,18 @@ export const readModifyCardinalityHeader = ( offset: number, ): ModifyCardinalityHeader => { const value: ModifyCardinalityHeader = { - sparse: ((buf[offset] >>> 0) & 1) === 1, + sparse: (((buf[offset] >>> 0) & 1)) === 1, precision: buf[offset + 1], } return value } export const readModifyCardinalityHeaderProps = { - sparse: (buf: Uint8Array, offset: number) => ((buf[offset] >>> 0) & 1) === 1, - precision: (buf: Uint8Array, offset: number) => buf[offset + 1], + sparse: (buf: Uint8Array, offset: number) => (((buf[offset] >>> 0) & 1)) === 1, + precision: (buf: Uint8Array, offset: number) => buf[offset + 1], } -export const createModifyCardinalityHeader = ( - header: ModifyCardinalityHeader, -): Uint8Array => { +export const createModifyCardinalityHeader = (header: ModifyCardinalityHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCardinalityHeaderByteSize) writeModifyCardinalityHeader(buffer, header, 0) return buffer @@ -1198,20 +1001,6 @@ export const ModifyResultItemByteSize = 5 export const ModifyResultItemAlignOf = 8 -export const packModifyResultItem = (obj: ModifyResultItem): bigint => { - let val = 0n - val |= (BigInt(obj.id) & 4294967295n) << 0n - val |= (BigInt(obj.err) & 255n) << 32n - return val -} - -export const unpackModifyResultItem = (val: bigint): ModifyResultItem => { - return { - id: Number((val >> 0n) & 4294967295n), - err: Number((val >> 32n) & 255n) as ModifyErrorEnum, - } -} - export const writeModifyResultItem = ( buf: Uint8Array, header: ModifyResultItem, @@ -1239,19 +1028,17 @@ export const readModifyResultItem = ( ): ModifyResultItem => { const value: ModifyResultItem = { id: readUint32(buf, offset), - err: buf[offset + 4] as ModifyErrorEnum, + err: (buf[offset + 4]) as ModifyErrorEnum, } return value } export const readModifyResultItemProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - err: (buf: Uint8Array, offset: number) => buf[offset + 4] as ModifyErrorEnum, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + err: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyErrorEnum, } -export const createModifyResultItem = ( - header: ModifyResultItem, -): Uint8Array => { +export const createModifyResultItem = (header: ModifyResultItem): Uint8Array => { const buffer = new Uint8Array(ModifyResultItemByteSize) writeModifyResultItem(buffer, header, 0) return buffer @@ -1453,8 +1240,7 @@ export const ReferencesSelectInverse = { any, all */ -export type ReferencesSelectEnum = - (typeof ReferencesSelect)[keyof typeof ReferencesSelect] +export type ReferencesSelectEnum = (typeof ReferencesSelect)[keyof typeof ReferencesSelect] export const RefEdgeOp = { noEdgeNoIndexRealId: 0, @@ -2141,30 +1927,6 @@ export const SortHeaderByteSize = 10 export const SortHeaderAlignOf = 16 -export const packSortHeader = (obj: SortHeader): bigint => { - let val = 0n - val |= (BigInt(obj.order) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.propType) & 255n) << 16n - val |= (BigInt(obj.start) & 65535n) << 24n - val |= (BigInt(obj.len) & 65535n) << 40n - val |= (BigInt(obj.lang) & 255n) << 56n - val |= (BigInt(obj.edgeType) & 65535n) << 64n - return val -} - -export const unpackSortHeader = (val: bigint): SortHeader => { - return { - order: Number((val >> 0n) & 255n) as OrderEnum, - prop: Number((val >> 8n) & 255n), - propType: Number((val >> 16n) & 255n) as PropTypeEnum, - start: Number((val >> 24n) & 65535n), - len: Number((val >> 40n) & 65535n), - lang: Number((val >> 56n) & 255n) as LangCodeEnum, - edgeType: Number((val >> 64n) & 65535n), - } -} - export const writeSortHeader = ( buf: Uint8Array, header: SortHeader, @@ -2211,28 +1973,30 @@ export const writeSortHeaderProps = { }, } -export const readSortHeader = (buf: Uint8Array, offset: number): SortHeader => { +export const readSortHeader = ( + buf: Uint8Array, + offset: number, +): SortHeader => { const value: SortHeader = { - order: buf[offset] as OrderEnum, + order: (buf[offset]) as OrderEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, start: readUint16(buf, offset + 3), len: readUint16(buf, offset + 5), - lang: buf[offset + 7] as LangCodeEnum, + lang: (buf[offset + 7]) as LangCodeEnum, edgeType: readUint16(buf, offset + 8), } return value } export const readSortHeaderProps = { - order: (buf: Uint8Array, offset: number) => buf[offset] as OrderEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - lang: (buf: Uint8Array, offset: number) => buf[offset + 7] as LangCodeEnum, - edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + order: (buf: Uint8Array, offset: number) => (buf[offset]) as OrderEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + lang: (buf: Uint8Array, offset: number) => (buf[offset + 7]) as LangCodeEnum, + edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } export const createSortHeader = (header: SortHeader): Uint8Array => { @@ -2353,8 +2117,7 @@ export const QueryIteratorTypeInverse = { vec, vecFilter */ -export type QueryIteratorTypeEnum = - (typeof QueryIteratorType)[keyof typeof QueryIteratorType] +export type QueryIteratorTypeEnum = (typeof QueryIteratorType)[keyof typeof QueryIteratorType] export const QueryType = { id: 0, @@ -2452,19 +2215,19 @@ export const IncludeHeaderByteSize = 3 export const IncludeHeaderAlignOf = 4 -export const packIncludeHeader = (obj: IncludeHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.propType) & 255n) << 16n +export const packIncludeHeader = (obj: IncludeHeader): number => { + let val = 0 + val |= (Number(obj.op) & 255) << 0 + val |= (Number(obj.prop) & 255) << 8 + val |= (Number(obj.propType) & 255) << 16 return val } -export const unpackIncludeHeader = (val: bigint): IncludeHeader => { +export const unpackIncludeHeader = (val: number): IncludeHeader => { return { - op: Number((val >> 0n) & 255n) as IncludeOpEnum, - prop: Number((val >> 8n) & 255n), - propType: Number((val >> 16n) & 255n) as PropTypeEnum, + op: ((val >>> 0) & 255) as IncludeOpEnum, + prop: Number((val >>> 8) & 255), + propType: ((val >>> 16) & 255) as PropTypeEnum, } } @@ -2499,18 +2262,17 @@ export const readIncludeHeader = ( offset: number, ): IncludeHeader => { const value: IncludeHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, } return value } export const readIncludeHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, } export const createIncludeHeader = (header: IncludeHeader): Uint8Array => { @@ -2540,19 +2302,19 @@ export const IncludeMetaHeaderByteSize = 3 export const IncludeMetaHeaderAlignOf = 4 -export const packIncludeMetaHeader = (obj: IncludeMetaHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.propType) & 255n) << 16n +export const packIncludeMetaHeader = (obj: IncludeMetaHeader): number => { + let val = 0 + val |= (Number(obj.op) & 255) << 0 + val |= (Number(obj.prop) & 255) << 8 + val |= (Number(obj.propType) & 255) << 16 return val } -export const unpackIncludeMetaHeader = (val: bigint): IncludeMetaHeader => { +export const unpackIncludeMetaHeader = (val: number): IncludeMetaHeader => { return { - op: Number((val >> 0n) & 255n) as IncludeOpEnum, - prop: Number((val >> 8n) & 255n), - propType: Number((val >> 16n) & 255n) as PropTypeEnum, + op: ((val >>> 0) & 255) as IncludeOpEnum, + prop: Number((val >>> 8) & 255), + propType: ((val >>> 16) & 255) as PropTypeEnum, } } @@ -2587,23 +2349,20 @@ export const readIncludeMetaHeader = ( offset: number, ): IncludeMetaHeader => { const value: IncludeMetaHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, } return value } export const readIncludeMetaHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, } -export const createIncludeMetaHeader = ( - header: IncludeMetaHeader, -): Uint8Array => { +export const createIncludeMetaHeader = (header: IncludeMetaHeader): Uint8Array => { const buffer = new Uint8Array(IncludeMetaHeaderByteSize) writeIncludeMetaHeader(buffer, header, 0) return buffer @@ -2631,26 +2390,6 @@ export const IncludePartialHeaderByteSize = 5 export const IncludePartialHeaderAlignOf = 8 -export const packIncludePartialHeader = (obj: IncludePartialHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.propType) & 255n) << 16n - val |= (BigInt(obj.amount) & 65535n) << 24n - return val -} - -export const unpackIncludePartialHeader = ( - val: bigint, -): IncludePartialHeader => { - return { - op: Number((val >> 0n) & 255n) as IncludeOpEnum, - prop: Number((val >> 8n) & 255n), - propType: Number((val >> 16n) & 255n) as PropTypeEnum, - amount: Number((val >> 24n) & 65535n), - } -} - export const writeIncludePartialHeader = ( buf: Uint8Array, header: IncludePartialHeader, @@ -2687,25 +2426,22 @@ export const readIncludePartialHeader = ( offset: number, ): IncludePartialHeader => { const value: IncludePartialHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, amount: readUint16(buf, offset + 3), } return value } export const readIncludePartialHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, - amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createIncludePartialHeader = ( - header: IncludePartialHeader, -): Uint8Array => { +export const createIncludePartialHeader = (header: IncludePartialHeader): Uint8Array => { const buffer = new Uint8Array(IncludePartialHeaderByteSize) writeIncludePartialHeader(buffer, header, 0) return buffer @@ -2732,17 +2468,17 @@ export const IncludePartialPropByteSize = 4 export const IncludePartialPropAlignOf = 4 -export const packIncludePartialProp = (obj: IncludePartialProp): bigint => { - let val = 0n - val |= (BigInt(obj.start) & 65535n) << 0n - val |= (BigInt(obj.size) & 65535n) << 16n +export const packIncludePartialProp = (obj: IncludePartialProp): number => { + let val = 0 + val |= (Number(obj.start) & 65535) << 0 + val |= (Number(obj.size) & 65535) << 16 return val } -export const unpackIncludePartialProp = (val: bigint): IncludePartialProp => { +export const unpackIncludePartialProp = (val: number): IncludePartialProp => { return { - start: Number((val >> 0n) & 65535n), - size: Number((val >> 16n) & 65535n), + start: Number((val >>> 0) & 65535), + size: Number((val >>> 16) & 65535), } } @@ -2779,13 +2515,11 @@ export const readIncludePartialProp = ( } export const readIncludePartialPropProps = { - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), } -export const createIncludePartialProp = ( - header: IncludePartialProp, -): Uint8Array => { +export const createIncludePartialProp = (header: IncludePartialProp): Uint8Array => { const buffer = new Uint8Array(IncludePartialPropByteSize) writeIncludePartialProp(buffer, header, 0) return buffer @@ -2813,26 +2547,6 @@ export const IncludeOptsByteSize = 7 export const IncludeOptsAlignOf = 8 -export const packIncludeOpts = (obj: IncludeOpts): bigint => { - let val = 0n - val |= (BigInt(obj.end) & 4294967295n) << 0n - val |= ((obj.isChars ? 1n : 0n) & 1n) << 32n - val |= ((obj.hasOpts ? 1n : 0n) & 1n) << 33n - val |= (BigInt(obj.langFallbackSize) & 255n) << 40n - val |= (BigInt(obj.lang) & 255n) << 48n - return val -} - -export const unpackIncludeOpts = (val: bigint): IncludeOpts => { - return { - end: Number((val >> 0n) & 4294967295n), - isChars: ((val >> 32n) & 1n) === 1n, - hasOpts: ((val >> 33n) & 1n) === 1n, - langFallbackSize: Number((val >> 40n) & 255n), - lang: Number((val >> 48n) & 255n) as LangCodeEnum, - } -} - export const writeIncludeOpts = ( buf: Uint8Array, header: IncludeOpts, @@ -2876,22 +2590,20 @@ export const readIncludeOpts = ( ): IncludeOpts => { const value: IncludeOpts = { end: readUint32(buf, offset), - isChars: ((buf[offset + 4] >>> 0) & 1) === 1, - hasOpts: ((buf[offset + 4] >>> 1) & 1) === 1, + isChars: (((buf[offset + 4] >>> 0) & 1)) === 1, + hasOpts: (((buf[offset + 4] >>> 1) & 1)) === 1, langFallbackSize: buf[offset + 5], - lang: buf[offset + 6] as LangCodeEnum, + lang: (buf[offset + 6]) as LangCodeEnum, } return value } export const readIncludeOptsProps = { - end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isChars: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - hasOpts: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 1) & 1) === 1, - langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], - lang: (buf: Uint8Array, offset: number) => buf[offset + 6] as LangCodeEnum, + end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isChars: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + hasOpts: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], + lang: (buf: Uint8Array, offset: number) => (buf[offset + 6]) as LangCodeEnum, } export const createIncludeOpts = (header: IncludeOpts): Uint8Array => { @@ -2924,20 +2636,6 @@ export const IncludeResponseByteSize = 5 export const IncludeResponseAlignOf = 8 -export const packIncludeResponse = (obj: IncludeResponse): bigint => { - let val = 0n - val |= (BigInt(obj.prop) & 255n) << 0n - val |= (BigInt(obj.size) & 4294967295n) << 8n - return val -} - -export const unpackIncludeResponse = (val: bigint): IncludeResponse => { - return { - prop: Number((val >> 0n) & 255n), - size: Number((val >> 8n) & 4294967295n), - } -} - export const writeIncludeResponse = ( buf: Uint8Array, header: IncludeResponse, @@ -2971,8 +2669,8 @@ export const readIncludeResponse = ( } export const readIncludeResponseProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + prop: (buf: Uint8Array, offset: number) => buf[offset], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } export const createIncludeResponse = (header: IncludeResponse): Uint8Array => { @@ -3004,28 +2702,6 @@ export const IncludeResponseMetaByteSize = 12 export const IncludeResponseMetaAlignOf = 16 -export const packIncludeResponseMeta = (obj: IncludeResponseMeta): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.lang) & 255n) << 16n - val |= ((obj.compressed ? 1n : 0n) & 1n) << 24n - val |= (BigInt(obj.crc32) & 4294967295n) << 32n - val |= (BigInt(obj.size) & 4294967295n) << 64n - return val -} - -export const unpackIncludeResponseMeta = (val: bigint): IncludeResponseMeta => { - return { - op: Number((val >> 0n) & 255n) as ReadOpEnum, - prop: Number((val >> 8n) & 255n), - lang: Number((val >> 16n) & 255n) as LangCodeEnum, - compressed: ((val >> 24n) & 1n) === 1n, - crc32: Number((val >> 32n) & 4294967295n), - size: Number((val >> 64n) & 4294967295n), - } -} - export const writeIncludeResponseMeta = ( buf: Uint8Array, header: IncludeResponseMeta, @@ -3074,10 +2750,10 @@ export const readIncludeResponseMeta = ( offset: number, ): IncludeResponseMeta => { const value: IncludeResponseMeta = { - op: buf[offset] as ReadOpEnum, + op: (buf[offset]) as ReadOpEnum, prop: buf[offset + 1], - lang: buf[offset + 2] as LangCodeEnum, - compressed: ((buf[offset + 3] >>> 0) & 1) === 1, + lang: (buf[offset + 2]) as LangCodeEnum, + compressed: (((buf[offset + 3] >>> 0) & 1)) === 1, crc32: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -3085,18 +2761,15 @@ export const readIncludeResponseMeta = ( } export const readIncludeResponseMetaProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ReadOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - lang: (buf: Uint8Array, offset: number) => buf[offset + 2] as LangCodeEnum, - compressed: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ReadOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + lang: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as LangCodeEnum, + compressed: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createIncludeResponseMeta = ( - header: IncludeResponseMeta, -): Uint8Array => { +export const createIncludeResponseMeta = (header: IncludeResponseMeta): Uint8Array => { const buffer = new Uint8Array(IncludeResponseMetaByteSize) writeIncludeResponseMeta(buffer, header, 0) return buffer @@ -3129,24 +2802,6 @@ export const SubscriptionHeaderByteSize = 5 export const SubscriptionHeaderAlignOf = 8 -export const packSubscriptionHeader = (obj: SubscriptionHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.typeId) & 65535n) << 8n - val |= (BigInt(obj.fieldsLen) & 255n) << 24n - val |= (BigInt(obj.partialLen) & 255n) << 32n - return val -} - -export const unpackSubscriptionHeader = (val: bigint): SubscriptionHeader => { - return { - op: Number((val >> 0n) & 255n) as OpTypeEnum, - typeId: Number((val >> 8n) & 65535n) as TypeId, - fieldsLen: Number((val >> 24n) & 255n), - partialLen: Number((val >> 32n) & 255n), - } -} - export const writeSubscriptionHeader = ( buf: Uint8Array, header: SubscriptionHeader, @@ -3183,8 +2838,8 @@ export const readSubscriptionHeader = ( offset: number, ): SubscriptionHeader => { const value: SubscriptionHeader = { - op: buf[offset] as OpTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as OpTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, fieldsLen: buf[offset + 3], partialLen: buf[offset + 4], } @@ -3192,16 +2847,13 @@ export const readSubscriptionHeader = ( } export const readSubscriptionHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as OpTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], - partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], + op: (buf: Uint8Array, offset: number) => (buf[offset]) as OpTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], + partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], } -export const createSubscriptionHeader = ( - header: SubscriptionHeader, -): Uint8Array => { +export const createSubscriptionHeader = (header: SubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(SubscriptionHeaderByteSize) writeSubscriptionHeader(buffer, header, 0) return buffer @@ -3240,44 +2892,6 @@ export const QueryHeaderByteSize = 28 export const QueryHeaderAlignOf = 16 -export const packQueryHeader = (obj: QueryHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.typeId) & 65535n) << 16n - val |= (BigInt(obj.edgeTypeId) & 65535n) << 32n - val |= (BigInt(obj.offset) & 4294967295n) << 48n - val |= (BigInt(obj.limit) & 4294967295n) << 80n - val |= (BigInt(obj.filterSize) & 65535n) << 112n - val |= (BigInt(obj.searchSize) & 65535n) << 128n - val |= (BigInt(obj.edgeSize) & 65535n) << 144n - val |= (BigInt(obj.edgeFilterSize) & 65535n) << 160n - val |= (BigInt(obj.includeSize) & 65535n) << 176n - val |= (BigInt(obj.iteratorType) & 255n) << 192n - val |= (BigInt(obj.size) & 65535n) << 200n - val |= ((obj.sort ? 1n : 0n) & 1n) << 216n - return val -} - -export const unpackQueryHeader = (val: bigint): QueryHeader => { - return { - op: Number((val >> 0n) & 255n) as QueryTypeEnum, - prop: Number((val >> 8n) & 255n), - typeId: Number((val >> 16n) & 65535n) as TypeId, - edgeTypeId: Number((val >> 32n) & 65535n) as TypeId, - offset: Number((val >> 48n) & 4294967295n), - limit: Number((val >> 80n) & 4294967295n), - filterSize: Number((val >> 112n) & 65535n), - searchSize: Number((val >> 128n) & 65535n), - edgeSize: Number((val >> 144n) & 65535n), - edgeFilterSize: Number((val >> 160n) & 65535n), - includeSize: Number((val >> 176n) & 65535n), - iteratorType: Number((val >> 192n) & 255n) as QueryIteratorTypeEnum, - size: Number((val >> 200n) & 65535n), - sort: ((val >> 216n) & 1n) === 1n, - } -} - export const writeQueryHeader = ( buf: Uint8Array, header: QueryHeader, @@ -3350,11 +2964,7 @@ export const writeQueryHeaderProps = { includeSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 22) }, - iteratorType: ( - buf: Uint8Array, - value: QueryIteratorTypeEnum, - offset: number, - ) => { + iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { buf[offset + 24] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3370,10 +2980,10 @@ export const readQueryHeader = ( offset: number, ): QueryHeader => { const value: QueryHeader = { - op: buf[offset] as QueryTypeEnum, + op: (buf[offset]) as QueryTypeEnum, prop: buf[offset + 1], - typeId: readUint16(buf, offset + 2) as TypeId, - edgeTypeId: readUint16(buf, offset + 4) as TypeId, + typeId: (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, offset: readUint32(buf, offset + 6), limit: readUint32(buf, offset + 10), filterSize: readUint16(buf, offset + 14), @@ -3381,34 +2991,28 @@ export const readQueryHeader = ( edgeSize: readUint16(buf, offset + 18), edgeFilterSize: readUint16(buf, offset + 20), includeSize: readUint16(buf, offset + 22), - iteratorType: buf[offset + 24] as QueryIteratorTypeEnum, + iteratorType: (buf[offset + 24]) as QueryIteratorTypeEnum, size: readUint16(buf, offset + 25), - sort: ((buf[offset + 27] >>> 0) & 1) === 1, + sort: (((buf[offset + 27] >>> 0) & 1)) === 1, } return value } export const readQueryHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 4) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), - edgeFilterSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 20), - includeSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 22), - iteratorType: (buf: Uint8Array, offset: number) => - buf[offset + 24] as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), - sort: (buf: Uint8Array, offset: number) => - ((buf[offset + 27] >>> 0) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), + edgeFilterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 20), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 22), + iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 24]) as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), + sort: (buf: Uint8Array, offset: number) => (((buf[offset + 27] >>> 0) & 1)) === 1, } export const createQueryHeader = (header: QueryHeader): Uint8Array => { @@ -3455,30 +3059,6 @@ export const QueryHeaderSingleByteSize = 14 export const QueryHeaderSingleAlignOf = 16 -export const packQueryHeaderSingle = (obj: QueryHeaderSingle): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.typeId) & 65535n) << 8n - val |= (BigInt(obj.prop) & 255n) << 24n - val |= (BigInt(obj.id) & 4294967295n) << 32n - val |= (BigInt(obj.filterSize) & 65535n) << 64n - val |= (BigInt(obj.includeSize) & 65535n) << 80n - val |= (BigInt(obj.aliasSize) & 65535n) << 96n - return val -} - -export const unpackQueryHeaderSingle = (val: bigint): QueryHeaderSingle => { - return { - op: Number((val >> 0n) & 255n) as QueryTypeEnum, - typeId: Number((val >> 8n) & 65535n) as TypeId, - prop: Number((val >> 24n) & 255n), - id: Number((val >> 32n) & 4294967295n), - filterSize: Number((val >> 64n) & 65535n), - includeSize: Number((val >> 80n) & 65535n), - aliasSize: Number((val >> 96n) & 65535n), - } -} - export const writeQueryHeaderSingle = ( buf: Uint8Array, header: QueryHeaderSingle, @@ -3530,8 +3110,8 @@ export const readQueryHeaderSingle = ( offset: number, ): QueryHeaderSingle => { const value: QueryHeaderSingle = { - op: buf[offset] as QueryTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as QueryTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, prop: buf[offset + 3], id: readUint32(buf, offset + 4), filterSize: readUint16(buf, offset + 8), @@ -3542,20 +3122,16 @@ export const readQueryHeaderSingle = ( } export const readQueryHeaderSingleProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - prop: (buf: Uint8Array, offset: number) => buf[offset + 3], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - includeSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 10), - aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), -} - -export const createQueryHeaderSingle = ( - header: QueryHeaderSingle, -): Uint8Array => { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + prop: (buf: Uint8Array, offset: number) => buf[offset + 3], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), + aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), +} + +export const createQueryHeaderSingle = (header: QueryHeaderSingle): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleByteSize) writeQueryHeaderSingle(buffer, header, 0) return buffer @@ -3589,32 +3165,6 @@ export const QueryHeaderSingleReferenceByteSize = 10 export const QueryHeaderSingleReferenceAlignOf = 16 -export const packQueryHeaderSingleReference = ( - obj: QueryHeaderSingleReference, -): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.prop) & 255n) << 8n - val |= (BigInt(obj.typeId) & 65535n) << 16n - val |= (BigInt(obj.edgeTypeId) & 65535n) << 32n - val |= (BigInt(obj.edgeSize) & 65535n) << 48n - val |= (BigInt(obj.includeSize) & 65535n) << 64n - return val -} - -export const unpackQueryHeaderSingleReference = ( - val: bigint, -): QueryHeaderSingleReference => { - return { - op: Number((val >> 0n) & 255n) as QueryTypeEnum, - prop: Number((val >> 8n) & 255n), - typeId: Number((val >> 16n) & 65535n) as TypeId, - edgeTypeId: Number((val >> 32n) & 65535n) as TypeId, - edgeSize: Number((val >> 48n) & 65535n), - includeSize: Number((val >> 64n) & 65535n), - } -} - export const writeQueryHeaderSingleReference = ( buf: Uint8Array, header: QueryHeaderSingleReference, @@ -3661,10 +3211,10 @@ export const readQueryHeaderSingleReference = ( offset: number, ): QueryHeaderSingleReference => { const value: QueryHeaderSingleReference = { - op: buf[offset] as QueryTypeEnum, + op: (buf[offset]) as QueryTypeEnum, prop: buf[offset + 1], - typeId: readUint16(buf, offset + 2) as TypeId, - edgeTypeId: readUint16(buf, offset + 4) as TypeId, + typeId: (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, edgeSize: readUint16(buf, offset + 6), includeSize: readUint16(buf, offset + 8), } @@ -3672,19 +3222,15 @@ export const readQueryHeaderSingleReference = ( } export const readQueryHeaderSingleReferenceProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 4) as TypeId, - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } -export const createQueryHeaderSingleReference = ( - header: QueryHeaderSingleReference, -): Uint8Array => { +export const createQueryHeaderSingleReference = (header: QueryHeaderSingleReference): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleReferenceByteSize) writeQueryHeaderSingleReference(buffer, header, 0) return buffer @@ -3736,8 +3282,7 @@ export const VectorBaseTypeInverse = { float32, float64 */ -export type VectorBaseTypeEnum = - (typeof VectorBaseType)[keyof typeof VectorBaseType] +export type VectorBaseTypeEnum = (typeof VectorBaseType)[keyof typeof VectorBaseType] export type AggHeader = { op: QueryTypeEnum @@ -3758,40 +3303,6 @@ export const AggHeaderByteSize = 21 export const AggHeaderAlignOf = 16 -export const packAggHeader = (obj: AggHeader): bigint => { - let val = 0n - val |= (BigInt(obj.op) & 255n) << 0n - val |= (BigInt(obj.typeId) & 65535n) << 8n - val |= (BigInt(obj.offset) & 4294967295n) << 24n - val |= (BigInt(obj.limit) & 4294967295n) << 56n - val |= (BigInt(obj.filterSize) & 65535n) << 88n - val |= (BigInt(obj.iteratorType) & 255n) << 104n - val |= (BigInt(obj.size) & 65535n) << 112n - val |= (BigInt(obj.resultsSize) & 65535n) << 128n - val |= (BigInt(obj.accumulatorSize) & 65535n) << 144n - val |= ((obj.sort ? 1n : 0n) & 1n) << 160n - val |= ((obj.hasGroupBy ? 1n : 0n) & 1n) << 161n - val |= ((obj.isSamplingSet ? 1n : 0n) & 1n) << 162n - return val -} - -export const unpackAggHeader = (val: bigint): AggHeader => { - return { - op: Number((val >> 0n) & 255n) as QueryTypeEnum, - typeId: Number((val >> 8n) & 65535n) as TypeId, - offset: Number((val >> 24n) & 4294967295n), - limit: Number((val >> 56n) & 4294967295n), - filterSize: Number((val >> 88n) & 65535n), - iteratorType: Number((val >> 104n) & 255n) as QueryIteratorTypeEnum, - size: Number((val >> 112n) & 65535n), - resultsSize: Number((val >> 128n) & 65535n), - accumulatorSize: Number((val >> 144n) & 65535n), - sort: ((val >> 160n) & 1n) === 1n, - hasGroupBy: ((val >> 161n) & 1n) === 1n, - isSamplingSet: ((val >> 162n) & 1n) === 1n, - } -} - export const writeAggHeader = ( buf: Uint8Array, header: AggHeader, @@ -3840,11 +3351,7 @@ export const writeAggHeaderProps = { filterSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 11) }, - iteratorType: ( - buf: Uint8Array, - value: QueryIteratorTypeEnum, - offset: number, - ) => { + iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { buf[offset + 13] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3867,44 +3374,40 @@ export const writeAggHeaderProps = { }, } -export const readAggHeader = (buf: Uint8Array, offset: number): AggHeader => { +export const readAggHeader = ( + buf: Uint8Array, + offset: number, +): AggHeader => { const value: AggHeader = { - op: buf[offset] as QueryTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as QueryTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, offset: readUint32(buf, offset + 3), limit: readUint32(buf, offset + 7), filterSize: readUint16(buf, offset + 11), - iteratorType: buf[offset + 13] as QueryIteratorTypeEnum, + iteratorType: (buf[offset + 13]) as QueryIteratorTypeEnum, size: readUint16(buf, offset + 14), resultsSize: readUint16(buf, offset + 16), accumulatorSize: readUint16(buf, offset + 18), - sort: ((buf[offset + 20] >>> 0) & 1) === 1, - hasGroupBy: ((buf[offset + 20] >>> 1) & 1) === 1, - isSamplingSet: ((buf[offset + 20] >>> 2) & 1) === 1, + sort: (((buf[offset + 20] >>> 0) & 1)) === 1, + hasGroupBy: (((buf[offset + 20] >>> 1) & 1)) === 1, + isSamplingSet: (((buf[offset + 20] >>> 2) & 1)) === 1, } return value } export const readAggHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), - iteratorType: (buf: Uint8Array, offset: number) => - buf[offset + 13] as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - resultsSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 16), - accumulatorSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 18), - sort: (buf: Uint8Array, offset: number) => - ((buf[offset + 20] >>> 0) & 1) === 1, - hasGroupBy: (buf: Uint8Array, offset: number) => - ((buf[offset + 20] >>> 1) & 1) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => - ((buf[offset + 20] >>> 2) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), + iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 13]) as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), + sort: (buf: Uint8Array, offset: number) => (((buf[offset + 20] >>> 0) & 1)) === 1, + hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 20] >>> 1) & 1)) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 20] >>> 2) & 1)) === 1, } export const createAggHeader = (header: AggHeader): Uint8Array => { @@ -3943,19 +3446,15 @@ export const addMultiSubscriptionHeaderByteSize = 2 export const addMultiSubscriptionHeaderAlignOf = 2 -export const packaddMultiSubscriptionHeader = ( - obj: addMultiSubscriptionHeader, -): bigint => { - let val = 0n - val |= (BigInt(obj.typeId) & 65535n) << 0n +export const packaddMultiSubscriptionHeader = (obj: addMultiSubscriptionHeader): number => { + let val = 0 + val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackaddMultiSubscriptionHeader = ( - val: bigint, -): addMultiSubscriptionHeader => { +export const unpackaddMultiSubscriptionHeader = (val: number): addMultiSubscriptionHeader => { return { - typeId: Number((val >> 0n) & 65535n), + typeId: Number((val >>> 0) & 65535), } } @@ -3986,12 +3485,10 @@ export const readaddMultiSubscriptionHeader = ( } export const readaddMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createaddMultiSubscriptionHeader = ( - header: addMultiSubscriptionHeader, -): Uint8Array => { +export const createaddMultiSubscriptionHeader = (header: addMultiSubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(addMultiSubscriptionHeaderByteSize) writeaddMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -4014,19 +3511,15 @@ export const removeMultiSubscriptionHeaderByteSize = 2 export const removeMultiSubscriptionHeaderAlignOf = 2 -export const packremoveMultiSubscriptionHeader = ( - obj: removeMultiSubscriptionHeader, -): bigint => { - let val = 0n - val |= (BigInt(obj.typeId) & 65535n) << 0n +export const packremoveMultiSubscriptionHeader = (obj: removeMultiSubscriptionHeader): number => { + let val = 0 + val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackremoveMultiSubscriptionHeader = ( - val: bigint, -): removeMultiSubscriptionHeader => { +export const unpackremoveMultiSubscriptionHeader = (val: number): removeMultiSubscriptionHeader => { return { - typeId: Number((val >> 0n) & 65535n), + typeId: Number((val >>> 0) & 65535), } } @@ -4057,12 +3550,10 @@ export const readremoveMultiSubscriptionHeader = ( } export const readremoveMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createremoveMultiSubscriptionHeader = ( - header: removeMultiSubscriptionHeader, -): Uint8Array => { +export const createremoveMultiSubscriptionHeader = (header: removeMultiSubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(removeMultiSubscriptionHeaderByteSize) writeremoveMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -4090,28 +3581,6 @@ export const AggPropByteSize = 9 export const AggPropAlignOf = 16 -export const packAggProp = (obj: AggProp): bigint => { - let val = 0n - val |= (BigInt(obj.propId) & 255n) << 0n - val |= (BigInt(obj.propType) & 255n) << 8n - val |= (BigInt(obj.propDefStart) & 65535n) << 16n - val |= (BigInt(obj.aggFunction) & 255n) << 32n - val |= (BigInt(obj.resultPos) & 65535n) << 40n - val |= (BigInt(obj.accumulatorPos) & 65535n) << 56n - return val -} - -export const unpackAggProp = (val: bigint): AggProp => { - return { - propId: Number((val >> 0n) & 255n), - propType: Number((val >> 8n) & 255n) as PropTypeEnum, - propDefStart: Number((val >> 16n) & 65535n), - aggFunction: Number((val >> 32n) & 255n) as AggFunctionEnum, - resultPos: Number((val >> 40n) & 65535n), - accumulatorPos: Number((val >> 56n) & 65535n), - } -} - export const writeAggProp = ( buf: Uint8Array, header: AggProp, @@ -4153,12 +3622,15 @@ export const writeAggPropProps = { }, } -export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { +export const readAggProp = ( + buf: Uint8Array, + offset: number, +): AggProp => { const value: AggProp = { propId: buf[offset], - propType: buf[offset + 1] as PropTypeEnum, + propType: (buf[offset + 1]) as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), - aggFunction: buf[offset + 4] as AggFunctionEnum, + aggFunction: (buf[offset + 4]) as AggFunctionEnum, resultPos: readUint16(buf, offset + 5), accumulatorPos: readUint16(buf, offset + 7), } @@ -4166,16 +3638,12 @@ export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { } export const readAggPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 1] as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2), - aggFunction: (buf: Uint8Array, offset: number) => - buf[offset + 4] as AggFunctionEnum, - resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - accumulatorPos: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 7), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + aggFunction: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as AggFunctionEnum, + resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + accumulatorPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), } export const createAggProp = (header: AggProp): Uint8Array => { @@ -4211,28 +3679,6 @@ export const GroupByKeyPropByteSize = 11 export const GroupByKeyPropAlignOf = 16 -export const packGroupByKeyProp = (obj: GroupByKeyProp): bigint => { - let val = 0n - val |= (BigInt(obj.propId) & 255n) << 0n - val |= (BigInt(obj.propType) & 255n) << 8n - val |= (BigInt(obj.propDefStart) & 65535n) << 16n - val |= (BigInt(obj.stepType) & 255n) << 32n - val |= (BigInt(obj.stepRange) & 4294967295n) << 40n - val |= (BigInt(obj.timezone) & 65535n) << 72n - return val -} - -export const unpackGroupByKeyProp = (val: bigint): GroupByKeyProp => { - return { - propId: Number((val >> 0n) & 255n), - propType: Number((val >> 8n) & 255n) as PropTypeEnum, - propDefStart: Number((val >> 16n) & 65535n), - stepType: Number((val >> 32n) & 255n), - stepRange: Number((val >> 40n) & 4294967295n), - timezone: Number((val >> 72n) & 65535n), - } -} - export const writeGroupByKeyProp = ( buf: Uint8Array, header: GroupByKeyProp, @@ -4280,7 +3726,7 @@ export const readGroupByKeyProp = ( ): GroupByKeyProp => { const value: GroupByKeyProp = { propId: buf[offset], - propType: buf[offset + 1] as PropTypeEnum, + propType: (buf[offset + 1]) as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), stepType: buf[offset + 4], stepRange: readUint32(buf, offset + 5), @@ -4290,14 +3736,12 @@ export const readGroupByKeyProp = ( } export const readGroupByKeyPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 1] as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2), - stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], - stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], + stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), } export const createGroupByKeyProp = (header: GroupByKeyProp): Uint8Array => { @@ -4406,8 +3850,7 @@ export const FilterOpCompareInverse = { selectLargeRefsEdge, nextOrIndex */ -export type FilterOpCompareEnum = - (typeof FilterOpCompare)[keyof typeof FilterOpCompare] +export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] export type FilterOp = { prop: PropTypeEnum @@ -4418,17 +3861,17 @@ export const FilterOpByteSize = 2 export const FilterOpAlignOf = 2 -export const packFilterOp = (obj: FilterOp): bigint => { - let val = 0n - val |= (BigInt(obj.prop) & 255n) << 0n - val |= (BigInt(obj.compare) & 255n) << 8n +export const packFilterOp = (obj: FilterOp): number => { + let val = 0 + val |= (Number(obj.prop) & 255) << 0 + val |= (Number(obj.compare) & 255) << 8 return val } -export const unpackFilterOp = (val: bigint): FilterOp => { +export const unpackFilterOp = (val: number): FilterOp => { return { - prop: Number((val >> 0n) & 255n) as PropTypeEnum, - compare: Number((val >> 8n) & 255n) as FilterOpCompareEnum, + prop: ((val >>> 0) & 255) as PropTypeEnum, + compare: ((val >>> 8) & 255) as FilterOpCompareEnum, } } @@ -4453,18 +3896,20 @@ export const writeFilterOpProps = { }, } -export const readFilterOp = (buf: Uint8Array, offset: number): FilterOp => { +export const readFilterOp = ( + buf: Uint8Array, + offset: number, +): FilterOp => { const value: FilterOp = { - prop: buf[offset] as PropTypeEnum, - compare: buf[offset + 1] as FilterOpCompareEnum, + prop: (buf[offset]) as PropTypeEnum, + compare: (buf[offset + 1]) as FilterOpCompareEnum, } return value } export const readFilterOpProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset] as PropTypeEnum, - compare: (buf: Uint8Array, offset: number) => - buf[offset + 1] as FilterOpCompareEnum, + prop: (buf: Uint8Array, offset: number) => (buf[offset]) as PropTypeEnum, + compare: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as FilterOpCompareEnum, } export const createFilterOp = (header: FilterOp): Uint8Array => { @@ -4497,30 +3942,6 @@ export const FilterConditionByteSize = 19 export const FilterConditionAlignOf = 16 -export const packFilterCondition = (obj: FilterCondition): bigint => { - let val = 0n - val |= (packFilterOp(obj.op) & 65535n) << 0n - val |= (BigInt(obj.size) & 4294967295n) << 16n - val |= (BigInt(obj.prop) & 255n) << 48n - val |= (BigInt(obj.start) & 65535n) << 56n - val |= (BigInt(obj.len) & 255n) << 72n - val |= (BigInt(obj.fieldSchema) & 18446744073709551615n) << 80n - val |= (BigInt(obj.offset) & 255n) << 144n - return val -} - -export const unpackFilterCondition = (val: bigint): FilterCondition => { - return { - op: unpackFilterOp((val >> 0n) & 65535n), - size: Number((val >> 16n) & 4294967295n), - prop: Number((val >> 48n) & 255n), - start: Number((val >> 56n) & 65535n), - len: Number((val >> 72n) & 255n), - fieldSchema: Number((val >> 80n) & 18446744073709551615n), - offset: Number((val >> 144n) & 255n), - } -} - export const writeFilterCondition = ( buf: Uint8Array, header: FilterCondition, @@ -4572,7 +3993,7 @@ export const readFilterCondition = ( offset: number, ): FilterCondition => { const value: FilterCondition = { - op: unpackFilterOp(BigInt(readUint16(buf, offset))), + op: unpackFilterOp(readUint16(buf, offset)), size: readUint32(buf, offset + 2), prop: buf[offset + 6], start: readUint16(buf, offset + 7), @@ -4584,15 +4005,13 @@ export const readFilterCondition = ( } export const readFilterConditionProps = { - op: (buf: Uint8Array, offset: number) => - unpackFilterOp(BigInt(readUint16(buf, offset))), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - prop: (buf: Uint8Array, offset: number) => buf[offset + 6], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), - len: (buf: Uint8Array, offset: number) => buf[offset + 9], - fieldSchema: (buf: Uint8Array, offset: number) => - readUint64(buf, offset + 10), - offset: (buf: Uint8Array, offset: number) => buf[offset + 18], + op: (buf: Uint8Array, offset: number) => unpackFilterOp(readUint16(buf, offset)), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + prop: (buf: Uint8Array, offset: number) => buf[offset + 6], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + len: (buf: Uint8Array, offset: number) => buf[offset + 9], + fieldSchema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 10), + offset: (buf: Uint8Array, offset: number) => buf[offset + 18], } export const createFilterCondition = (header: FilterCondition): Uint8Array => { @@ -4626,22 +4045,6 @@ export const FilterSelectByteSize = 14 export const FilterSelectAlignOf = 16 -export const packFilterSelect = (obj: FilterSelect): bigint => { - let val = 0n - val |= (BigInt(obj.size) & 4294967295n) << 0n - val |= (BigInt(obj.typeEntry) & 18446744073709551615n) << 32n - val |= (BigInt(obj.typeId) & 65535n) << 96n - return val -} - -export const unpackFilterSelect = (val: bigint): FilterSelect => { - return { - size: Number((val >> 0n) & 4294967295n), - typeEntry: Number((val >> 32n) & 18446744073709551615n), - typeId: Number((val >> 96n) & 65535n) as TypeId, - } -} - export const writeFilterSelect = ( buf: Uint8Array, header: FilterSelect, @@ -4675,16 +4078,15 @@ export const readFilterSelect = ( const value: FilterSelect = { size: readUint32(buf, offset), typeEntry: readUint64(buf, offset + 4), - typeId: readUint16(buf, offset + 12) as TypeId, + typeId: (readUint16(buf, offset + 12)) as TypeId, } return value } export const readFilterSelectProps = { - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 12) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 12)) as TypeId, } export const createFilterSelect = (header: FilterSelect): Uint8Array => { @@ -4703,3 +4105,4 @@ export const pushFilterSelect = ( buf.pushU16(Number(header.typeId)) return index } + diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts new file mode 100644 index 0000000000..103439ed7b --- /dev/null +++ b/test/query-ast/include.ts @@ -0,0 +1,27 @@ +import { QueryAst } from '../../src/db-query/ast.js' +import { queryAstToByteCode } from '../../src/db-query/toByteCode/toByteCode.js' +import { parseSchema, Schema } from '../../src/schema.js' +import { debugBuffer } from '../../src/sdk.js' + +import test from '../shared/test.js' + +await test('include', async (t) => { + const schema: Schema = { + types: { + user: { + name: 'string', + }, + }, + } + + const strictSchema = parseSchema(schema) + + const buf = queryAstToByteCode(strictSchema, { + type: 'user', + props: { + name: { include: {} }, + }, + }) + + debugBuffer(buf) +}) From b28a8fca7efa18a88fd5d14b5ea5994355ba1000 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 29 Jan 2026 08:54:25 +0100 Subject: [PATCH 021/449] cleaner --- src/db-client/index.ts | 2 - src/db-client/modify/index.ts | 2 +- src/db-client/modify2.ts | 93 -------------------------------- src/utils/AutoSizedUint8Array.ts | 4 -- 4 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 src/db-client/modify2.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index c743d44720..a9298783d6 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -36,8 +36,6 @@ export type ModifyOpts = { locale?: keyof typeof LangCode } -// ... imports - export class DbClient = SchemaOut> extends DbShared { constructor({ hooks, diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index e63583e33d..8cdc6de2a1 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -178,7 +178,7 @@ export const flush = (ctx: ModifyCtx) => { } }) - ctx.buf.flush() + ctx.buf.length = 0 ctx.batch = { count: 0 } } diff --git a/src/db-client/modify2.ts b/src/db-client/modify2.ts deleted file mode 100644 index 8fdf86c47f..0000000000 --- a/src/db-client/modify2.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { - parseSchema, - type Schema, - type SchemaOut, - type SchemaTypes, -} from '../schema.js' - -type TypedArray = - | Uint8Array - | Float32Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array - -type TypeMap = { - string: string - number: number - int8: number - uint8: number - int16: number - uint16: number - int32: number - uint32: number - boolean: boolean - text: string - json: any - timestamp: number | string | Date - binary: Uint8Array - alias: string - vector: TypedArray - colvec: TypedArray - cardinality: string | string[] -} - -type InferProp = Prop extends { type: 'object'; props: infer P } - ? InferType - : Prop extends { type: infer T extends keyof TypeMap } - ? TypeMap[T] - : Prop extends { enum: infer E extends readonly any[] } - ? E[number] - : Prop extends { ref: string } - ? string | number - : Prop extends { items: { ref: string } } - ? (string | number)[] - : never - -type InferType = { - [K in keyof Props as Props[K] extends { required: true } - ? K - : never]: InferProp -} & { - [K in keyof Props as Props[K] extends { required: true } - ? never - : K]?: InferProp -} - -type InferPayload> = { - [K in keyof Types]: InferType -} - -const schema = parseSchema({ - types: { - user: { - props: { - name: 'string', - foo: 'string', - }, - }, - }, -}) - -export const create = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] = keyof S['types'], ->( - schema: S, - type: T, - payload: InferPayload[T], -) => { - const buf: Uint8Array = new Uint8Array(100) - buf - - const buffers = [] -} - -create(schema, 'user', { - name: 'xxx', - foo: 'abc', -}) diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index ffc0b1782e..55948fb5f1 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -80,10 +80,6 @@ export class AutoSizedUint8Array { if (end > this.length) this.length = end } - flush(): void { - this.length = 0 - } - fill(value: number, start: number = 0, end: number = this.length): this { if (end > this.length) { if (end > this.data.length) { From 672c7021a6b6c794c0cc2374851018793ffc3472 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 29 Jan 2026 10:10:33 +0100 Subject: [PATCH 022/449] update --- test/youzi.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/youzi.ts b/test/youzi.ts index 2766a36984..a00e484fb6 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -107,8 +107,6 @@ await test.skip('modify raw', async (t) => { await db.server.modify(buf.view) - buf.flush() - console.log('done did it!') const res = await db.query('user').include('*', 'friends.*').get().toObject() From edc61f3a1234f08ffbccea1018c8599e5931126c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 29 Jan 2026 09:40:45 +0100 Subject: [PATCH 023/449] Should use schema_bool_t here --- clibs/lib/selva/schema.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index 871d6262e5..e490239d65 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -257,7 +257,7 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc enum SelvaFieldType type; uint16_t vec_len; /*!< Length of a single vector. */ uint16_t comp_size; /*!< Component size in the vector. */ - uint8_t has_default; + schema_bool_t has_default; } __packed spec; if (ctx->len < sizeof(spec)) { From 6dbd802f058634320400829ab53036547296f74e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 29 Jan 2026 10:09:07 +0100 Subject: [PATCH 024/449] Nicer typing for selva schema --- native/types.zig | 35 +++ src/db-server/schemaSelvaBuffer.ts | 75 +++--- src/zigTsExports.ts | 416 +++++++++++++++++++++++++++++ 3 files changed, 486 insertions(+), 40 deletions(-) diff --git a/native/types.zig b/native/types.zig index 1016c9f87b..fac47a2390 100644 --- a/native/types.zig +++ b/native/types.zig @@ -2,6 +2,8 @@ const Schema = @import("selva/schema.zig"); const Node = @import("selva/node.zig"); pub const TypeId = u16; +pub const SelvaFieldType = u8; +pub const SelvaField = u8; pub const BridgeResponse = enum(u32) { query = 1, @@ -886,3 +888,36 @@ pub const FilterSelect = packed struct { // edgeTypeId: TypeId, // you want EDGE INDEX as well }; + +pub const SelvaSchemaMicroBuffer = packed struct { + type: SelvaFieldType, + len: u16, + hasDefault: u8, +}; + +pub const SelvaSchemaString = packed struct { + type: SelvaFieldType, + fixedLen: u8, + defaultLen: u32, +}; + +pub const SelvaSchemaText = packed struct { + type: SelvaFieldType, + nrDefaults: u8, +}; + +pub const SelvaSchemaRef = packed struct { + type: SelvaFieldType, + flags: u8, + dstNodeType: TypeId, + inverseField: SelvaField, + edgeNodeType: TypeId, + capped: u32, +}; + +pub const SelvaSchemaColvec = packed struct { + type: SelvaFieldType, + vecLen: u16, + compSize: u16, + hasDefault: u8, +}; diff --git a/src/db-server/schemaSelvaBuffer.ts b/src/db-server/schemaSelvaBuffer.ts index b51374587c..2aa2666f9f 100644 --- a/src/db-server/schemaSelvaBuffer.ts +++ b/src/db-server/schemaSelvaBuffer.ts @@ -1,6 +1,5 @@ -import { writeUint16, writeUint32 } from '../utils/index.js' -import native from '../native.js' -import { LangCode, PropType, PropTypeEnum } from '../zigTsExports.js' +import { writeUint32 } from '../utils/index.js' +import { createSelvaSchemaColvec, createSelvaSchemaRef, createSelvaSchemaString, createSelvaSchemaText, LangCode, PropType, PropTypeEnum, writeSelvaSchemaMicroBuffer } from '../zigTsExports.js' import { EMPTY_MICRO_BUFFER, VECTOR_BASE_TYPE_SIZE_MAP, @@ -47,7 +46,6 @@ const supportedDefaults = new Set([ PropType.vector, PropType.json, // same as binary (Uint8Array) ]) -const STRING_EXTRA_MAX = 10 function blockCapacity(blockCapacity: number): Uint8Array { const buf = new Uint8Array(Uint32Array.BYTES_PER_ELEMENT) @@ -76,37 +74,35 @@ const propDefBuffer = ( if (prop.len && (type === PropType.microBuffer || type === PropType.vector)) { const buf = new Uint8Array(4) + writeSelvaSchemaMicroBuffer(buf, { + type: selvaType, + len: prop.len, + hasDefault: ~~!!prop.default, + }, 0) - buf[0] = selvaType - writeUint16(buf, prop.len, 1) if (prop.default) { - buf[3] = 1 // has default return [...buf, ...prop.default] } else { - buf[3] = 0 // has default return [...buf] } } else if (prop.len && type === PropType.colVec) { - const buf = new Uint8Array(5) - - buf[0] = selvaType const baseSize = VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] - - writeUint16(buf, prop.len / baseSize, 1) // elements - writeUint16(buf, baseSize, 3) // element size - return [...buf] + return [...createSelvaSchemaColvec({ + type: selvaType, + vecLen: prop.len / baseSize, + compSize: baseSize, + hasDefault: 0, + })] // TODO Add support for default } else if (type === PropType.reference || type === PropType.references) { - const buf = new Uint8Array(11) const dstType: SchemaTypeDef = schema[prop.inverseTypeName!] - - buf[0] = selvaType // field type - buf[1] = makeEdgeConstraintFlags(prop) // flags - writeUint16(buf, dstType.id, 2) // dst_node_type - buf[4] = prop.inversePropNumber! // inverse_field - writeUint16(buf, prop.edgeNodeTypeId ?? 0, 5) // edge_node_type - writeUint32(buf, prop.referencesCapped ?? 0, 7) - - return [...buf] + return [...createSelvaSchemaRef({ + type: selvaType, + flags: makeEdgeConstraintFlags(prop), + dstNodeType: dstType.id, + inverseField: prop.inversePropNumber!, + edgeNodeType: prop.edgeNodeTypeId ?? 0, + capped: prop.referencesCapped ?? 0 + })] } else if ( type === PropType.string || type === PropType.binary || @@ -140,20 +136,21 @@ const propDefBuffer = ( // return [...buf] } else { - const buf = new Uint8Array(6) - - buf[0] = selvaType - buf[1] = prop.len < 50 ? prop.len : 0 - writeUint32(buf, 0, 2) // no default - - return [...buf] + return [...createSelvaSchemaString({ + type: selvaType, + fixedLen: prop.len < 50 ? prop.len : 0, + defaultLen: 0, + })] } } else if (type === PropType.text) { - const fs: number[] = [selvaType, Object.keys(prop.default).length] - // [ type, nrDefaults, [len, default], [len, default]...] - - for (const langName in prop.default) { - console.warn('TODO default!!') + // TODO Defaults + return [...createSelvaSchemaText({ + type: selvaType, + nrDefaults: Object.keys(prop.default).length, + })] + + //for (const langName in prop.default) { + // console.warn('TODO default!!') // const lang = LangCode[langName] // const value = prop.default[langName].normalize('NFKD') // const tmpLen = 4 + 2 * native.stringByteLength(value) + STRING_EXTRA_MAX @@ -165,9 +162,7 @@ const propDefBuffer = ( // } // writeUint32(buf, l, 0) // length of the default // fs.push(...buf) - } - - return fs + //} } return [selvaType] } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 20ba2ddbfa..327cbbaf39 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -12,6 +12,10 @@ import { AutoSizedUint8Array } from './utils/AutoSizedUint8Array.js' export type TypeId = number +export type SelvaFieldType = number + +export type SelvaField = number + export const BridgeResponse = { query: 1, modify: 2, @@ -4106,3 +4110,415 @@ export const pushFilterSelect = ( return index } +export type SelvaSchemaMicroBuffer = { + type: SelvaFieldType + len: number + hasDefault: number +} + +export const SelvaSchemaMicroBufferByteSize = 4 + +export const SelvaSchemaMicroBufferAlignOf = 4 + +export const packSelvaSchemaMicroBuffer = (obj: SelvaSchemaMicroBuffer): number => { + let val = 0 + val |= (Number(obj.type) & 255) << 0 + val |= (Number(obj.len) & 65535) << 8 + val |= (Number(obj.hasDefault) & 255) << 24 + return val +} + +export const unpackSelvaSchemaMicroBuffer = (val: number): SelvaSchemaMicroBuffer => { + return { + type: Number((val >>> 0) & 255), + len: Number((val >>> 8) & 65535), + hasDefault: Number((val >>> 24) & 255), + } +} + +export const writeSelvaSchemaMicroBuffer = ( + buf: Uint8Array, + header: SelvaSchemaMicroBuffer, + offset: number, +): number => { + buf[offset] = Number(header.type) + offset += 1 + writeUint16(buf, Number(header.len), offset) + offset += 2 + buf[offset] = Number(header.hasDefault) + offset += 1 + return offset +} + +export const writeSelvaSchemaMicroBufferProps = { + type: (buf: Uint8Array, value: SelvaFieldType, offset: number) => { + buf[offset] = Number(value) + }, + len: (buf: Uint8Array, value: number, offset: number) => { + writeUint16(buf, Number(value), offset + 1) + }, + hasDefault: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 3] = Number(value) + }, +} + +export const readSelvaSchemaMicroBuffer = ( + buf: Uint8Array, + offset: number, +): SelvaSchemaMicroBuffer => { + const value: SelvaSchemaMicroBuffer = { + type: buf[offset], + len: readUint16(buf, offset + 1), + hasDefault: buf[offset + 3], + } + return value +} + +export const readSelvaSchemaMicroBufferProps = { + type: (buf: Uint8Array, offset: number) => buf[offset], + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], +} + +export const createSelvaSchemaMicroBuffer = (header: SelvaSchemaMicroBuffer): Uint8Array => { + const buffer = new Uint8Array(SelvaSchemaMicroBufferByteSize) + writeSelvaSchemaMicroBuffer(buffer, header, 0) + return buffer +} + +export const pushSelvaSchemaMicroBuffer = ( + buf: AutoSizedUint8Array, + header: SelvaSchemaMicroBuffer, +): number => { + const index = buf.length + buf.pushUint8(Number(header.type)) + buf.pushUint16(Number(header.len)) + buf.pushUint8(Number(header.hasDefault)) + return index +} + +export type SelvaSchemaString = { + type: SelvaFieldType + fixedLen: number + defaultLen: number +} + +export const SelvaSchemaStringByteSize = 6 + +export const SelvaSchemaStringAlignOf = 8 + +export const writeSelvaSchemaString = ( + buf: Uint8Array, + header: SelvaSchemaString, + offset: number, +): number => { + buf[offset] = Number(header.type) + offset += 1 + buf[offset] = Number(header.fixedLen) + offset += 1 + writeUint32(buf, Number(header.defaultLen), offset) + offset += 4 + return offset +} + +export const writeSelvaSchemaStringProps = { + type: (buf: Uint8Array, value: SelvaFieldType, offset: number) => { + buf[offset] = Number(value) + }, + fixedLen: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + defaultLen: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, +} + +export const readSelvaSchemaString = ( + buf: Uint8Array, + offset: number, +): SelvaSchemaString => { + const value: SelvaSchemaString = { + type: buf[offset], + fixedLen: buf[offset + 1], + defaultLen: readUint32(buf, offset + 2), + } + return value +} + +export const readSelvaSchemaStringProps = { + type: (buf: Uint8Array, offset: number) => buf[offset], + fixedLen: (buf: Uint8Array, offset: number) => buf[offset + 1], + defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), +} + +export const createSelvaSchemaString = (header: SelvaSchemaString): Uint8Array => { + const buffer = new Uint8Array(SelvaSchemaStringByteSize) + writeSelvaSchemaString(buffer, header, 0) + return buffer +} + +export const pushSelvaSchemaString = ( + buf: AutoSizedUint8Array, + header: SelvaSchemaString, +): number => { + const index = buf.length + buf.pushUint8(Number(header.type)) + buf.pushUint8(Number(header.fixedLen)) + buf.pushUint32(Number(header.defaultLen)) + return index +} + +export type SelvaSchemaText = { + type: SelvaFieldType + nrDefaults: number +} + +export const SelvaSchemaTextByteSize = 2 + +export const SelvaSchemaTextAlignOf = 2 + +export const packSelvaSchemaText = (obj: SelvaSchemaText): number => { + let val = 0 + val |= (Number(obj.type) & 255) << 0 + val |= (Number(obj.nrDefaults) & 255) << 8 + return val +} + +export const unpackSelvaSchemaText = (val: number): SelvaSchemaText => { + return { + type: Number((val >>> 0) & 255), + nrDefaults: Number((val >>> 8) & 255), + } +} + +export const writeSelvaSchemaText = ( + buf: Uint8Array, + header: SelvaSchemaText, + offset: number, +): number => { + buf[offset] = Number(header.type) + offset += 1 + buf[offset] = Number(header.nrDefaults) + offset += 1 + return offset +} + +export const writeSelvaSchemaTextProps = { + type: (buf: Uint8Array, value: SelvaFieldType, offset: number) => { + buf[offset] = Number(value) + }, + nrDefaults: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, +} + +export const readSelvaSchemaText = ( + buf: Uint8Array, + offset: number, +): SelvaSchemaText => { + const value: SelvaSchemaText = { + type: buf[offset], + nrDefaults: buf[offset + 1], + } + return value +} + +export const readSelvaSchemaTextProps = { + type: (buf: Uint8Array, offset: number) => buf[offset], + nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], +} + +export const createSelvaSchemaText = (header: SelvaSchemaText): Uint8Array => { + const buffer = new Uint8Array(SelvaSchemaTextByteSize) + writeSelvaSchemaText(buffer, header, 0) + return buffer +} + +export const pushSelvaSchemaText = ( + buf: AutoSizedUint8Array, + header: SelvaSchemaText, +): number => { + const index = buf.length + buf.pushUint8(Number(header.type)) + buf.pushUint8(Number(header.nrDefaults)) + return index +} + +export type SelvaSchemaRef = { + type: SelvaFieldType + flags: number + dstNodeType: TypeId + inverseField: SelvaField + edgeNodeType: TypeId + capped: number +} + +export const SelvaSchemaRefByteSize = 11 + +export const SelvaSchemaRefAlignOf = 16 + +export const writeSelvaSchemaRef = ( + buf: Uint8Array, + header: SelvaSchemaRef, + offset: number, +): number => { + buf[offset] = Number(header.type) + offset += 1 + buf[offset] = Number(header.flags) + offset += 1 + writeUint16(buf, Number(header.dstNodeType), offset) + offset += 2 + buf[offset] = Number(header.inverseField) + offset += 1 + writeUint16(buf, Number(header.edgeNodeType), offset) + offset += 2 + writeUint32(buf, Number(header.capped), offset) + offset += 4 + return offset +} + +export const writeSelvaSchemaRefProps = { + type: (buf: Uint8Array, value: SelvaFieldType, offset: number) => { + buf[offset] = Number(value) + }, + flags: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + dstNodeType: (buf: Uint8Array, value: TypeId, offset: number) => { + writeUint16(buf, Number(value), offset + 2) + }, + inverseField: (buf: Uint8Array, value: SelvaField, offset: number) => { + buf[offset + 4] = Number(value) + }, + edgeNodeType: (buf: Uint8Array, value: TypeId, offset: number) => { + writeUint16(buf, Number(value), offset + 5) + }, + capped: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 7) + }, +} + +export const readSelvaSchemaRef = ( + buf: Uint8Array, + offset: number, +): SelvaSchemaRef => { + const value: SelvaSchemaRef = { + type: buf[offset], + flags: buf[offset + 1], + dstNodeType: (readUint16(buf, offset + 2)) as TypeId, + inverseField: buf[offset + 4], + edgeNodeType: (readUint16(buf, offset + 5)) as TypeId, + capped: readUint32(buf, offset + 7), + } + return value +} + +export const readSelvaSchemaRefProps = { + type: (buf: Uint8Array, offset: number) => buf[offset], + flags: (buf: Uint8Array, offset: number) => buf[offset + 1], + dstNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], + edgeNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 5)) as TypeId, + capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), +} + +export const createSelvaSchemaRef = (header: SelvaSchemaRef): Uint8Array => { + const buffer = new Uint8Array(SelvaSchemaRefByteSize) + writeSelvaSchemaRef(buffer, header, 0) + return buffer +} + +export const pushSelvaSchemaRef = ( + buf: AutoSizedUint8Array, + header: SelvaSchemaRef, +): number => { + const index = buf.length + buf.pushUint8(Number(header.type)) + buf.pushUint8(Number(header.flags)) + buf.pushUint16(Number(header.dstNodeType)) + buf.pushUint8(Number(header.inverseField)) + buf.pushUint16(Number(header.edgeNodeType)) + buf.pushUint32(Number(header.capped)) + return index +} + +export type SelvaSchemaColvec = { + type: SelvaFieldType + vecLen: number + compSize: number + hasDefault: number +} + +export const SelvaSchemaColvecByteSize = 6 + +export const SelvaSchemaColvecAlignOf = 8 + +export const writeSelvaSchemaColvec = ( + buf: Uint8Array, + header: SelvaSchemaColvec, + offset: number, +): number => { + buf[offset] = Number(header.type) + offset += 1 + writeUint16(buf, Number(header.vecLen), offset) + offset += 2 + writeUint16(buf, Number(header.compSize), offset) + offset += 2 + buf[offset] = Number(header.hasDefault) + offset += 1 + return offset +} + +export const writeSelvaSchemaColvecProps = { + type: (buf: Uint8Array, value: SelvaFieldType, offset: number) => { + buf[offset] = Number(value) + }, + vecLen: (buf: Uint8Array, value: number, offset: number) => { + writeUint16(buf, Number(value), offset + 1) + }, + compSize: (buf: Uint8Array, value: number, offset: number) => { + writeUint16(buf, Number(value), offset + 3) + }, + hasDefault: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 5] = Number(value) + }, +} + +export const readSelvaSchemaColvec = ( + buf: Uint8Array, + offset: number, +): SelvaSchemaColvec => { + const value: SelvaSchemaColvec = { + type: buf[offset], + vecLen: readUint16(buf, offset + 1), + compSize: readUint16(buf, offset + 3), + hasDefault: buf[offset + 5], + } + return value +} + +export const readSelvaSchemaColvecProps = { + type: (buf: Uint8Array, offset: number) => buf[offset], + vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], +} + +export const createSelvaSchemaColvec = (header: SelvaSchemaColvec): Uint8Array => { + const buffer = new Uint8Array(SelvaSchemaColvecByteSize) + writeSelvaSchemaColvec(buffer, header, 0) + return buffer +} + +export const pushSelvaSchemaColvec = ( + buf: AutoSizedUint8Array, + header: SelvaSchemaColvec, +): number => { + const index = buf.length + buf.pushUint8(Number(header.type)) + buf.pushUint16(Number(header.vecLen)) + buf.pushUint16(Number(header.compSize)) + buf.pushUint8(Number(header.hasDefault)) + return index +} + From 8b85e6a6020cf32275c35cf79bc23c8bf23b6109 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 29 Jan 2026 10:13:27 +0100 Subject: [PATCH 025/449] prettier --- src/db-server/schemaSelvaBuffer.ts | 101 +++++++++++++++++------------ 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/src/db-server/schemaSelvaBuffer.ts b/src/db-server/schemaSelvaBuffer.ts index 2aa2666f9f..53d08d0d47 100644 --- a/src/db-server/schemaSelvaBuffer.ts +++ b/src/db-server/schemaSelvaBuffer.ts @@ -1,5 +1,14 @@ import { writeUint32 } from '../utils/index.js' -import { createSelvaSchemaColvec, createSelvaSchemaRef, createSelvaSchemaString, createSelvaSchemaText, LangCode, PropType, PropTypeEnum, writeSelvaSchemaMicroBuffer } from '../zigTsExports.js' +import { + createSelvaSchemaColvec, + createSelvaSchemaRef, + createSelvaSchemaString, + createSelvaSchemaText, + LangCode, + PropType, + PropTypeEnum, + writeSelvaSchemaMicroBuffer, +} from '../zigTsExports.js' import { EMPTY_MICRO_BUFFER, VECTOR_BASE_TYPE_SIZE_MAP, @@ -74,11 +83,15 @@ const propDefBuffer = ( if (prop.len && (type === PropType.microBuffer || type === PropType.vector)) { const buf = new Uint8Array(4) - writeSelvaSchemaMicroBuffer(buf, { - type: selvaType, - len: prop.len, - hasDefault: ~~!!prop.default, - }, 0) + writeSelvaSchemaMicroBuffer( + buf, + { + type: selvaType, + len: prop.len, + hasDefault: ~~!!prop.default, + }, + 0, + ) if (prop.default) { return [...buf, ...prop.default] @@ -87,22 +100,26 @@ const propDefBuffer = ( } } else if (prop.len && type === PropType.colVec) { const baseSize = VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] - return [...createSelvaSchemaColvec({ - type: selvaType, - vecLen: prop.len / baseSize, - compSize: baseSize, - hasDefault: 0, - })] // TODO Add support for default + return [ + ...createSelvaSchemaColvec({ + type: selvaType, + vecLen: prop.len / baseSize, + compSize: baseSize, + hasDefault: 0, + }), + ] // TODO Add support for default } else if (type === PropType.reference || type === PropType.references) { const dstType: SchemaTypeDef = schema[prop.inverseTypeName!] - return [...createSelvaSchemaRef({ - type: selvaType, - flags: makeEdgeConstraintFlags(prop), - dstNodeType: dstType.id, - inverseField: prop.inversePropNumber!, - edgeNodeType: prop.edgeNodeTypeId ?? 0, - capped: prop.referencesCapped ?? 0 - })] + return [ + ...createSelvaSchemaRef({ + type: selvaType, + flags: makeEdgeConstraintFlags(prop), + dstNodeType: dstType.id, + inverseField: prop.inversePropNumber!, + edgeNodeType: prop.edgeNodeTypeId ?? 0, + capped: prop.referencesCapped ?? 0, + }), + ] } else if ( type === PropType.string || type === PropType.binary || @@ -136,32 +153,36 @@ const propDefBuffer = ( // return [...buf] } else { - return [...createSelvaSchemaString({ - type: selvaType, - fixedLen: prop.len < 50 ? prop.len : 0, - defaultLen: 0, - })] + return [ + ...createSelvaSchemaString({ + type: selvaType, + fixedLen: prop.len < 50 ? prop.len : 0, + defaultLen: 0, + }), + ] } } else if (type === PropType.text) { // TODO Defaults - return [...createSelvaSchemaText({ - type: selvaType, - nrDefaults: Object.keys(prop.default).length, - })] + return [ + ...createSelvaSchemaText({ + type: selvaType, + nrDefaults: Object.keys(prop.default).length, + }), + ] //for (const langName in prop.default) { // console.warn('TODO default!!') - // const lang = LangCode[langName] - // const value = prop.default[langName].normalize('NFKD') - // const tmpLen = 4 + 2 * native.stringByteLength(value) + STRING_EXTRA_MAX - // let buf = new Uint8Array(tmpLen) - - // const l = writeString({ buf } as Ctx, value, 4, lang, false) - // if (l != buf.length) { - // buf = buf.subarray(0, 4 + l) - // } - // writeUint32(buf, l, 0) // length of the default - // fs.push(...buf) + // const lang = LangCode[langName] + // const value = prop.default[langName].normalize('NFKD') + // const tmpLen = 4 + 2 * native.stringByteLength(value) + STRING_EXTRA_MAX + // let buf = new Uint8Array(tmpLen) + + // const l = writeString({ buf } as Ctx, value, 4, lang, false) + // if (l != buf.length) { + // buf = buf.subarray(0, 4 + l) + // } + // writeUint32(buf, l, 0) // length of the default + // fs.push(...buf) //} } return [selvaType] From 9101a0aa19c74033c0aa68608211d3d6d626e335 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 11:22:36 +0100 Subject: [PATCH 026/449] start of buf generation qAst --- native/query/multiple.zig | 1 + src/db-query/ast.ts | 7 ++++- src/db-query/toByteCode/collect.ts | 37 +++++++++++++++++++++++++++ src/db-query/toByteCode/include.ts | 16 ++++++++++++ src/db-query/toByteCode/multiple.ts | 27 ++++++++++++------- src/db-query/toByteCode/toByteCode.ts | 9 +++---- 6 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 src/db-query/toByteCode/collect.ts create mode 100644 src/db-query/toByteCode/include.ts diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 855041b520..2a30666992 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -148,6 +148,7 @@ pub fn default( q: []u8, ) !void { var i: usize = 0; + // make default header! const header = utils.readNext(t.QueryHeader, q, &i); const sizeIndex = try ctx.thread.query.reserve(4); const typeEntry = try Node.getType(ctx.db, header.typeId); diff --git a/src/db-query/ast.ts b/src/db-query/ast.ts index 006539cc58..3d99ddf928 100644 --- a/src/db-query/ast.ts +++ b/src/db-query/ast.ts @@ -1,3 +1,5 @@ +import { PropDef } from '../schema/defs/index.js' + export type FilterOp = { op: '=' | '<' | '>' | '..' | 'includes' | 'exists' | 'exist' val?: any @@ -36,7 +38,10 @@ export type QueryAst = { > } -type QueryAstNode = FilterAst | QueryAst +export type QueryAstCtx = { + main: PropDef[] + // more? +} // const x: QueryAst = { // type: 'user', diff --git a/src/db-query/toByteCode/collect.ts b/src/db-query/toByteCode/collect.ts new file mode 100644 index 0000000000..bc3fa97dba --- /dev/null +++ b/src/db-query/toByteCode/collect.ts @@ -0,0 +1,37 @@ +import { TypeDef } from '../../schema/defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { IncludeOp, PropType, pushIncludeHeader } from '../../zigTsExports.js' +import { QueryAst, QueryAstCtx } from '../ast.js' +import { includeProp } from './include.js' + +export const collect = ( + ast: QueryAst, + buf: AutoSizedUint8Array, + typeDef: TypeDef, + path: string[], + ctx: QueryAstCtx = { main: [] }, +) => { + for (const field in ast.props) { + const propDef = typeDef.props.get(field) + if (propDef) { + // LANGUAGE + // $EDGE + if (propDef.type === PropType.reference) { + // REFERENCE + } else if (propDef.type === PropType.references) { + // REFERENCES + } else if (propDef.id === 0) { + ctx.main.push(propDef) + } else { + includeProp(buf, propDef) + } + } else { + if ('props' in ast) { + collect(ast, buf, typeDef, [...path, field], ctx) + } else { + throw new Error(`Prop does not exist ${field}`) + } + } + } + return ctx +} diff --git a/src/db-query/toByteCode/include.ts b/src/db-query/toByteCode/include.ts new file mode 100644 index 0000000000..aa6489a489 --- /dev/null +++ b/src/db-query/toByteCode/include.ts @@ -0,0 +1,16 @@ +import { PropDef } from '../../schema/defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { IncludeOp, pushIncludeHeader } from '../../zigTsExports.js' + +export const includeProp = (buf: AutoSizedUint8Array, propDef: PropDef) => { + // derp + pushIncludeHeader(buf, { + op: IncludeOp.default, + prop: propDef.id, + propType: propDef.type, + }) +} + +export const includeProps = (buf: AutoSizedUint8Array, propDef: PropDef[]) => { + // sort all main on start +} diff --git a/src/db-query/toByteCode/multiple.ts b/src/db-query/toByteCode/multiple.ts index e26c3948e3..23a091f960 100644 --- a/src/db-query/toByteCode/multiple.ts +++ b/src/db-query/toByteCode/multiple.ts @@ -5,27 +5,31 @@ import { QueryType, ID_PROP, QueryHeaderByteSize, + readQueryHeaderProps, + writeQueryHeaderProps, } from '../../zigTsExports.js' -import { QueryAst } from '../ast.js' +import { QueryAst, QueryAstCtx } from '../ast.js' +import { collect } from './collect.js' +import { includeProps } from './include.js' import { getIteratorType } from './iteratorType.js' -export const multiple = ( +export const defaultMultiple = ( ast: QueryAst, buf: AutoSizedUint8Array, typeDef: TypeDef, - prop?: PropDef, ) => { let startOffset = buf.length - console.log('start here', startOffset) - const offset = ast.range?.start || 0 + // now include + + // MAKE THIS DEFAULT const queryHeaderOffset = pushQueryHeader(buf, { - op: prop ? QueryType.references : QueryType.default, - prop: prop ? prop.id : ID_PROP, + op: QueryType.default, + prop: ID_PROP, includeSize: 0, - typeId: prop?.typeDef.id || typeDef.id, + typeId: typeDef.id, offset, limit: (ast.range?.end || 1000) + offset, // fix sort: false, @@ -40,5 +44,10 @@ export const multiple = ( // const buffer = new Uint8Array(QueryHeaderByteSize + searchSize + sortSize) }) - // use offset to write includeSize and filterSize + // make fn for this + const includeStart = buf.length + const ctx = collect(ast, buf, typeDef, []) + includeProps(buf, ctx.main) + const includeSize = buf.length - includeStart + writeQueryHeaderProps.includeSize(buf.data, includeSize, 0) } diff --git a/src/db-query/toByteCode/toByteCode.ts b/src/db-query/toByteCode/toByteCode.ts index 63e5758054..6619817001 100644 --- a/src/db-query/toByteCode/toByteCode.ts +++ b/src/db-query/toByteCode/toByteCode.ts @@ -1,16 +1,15 @@ import { crc32 } from '../../db-client/crc32.js' -import { SchemaOut } from '../../schema.js' +import { PropDef, SchemaOut } from '../../schema.js' import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' +import { TypeDef } from '../../schema/defs/index.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { QueryAst } from '../ast.js' -import { multiple } from './multiple.js' +import { defaultMultiple } from './multiple.js' export const queryAstToByteCode = ( schema: SchemaOut, ast: QueryAst, ): Uint8Array => { - // 8 for schema checksum, 4 for query id, and QueryHeader - if (!ast.type) { throw new Error('Query requires type') } @@ -27,7 +26,7 @@ export const queryAstToByteCode = ( const queryIdPos = buf.reserveUint32() if (!ast.target) { - multiple(ast, buf, typeDef) + defaultMultiple(ast, buf, typeDef) } buf.pushUint64(schema.hash) From d69db8c08624c6036db6a2f321c87ca7f42992bc Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 29 Jan 2026 11:27:55 +0100 Subject: [PATCH 027/449] pushit --- native/modify/modify.zig | 15 ++++++--- native/types.zig | 4 +++ src/db-client/index.ts | 28 ++++++++++------- src/db-client/modify/index.ts | 47 ++++++++++++++++++++++------ src/db-client/modify/types.ts | 8 ++--- src/schema/defs/props/references.ts | 26 +++++++--------- src/utils/AutoSizedUint8Array.ts | 3 +- src/zigTsExports.ts | 48 ++++++++++++++++++++++------- test/boolean.ts | 27 ++++++++-------- test/modify/boolean.ts | 43 ++++++++++++++++++++++++++ test/shared/index.ts | 12 ++++++++ test/youzi.ts | 7 ++--- 12 files changed, 193 insertions(+), 75 deletions(-) create mode 100644 test/modify/boolean.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index d4e7e8b520..2a4f4e571d 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -446,7 +446,7 @@ pub fn modify( const items = utils.toSlice(t.ModifyResultItem, result[j..]); while (i < buf.len) { const op: t.Modify = @enumFromInt(buf[i]); - + // std.debug.print("op: {any}\n", .{op}); switch (op) { .create => { const create = utils.read(t.ModifyCreateHeader, buf, i); @@ -455,6 +455,7 @@ pub fn modify( const data: []u8 = buf[i .. i + create.size]; const id = db.ids[create.type - 1] + 1; const node = try Node.upsertNode(typeEntry, id); + // std.debug.print("create id: {any}\n", .{id}); modifyProps(db, typeEntry, node, data, items) catch { // handle errors }; @@ -468,8 +469,10 @@ pub fn modify( const update = utils.read(t.ModifyUpdateHeader, buf, i); i += utils.sizeOf(t.ModifyUpdateHeader); const typeEntry = try Node.getType(db, update.type); - utils.write(result, update.id, j); - if (Node.getNode(typeEntry, update.id)) |node| { + var id = update.id; + if (update.isTmp) id = items[id].id; + utils.write(result, id, j); + if (Node.getNode(typeEntry, id)) |node| { const data: []u8 = buf[i .. i + update.size]; modifyProps(db, typeEntry, node, data, items) catch { // handle errors @@ -485,8 +488,10 @@ pub fn modify( const delete = utils.read(t.ModifyDeleteHeader, buf, i); i += utils.sizeOf(t.ModifyDeleteHeader); const typeEntry = try Node.getType(db, delete.type); - utils.write(result, delete.id, j); - if (Node.getNode(typeEntry, delete.id)) |node| { + var id = delete.id; + if (delete.isTmp) id = items[id].id; + utils.write(result, id, j); + if (Node.getNode(typeEntry, id)) |node| { Node.deleteNode(db, typeEntry, node) catch { // handle errors }; diff --git a/native/types.zig b/native/types.zig index 1016c9f87b..92ba429ff0 100644 --- a/native/types.zig +++ b/native/types.zig @@ -91,6 +91,8 @@ pub const ModifyHeader = packed struct { pub const ModifyUpdateHeader = packed struct { op: Modify, type: u8, + isTmp: bool, + _padding: u7, id: u32, size: u32, }; @@ -98,6 +100,8 @@ pub const ModifyUpdateHeader = packed struct { pub const ModifyDeleteHeader = packed struct { op: Modify, type: u8, + isTmp: bool, + _padding: u7, id: u32, }; diff --git a/src/db-client/index.ts b/src/db-client/index.ts index a9298783d6..44f64fa8f5 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -20,7 +20,7 @@ import { serializeUpdate, ModifyCtx, flush, - ModifyCmd, + BasedModify, } from './modify/index.js' import type { InferPayload } from './modify/types.js' @@ -31,6 +31,10 @@ type DbClientOpts = { debug?: boolean } +type BasedCreatePromise = BasedModify +type BasedUpdatePromise = BasedModify +type BasedDeletePromise = BasedModify + export type ModifyOpts = { unsafe?: boolean locale?: keyof typeof LangCode @@ -95,8 +99,8 @@ export class DbClient = SchemaOut> extends DbShared { type: T, obj: InferPayload[T], opts?: ModifyOpts, - ): ModifyCmd { - return new ModifyCmd( + ): BasedCreatePromise { + return new BasedModify( this.modifyCtx, serializeCreate, this.schema!, @@ -109,30 +113,32 @@ export class DbClient = SchemaOut> extends DbShared { update( type: T, - id: number, + target: number | BasedModify, obj: InferPayload[T], opts?: ModifyOpts, - ): ModifyCmd { - return new ModifyCmd( + ): BasedUpdatePromise { + return new BasedModify( this.modifyCtx, serializeUpdate, this.schema!, type, - id, + target, obj, this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, ) } - delete(type: keyof S['types'] & string, id: number): ModifyCmd { - return new ModifyCmd( + delete( + type: keyof S['types'] & string, + target: number | BasedModify, + ): BasedDeletePromise { + return new BasedModify( this.modifyCtx, serializeDelete, this.schema!, type, - // TODO make it perf - id, + target, this.modifyCtx.buf, ) } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 8cdc6de2a1..97bb8fbc08 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -82,21 +82,39 @@ export const serializeCreate = < writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) } +export const getRealId = (item: unknown) => { + if (typeof item === 'number') return item + if (item instanceof BasedModify) return item.id +} + +export const getTmpId = (item: unknown) => { + if (item instanceof BasedModify) return item.tmpId +} + export const serializeUpdate = < S extends SchemaOut = SchemaOut, T extends keyof S['types'] & string = keyof S['types'] & string, >( schema: S, type: T, - id: number, + item: number | BasedModify, payload: InferPayload[T], buf: AutoSizedUint8Array, lang: LangCodeEnum, ) => { const typeDef = getTypeDef(schema, type) + const realId = getRealId(item) + const id = realId || getTmpId(item) + if (id === undefined) { + if (item instanceof BasedModify) { + throw item + } + throw new Error('Invalid id') + } const index = pushModifyUpdateHeader(buf, { op: Modify.update, type: typeDef.id, + isTmp: !realId, id, size: 0, }) @@ -111,13 +129,22 @@ export const serializeDelete = < >( schema: S, type: T, - id: number, + item: number | BasedModify, buf: AutoSizedUint8Array, ) => { const typeDef = getTypeDef(schema, type) + const realId = getRealId(item) + const id = realId || getTmpId(item) + if (id === undefined) { + if (item instanceof BasedModify) { + throw item + } + throw new Error('Invalid id') + } pushModifyDeleteHeader(buf, { op: Modify.delete, type: typeDef.id, + isTmp: !realId, id, }) } @@ -129,8 +156,8 @@ type ModifySerializer = type ModifyBatch = { count: number - promises?: ModifyCmd[] - dependents?: ModifyCmd[] + promises?: BasedModify[] + dependents?: BasedModify[] result?: Uint8Array flushed?: true } @@ -139,7 +166,7 @@ export type ModifyCtx = { buf: AutoSizedUint8Array batch: ModifyBatch flushTime: number - lastModify?: ModifyCmd + lastModify?: BasedModify flushTimer?: NodeJS.Timeout | true | undefined hooks: { flushModify: (buf: Uint8Array) => Promise @@ -198,10 +225,10 @@ const schedule = (ctx: ModifyCtx) => { } } -export class ModifyCmd +export class BasedModify any = ModifySerializer> implements Promise { - [Symbol.toStringTag]!: 'ModifyCmd' + [Symbol.toStringTag]!: 'BasedModify' constructor(ctx: ModifyCtx, serialize: S, ...args: Parameters) { this._exec(ctx, serialize, ...args) } @@ -243,7 +270,7 @@ export class ModifyCmd private _id?: number private _error?: Error - private _blocker?: ModifyCmd + private _blocker?: BasedModify private _index?: number private _batch?: ModifyBatch private _promise?: Promise @@ -277,8 +304,8 @@ export class ModifyCmd flush(ctx) this._exec.apply(this, arguments) return - } else if (e instanceof ModifyCmd) { - let blocker: ModifyCmd = e + } else if (e instanceof BasedModify) { + let blocker: BasedModify = e while (blocker._blocker) blocker = blocker._blocker blocker._batch!.dependents ??= [] blocker._batch!.dependents.push(this) diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 1765fcaa7a..7340211db0 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -1,6 +1,6 @@ import { type SchemaTypes } from '../../schema.js' -import type { ModifyCmd } from './index.js' +import type { BasedModify } from './index.js' type TypedArray = | Uint8Array @@ -43,15 +43,15 @@ type InferEdgeProps = { type InferRefValue = | number - | ModifyCmd - | ({ id: number | ModifyCmd } & InferEdgeProps) + | BasedModify + | ({ id: number | BasedModify } & InferEdgeProps) type InferReferences = | InferRefValue[] | { add?: InferRefValue[] update?: InferRefValue[] - delete?: (number | ModifyCmd)[] + delete?: (number | BasedModify)[] } type InferProp = Prop extends { type: 'object'; props: infer P } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 1eefebb830..6c5b15b6d9 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -16,7 +16,12 @@ import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { SchemaProp } from '../../../schema.js' import { BasePropDef } from './base.js' import type { PropDef, TypeDef } from '../index.js' -import { ModifyCmd, serializeProps } from '../../../db-client/modify/index.js' +import { + BasedModify, + getRealId, + getTmpId, + serializeProps, +} from '../../../db-client/modify/index.js' type Edges = Record<`${string}`, unknown> | undefined @@ -49,7 +54,7 @@ const serializeIds = ( const serializeTmpIds = ( buf: AutoSizedUint8Array, - items: ModifyCmd[], + items: BasedModify[], offset: number, ): undefined | any => { let i = offset @@ -113,15 +118,6 @@ const serializeIdsAndMeta = ( return i } -const getRealId = (item: any) => { - if (typeof item === 'number') return item - if (item instanceof ModifyCmd) return item.id -} - -const getTmpId = (item: any) => { - if (item instanceof ModifyCmd) return item.tmpId -} - const isValidRefObj = (item: any) => { if (typeof item === 'object' && item !== null) { return getRealId(item.id) || getTmpId(item.id) !== undefined @@ -162,9 +158,9 @@ const setReferences = ( const start = buf.length offset = serializeIdsAndMeta(buf, value, op, offset, lang, prop.edges) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - } else if (item instanceof ModifyCmd) { + } else if (item instanceof BasedModify) { throw item - } else if (typeof item === 'object' && item?.id instanceof ModifyCmd) { + } else if (typeof item === 'object' && item?.id instanceof BasedModify) { throw item.id } else { console.log('??', item, value) @@ -257,7 +253,7 @@ export const reference = class Reference extends BasePropDef { return } - if (value instanceof ModifyCmd) { + if (value instanceof BasedModify) { throw value } @@ -286,7 +282,7 @@ export const reference = class Reference extends BasePropDef { return } - if (value.id instanceof ModifyCmd) { + if (value.id instanceof BasedModify) { throw value.id } } diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 55948fb5f1..3372d70701 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -4,10 +4,9 @@ import { writeFloatLE, writeUint64, writeInt64, + ENCODER, } from './index.js' -const ENCODER = new TextEncoder() - // Runtime method injection const delegateMethods = [ 'toString', diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 20ba2ddbfa..0cd568ef58 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -294,11 +294,12 @@ export const pushModifyHeader = ( export type ModifyUpdateHeader = { op: ModifyEnum type: number + isTmp: boolean id: number size: number } -export const ModifyUpdateHeaderByteSize = 10 +export const ModifyUpdateHeaderByteSize = 11 export const ModifyUpdateHeaderAlignOf = 16 @@ -311,6 +312,10 @@ export const writeModifyUpdateHeader = ( offset += 1 buf[offset] = Number(header.type) offset += 1 + buf[offset] = 0 + buf[offset] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= ((0 >>> 0) & 127) << 1 + offset += 1 writeUint32(buf, Number(header.id), offset) offset += 4 writeUint32(buf, Number(header.size), offset) @@ -325,11 +330,14 @@ export const writeModifyUpdateHeaderProps = { type: (buf: Uint8Array, value: number, offset: number) => { buf[offset + 1] = Number(value) }, + isTmp: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + }, id: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 2) + writeUint32(buf, Number(value), offset + 3) }, size: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 6) + writeUint32(buf, Number(value), offset + 7) }, } @@ -340,8 +348,9 @@ export const readModifyUpdateHeader = ( const value: ModifyUpdateHeader = { op: (buf[offset]) as ModifyEnum, type: buf[offset + 1], - id: readUint32(buf, offset + 2), - size: readUint32(buf, offset + 6), + isTmp: (((buf[offset + 2] >>> 0) & 1)) === 1, + id: readUint32(buf, offset + 3), + size: readUint32(buf, offset + 7), } return value } @@ -349,8 +358,9 @@ export const readModifyUpdateHeader = ( export const readModifyUpdateHeaderProps = { op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, type: (buf: Uint8Array, offset: number) => buf[offset + 1], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { @@ -366,6 +376,9 @@ export const pushModifyUpdateHeader = ( const index = buf.length buf.pushUint8(Number(header.op)) buf.pushUint8(Number(header.type)) + buf.pushUint8(0) + buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 buf.pushUint32(Number(header.id)) buf.pushUint32(Number(header.size)) return index @@ -374,10 +387,11 @@ export const pushModifyUpdateHeader = ( export type ModifyDeleteHeader = { op: ModifyEnum type: number + isTmp: boolean id: number } -export const ModifyDeleteHeaderByteSize = 6 +export const ModifyDeleteHeaderByteSize = 7 export const ModifyDeleteHeaderAlignOf = 8 @@ -390,6 +404,10 @@ export const writeModifyDeleteHeader = ( offset += 1 buf[offset] = Number(header.type) offset += 1 + buf[offset] = 0 + buf[offset] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= ((0 >>> 0) & 127) << 1 + offset += 1 writeUint32(buf, Number(header.id), offset) offset += 4 return offset @@ -402,8 +420,11 @@ export const writeModifyDeleteHeaderProps = { type: (buf: Uint8Array, value: number, offset: number) => { buf[offset + 1] = Number(value) }, + isTmp: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + }, id: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 2) + writeUint32(buf, Number(value), offset + 3) }, } @@ -414,7 +435,8 @@ export const readModifyDeleteHeader = ( const value: ModifyDeleteHeader = { op: (buf[offset]) as ModifyEnum, type: buf[offset + 1], - id: readUint32(buf, offset + 2), + isTmp: (((buf[offset + 2] >>> 0) & 1)) === 1, + id: readUint32(buf, offset + 3), } return value } @@ -422,7 +444,8 @@ export const readModifyDeleteHeader = ( export const readModifyDeleteHeaderProps = { op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, type: (buf: Uint8Array, offset: number) => buf[offset + 1], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), } export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { @@ -438,6 +461,9 @@ export const pushModifyDeleteHeader = ( const index = buf.length buf.pushUint8(Number(header.op)) buf.pushUint8(Number(header.type)) + buf.pushUint8(0) + buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 buf.pushUint32(Number(header.id)) return index } diff --git a/test/boolean.ts b/test/boolean.ts index f3378463ed..92efef8ebe 100644 --- a/test/boolean.ts +++ b/test/boolean.ts @@ -10,7 +10,7 @@ await test('boolean', async (t) => { // t.after(() => t.backup(db)) t.after(() => db.stop(true)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -20,35 +20,38 @@ await test('boolean', async (t) => { }, }) - const user1 = await db.create('user', {}) + await client.create('user', {}) - db.create('user', { + await client.create('user', { isNice: true, }) - db.create('user', { + await client.create('user', { isNice: false, }) - await db.drain() + await client.drain() - deepEqual((await db.query('user').get()).toObject(), [ + deepEqual((await client.query('user').get()).toObject(), [ { id: 1, isNice: false }, { id: 2, isNice: true }, { id: 3, isNice: false }, ]) deepEqual( - (await db.query('user').filter('isNice', '=', true).get()).toObject(), + (await client.query('user').filter('isNice', '=', true).get()).toObject(), [{ id: 2, isNice: true }], ) - deepEqual((await db.query('user').filter('isNice').get()).toObject(), [ + deepEqual((await client.query('user').filter('isNice').get()).toObject(), [ { id: 2, isNice: true }, ]) - deepEqual((await db.query('user').filter('isNice', false).get()).toObject(), [ - { id: 1, isNice: false }, - { id: 3, isNice: false }, - ]) + deepEqual( + (await client.query('user').filter('isNice', '=', false).get()).toObject(), + [ + { id: 1, isNice: false }, + { id: 3, isNice: false }, + ], + ) }) diff --git a/test/modify/boolean.ts b/test/modify/boolean.ts new file mode 100644 index 0000000000..4597abcd4c --- /dev/null +++ b/test/modify/boolean.ts @@ -0,0 +1,43 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('modify boolean', async (t) => { + const db = await testDb(t, { + types: { + user: { + isNice: 'boolean', + }, + }, + }) + + const a = db.create('user', {}) + const b = db.create('user', { isNice: true }) + const c = db.create('user', { isNice: false }) + + deepEqual(await db.query('user').get(), [ + { id: 1, isNice: false }, + { id: 2, isNice: true }, + { id: 3, isNice: false }, + ]) + + db.update('user', a, { isNice: true }) + db.update('user', b, { isNice: true }) + db.update('user', c, { isNice: true }) + + deepEqual(await db.query('user').get(), [ + { id: 1, isNice: true }, + { id: 2, isNice: true }, + { id: 3, isNice: true }, + ]) + + db.update('user', a, { isNice: false }) + db.update('user', b, { isNice: false }) + db.update('user', c, { isNice: false }) + + deepEqual(await db.query('user').get(), [ + { id: 1, isNice: false }, + { id: 2, isNice: false }, + { id: 3, isNice: false }, + ]) +}) diff --git a/test/shared/index.ts b/test/shared/index.ts index c61c217401..add7e3d6ca 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,3 +1,5 @@ +import type { ResolveSchema, SchemaIn } from '../../src/schema.js' +import { BasedDb, type DbClient } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' export * from './examples.js' @@ -30,3 +32,13 @@ export function logMemoryUsage() { console.log(` arrayBuffers: ${formatBytes(memoryUsage.arrayBuffers)}`) } } + +export const testDb = async ( + t, + schema: S, +): Promise>> => { + const db = new BasedDb({ path: t.tmp }) + await db.start({ clean: true }) + t.after(() => db.destroy()) + return db.setSchema(schema) +} diff --git a/test/youzi.ts b/test/youzi.ts index a00e484fb6..0b60b6a016 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -1,12 +1,12 @@ import { BasedDb } from '../src/index.js' import { AutoSizedUint8Array } from '../src/utils/AutoSizedUint8Array.js' +import { LangCode, pushModifyHeader } from '../src/zigTsExports.js' import { flush, getTypeDefs, serializeCreate, } from '../src/db-client/modify/index.js' import { parseSchema } from '../src/schema.js' -import { LangCode, Modify, pushModifyHeader } from '../src/zigTsExports.js' import test from './shared/test.js' await test('schema defs', async (t) => { @@ -59,7 +59,6 @@ await test.skip('modify raw', async (t) => { user: { age: 'number', rating: 'uint8', - // TODO refs have to be ordered friends: { items: { ref: 'user', @@ -107,8 +106,6 @@ await test.skip('modify raw', async (t) => { await db.server.modify(buf.view) - console.log('done did it!') - const res = await db.query('user').include('*', 'friends.*').get().toObject() console.dir(res, { depth: null }) }) @@ -144,7 +141,7 @@ await test('modify client', async (t) => { name: 'youzi', }) - // olli uses ModifyCmd for youzi + // olli uses BasedModify for youzi const olli = client.create('user', { name: 'olli', friends: { add: [youzi] }, From 314d1471de4e8e4b7032ef72d345022c2a6852e7 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 29 Jan 2026 11:31:09 +0100 Subject: [PATCH 028/449] Use AutoSizedUint8Array for selvaBuffer --- native/types.zig | 8 +++ src/db-server/schema.ts | 4 +- src/db-server/schemaSelvaBuffer.ts | 111 ++++++++++++++--------------- src/zigTsExports.ts | 89 +++++++++++++++++++++++ 4 files changed, 154 insertions(+), 58 deletions(-) diff --git a/native/types.zig b/native/types.zig index 2f46730667..22e049306b 100644 --- a/native/types.zig +++ b/native/types.zig @@ -893,6 +893,14 @@ pub const FilterSelect = packed struct { // you want EDGE INDEX as well }; +pub const SelvaSchemaHeader = packed struct { + blockCapacity: u32, + nrFields: u8, + nrFixedFields: u8, + nrVirtualFields: u8, + sdbVersion: u8, +}; + pub const SelvaSchemaMicroBuffer = packed struct { type: SelvaFieldType, len: u16, diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index 0d4ba632a7..ebcbc74757 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -105,11 +105,11 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { let maxTid = 0 await Promise.all( - s.map(async (ab, i) => { + s.map(async (buf, i) => { const type = server.schemaTypesParsed[types[i]] maxTid = Math.max(maxTid, type.id) try { - await createSelvaType(server, type.id, new Uint8Array(ab)) + await createSelvaType(server, type.id, buf) } catch (err) { throw new Error( `Cannot update schema on selva (native) ${type.type} ${err.message}`, diff --git a/src/db-server/schemaSelvaBuffer.ts b/src/db-server/schemaSelvaBuffer.ts index 53d08d0d47..910ebe9734 100644 --- a/src/db-server/schemaSelvaBuffer.ts +++ b/src/db-server/schemaSelvaBuffer.ts @@ -7,6 +7,12 @@ import { LangCode, PropType, PropTypeEnum, + pushSelvaSchemaColvec, + pushSelvaSchemaHeader, + pushSelvaSchemaMicroBuffer, + pushSelvaSchemaRef, + pushSelvaSchemaString, + pushSelvaSchemaText, writeSelvaSchemaMicroBuffer, } from '../zigTsExports.js' import { @@ -18,6 +24,7 @@ import { } from '../schema/index.js' // import { write as writeString } from '../db-client/string.js' import { fillEmptyMain } from '../schema/def/fillEmptyMain.js' +import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' // import { Ctx } from '../db-client/_modify/Ctx.js' const selvaFieldType: Readonly> = { @@ -75,51 +82,42 @@ function makeEdgeConstraintFlags(prop: PropDef): number { } const propDefBuffer = ( + schemaBuf: AutoSizedUint8Array, schema: { [key: string]: SchemaTypeDef }, prop: PropDef, -): number[] => { +): void => { const type = prop.typeIndex const selvaType = selvaTypeMap[type] if (prop.len && (type === PropType.microBuffer || type === PropType.vector)) { - const buf = new Uint8Array(4) - writeSelvaSchemaMicroBuffer( - buf, - { - type: selvaType, - len: prop.len, - hasDefault: ~~!!prop.default, - }, - 0, - ) + pushSelvaSchemaMicroBuffer(schemaBuf, { + type: selvaType, + len: prop.len, + hasDefault: ~~!!prop.default, + }) if (prop.default) { - return [...buf, ...prop.default] - } else { - return [...buf] + schemaBuf.set(prop.default, schemaBuf.length) } } else if (prop.len && type === PropType.colVec) { const baseSize = VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] - return [ - ...createSelvaSchemaColvec({ - type: selvaType, - vecLen: prop.len / baseSize, - compSize: baseSize, - hasDefault: 0, - }), - ] // TODO Add support for default + pushSelvaSchemaColvec(schemaBuf, { + type: selvaType, + vecLen: prop.len / baseSize, + compSize: baseSize, + hasDefault: 0, + }) + // TODO Add support for default } else if (type === PropType.reference || type === PropType.references) { const dstType: SchemaTypeDef = schema[prop.inverseTypeName!] - return [ - ...createSelvaSchemaRef({ - type: selvaType, - flags: makeEdgeConstraintFlags(prop), - dstNodeType: dstType.id, - inverseField: prop.inversePropNumber!, - edgeNodeType: prop.edgeNodeTypeId ?? 0, - capped: prop.referencesCapped ?? 0, - }), - ] + pushSelvaSchemaRef(schemaBuf, { + type: selvaType, + flags: makeEdgeConstraintFlags(prop), + dstNodeType: dstType.id, + inverseField: prop.inversePropNumber!, + edgeNodeType: prop.edgeNodeTypeId ?? 0, + capped: prop.referencesCapped ?? 0, + }) } else if ( type === PropType.string || type === PropType.binary || @@ -153,22 +151,18 @@ const propDefBuffer = ( // return [...buf] } else { - return [ - ...createSelvaSchemaString({ - type: selvaType, - fixedLen: prop.len < 50 ? prop.len : 0, - defaultLen: 0, - }), - ] + pushSelvaSchemaString(schemaBuf, { + type: selvaType, + fixedLen: prop.len < 50 ? prop.len : 0, + defaultLen: 0, + }) } } else if (type === PropType.text) { // TODO Defaults - return [ - ...createSelvaSchemaText({ - type: selvaType, - nrDefaults: Object.keys(prop.default).length, - }), - ] + pushSelvaSchemaText(schemaBuf, { + type: selvaType, + nrDefaults: Object.keys(prop.default).length, + }) //for (const langName in prop.default) { // console.warn('TODO default!!') @@ -184,13 +178,14 @@ const propDefBuffer = ( // writeUint32(buf, l, 0) // length of the default // fs.push(...buf) //} + } else { + schemaBuf.pushUint8(selvaType) } - return [selvaType] } export function schemaToSelvaBuffer(schema: { [key: string]: SchemaTypeDef -}): ArrayBuffer[] { +}): Uint8Array[] { return Object.values(schema).map((t) => { const props: PropDef[] = Object.values(t.props) const rest: PropDef[] = [] @@ -209,6 +204,8 @@ export function schemaToSelvaBuffer(schema: { len: mainLen, } + const buf = new AutoSizedUint8Array(4, 65536) + for (const f of props) { if (!f.separate) { continue @@ -233,14 +230,16 @@ export function schemaToSelvaBuffer(schema: { } rest.sort((a, b) => a.prop - b.prop) - return Uint8Array.from([ - ...blockCapacity(t.blockCapacity), // u32 blockCapacity - nrFields, // u8 nrFields - nrFixedFields, // u8 nrFixedFields - virtualFields, // u8 nrVirtualFields - 8, // u8 version (generally follows the sdb version) - ...propDefBuffer(schema, main), - ...rest.map((f) => propDefBuffer(schema, f)).flat(1), - ]).buffer + + pushSelvaSchemaHeader(buf, { + blockCapacity: t.blockCapacity, + nrFields, + nrFixedFields, + nrVirtualFields: virtualFields, + sdbVersion: 8, + }) + propDefBuffer(buf, schema, main) + rest.forEach((f) => propDefBuffer(buf, schema, f)) + return buf.slice() // Doing a slice might reduce the mem pressure? }) } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 311e16e96b..f4c2ffc689 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -4136,6 +4136,95 @@ export const pushFilterSelect = ( return index } +export type SelvaSchemaHeader = { + blockCapacity: number + nrFields: number + nrFixedFields: number + nrVirtualFields: number + sdbVersion: number +} + +export const SelvaSchemaHeaderByteSize = 8 + +export const SelvaSchemaHeaderAlignOf = 8 + +export const writeSelvaSchemaHeader = ( + buf: Uint8Array, + header: SelvaSchemaHeader, + offset: number, +): number => { + writeUint32(buf, Number(header.blockCapacity), offset) + offset += 4 + buf[offset] = Number(header.nrFields) + offset += 1 + buf[offset] = Number(header.nrFixedFields) + offset += 1 + buf[offset] = Number(header.nrVirtualFields) + offset += 1 + buf[offset] = Number(header.sdbVersion) + offset += 1 + return offset +} + +export const writeSelvaSchemaHeaderProps = { + blockCapacity: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset) + }, + nrFields: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 4] = Number(value) + }, + nrFixedFields: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 5] = Number(value) + }, + nrVirtualFields: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 6] = Number(value) + }, + sdbVersion: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 7] = Number(value) + }, +} + +export const readSelvaSchemaHeader = ( + buf: Uint8Array, + offset: number, +): SelvaSchemaHeader => { + const value: SelvaSchemaHeader = { + blockCapacity: readUint32(buf, offset), + nrFields: buf[offset + 4], + nrFixedFields: buf[offset + 5], + nrVirtualFields: buf[offset + 6], + sdbVersion: buf[offset + 7], + } + return value +} + +export const readSelvaSchemaHeaderProps = { + blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], + nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], + nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], + sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], +} + +export const createSelvaSchemaHeader = (header: SelvaSchemaHeader): Uint8Array => { + const buffer = new Uint8Array(SelvaSchemaHeaderByteSize) + writeSelvaSchemaHeader(buffer, header, 0) + return buffer +} + +export const pushSelvaSchemaHeader = ( + buf: AutoSizedUint8Array, + header: SelvaSchemaHeader, +): number => { + const index = buf.length + buf.pushUint32(Number(header.blockCapacity)) + buf.pushUint8(Number(header.nrFields)) + buf.pushUint8(Number(header.nrFixedFields)) + buf.pushUint8(Number(header.nrVirtualFields)) + buf.pushUint8(Number(header.sdbVersion)) + return index +} + export type SelvaSchemaMicroBuffer = { type: SelvaFieldType len: number From 651f3c12883baa7a0ca0de9cad85a53c8ea937d8 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 12:48:51 +0100 Subject: [PATCH 029/449] main partial --- native/query/multiple.zig | 5 +- src/db-client/query/include/toByteCode.ts | 3 +- src/db-query/ast.ts | 5 -- src/db-query/toByteCode/collect.ts | 37 --------- src/db-query/toByteCode/include.ts | 95 +++++++++++++++++++++-- src/db-query/toByteCode/multiple.ts | 34 +++----- src/db-query/toByteCode/toByteCode.ts | 10 +-- test/query-ast/include.ts | 56 ++++++++++--- 8 files changed, 154 insertions(+), 91 deletions(-) delete mode 100644 src/db-query/toByteCode/collect.ts diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 2a30666992..25191f6634 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -22,7 +22,7 @@ fn iterator( ctx: *Query.QueryCtx, q: []u8, it: anytype, - header: *const t.QueryHeader, + header: *const t.QueryHeader, // make this type typeEntry: Node.Type, i: *usize, ) !u32 { @@ -150,6 +150,9 @@ pub fn default( var i: usize = 0; // make default header! const header = utils.readNext(t.QueryHeader, q, &i); + + std.debug.print("flap {any} \n", .{header}); + const sizeIndex = try ctx.thread.query.reserve(4); const typeEntry = try Node.getType(ctx.db, header.typeId); var nodeCnt: u32 = 0; diff --git a/src/db-client/query/include/toByteCode.ts b/src/db-client/query/include/toByteCode.ts index 28102c1a20..95490dc9d5 100644 --- a/src/db-client/query/include/toByteCode.ts +++ b/src/db-client/query/include/toByteCode.ts @@ -76,7 +76,7 @@ export const includeToBuffer = ( i, ) // This writes the actual address of the prop to be used on read - value[0] = m + value[0] = m // this is for the reader i += 4 m += propDef.len } @@ -92,6 +92,7 @@ export const includeToBuffer = ( } else { for (const [start, value] of def.include.main.include.entries()) { value[0] = start + // this is for reading } result.push( createIncludeHeader({ diff --git a/src/db-query/ast.ts b/src/db-query/ast.ts index 3d99ddf928..5914967463 100644 --- a/src/db-query/ast.ts +++ b/src/db-query/ast.ts @@ -38,11 +38,6 @@ export type QueryAst = { > } -export type QueryAstCtx = { - main: PropDef[] - // more? -} - // const x: QueryAst = { // type: 'user', // locale: 'nl', diff --git a/src/db-query/toByteCode/collect.ts b/src/db-query/toByteCode/collect.ts deleted file mode 100644 index bc3fa97dba..0000000000 --- a/src/db-query/toByteCode/collect.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { TypeDef } from '../../schema/defs/index.js' -import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import { IncludeOp, PropType, pushIncludeHeader } from '../../zigTsExports.js' -import { QueryAst, QueryAstCtx } from '../ast.js' -import { includeProp } from './include.js' - -export const collect = ( - ast: QueryAst, - buf: AutoSizedUint8Array, - typeDef: TypeDef, - path: string[], - ctx: QueryAstCtx = { main: [] }, -) => { - for (const field in ast.props) { - const propDef = typeDef.props.get(field) - if (propDef) { - // LANGUAGE - // $EDGE - if (propDef.type === PropType.reference) { - // REFERENCE - } else if (propDef.type === PropType.references) { - // REFERENCES - } else if (propDef.id === 0) { - ctx.main.push(propDef) - } else { - includeProp(buf, propDef) - } - } else { - if ('props' in ast) { - collect(ast, buf, typeDef, [...path, field], ctx) - } else { - throw new Error(`Prop does not exist ${field}`) - } - } - } - return ctx -} diff --git a/src/db-query/toByteCode/include.ts b/src/db-query/toByteCode/include.ts index aa6489a489..6632840fa2 100644 --- a/src/db-query/toByteCode/include.ts +++ b/src/db-query/toByteCode/include.ts @@ -1,16 +1,95 @@ -import { PropDef } from '../../schema/defs/index.js' +import { PropDef, TypeDef } from '../../schema/defs/index.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import { IncludeOp, pushIncludeHeader } from '../../zigTsExports.js' +import { + IncludeOp, + MAIN_PROP, + PropType, + pushIncludeHeader, + pushIncludePartialHeader, + pushIncludePartialProp, +} from '../../zigTsExports.js' +import { QueryAst } from '../ast.js' -export const includeProp = (buf: AutoSizedUint8Array, propDef: PropDef) => { - // derp +type IncludeCtx = { + main: PropDef[] +} + +const includeProp = (buf: AutoSizedUint8Array, prop: PropDef) => { pushIncludeHeader(buf, { op: IncludeOp.default, - prop: propDef.id, - propType: propDef.type, + prop: prop.id, + propType: prop.type, }) } -export const includeProps = (buf: AutoSizedUint8Array, propDef: PropDef[]) => { - // sort all main on start +const includeMainProps = ( + buf: AutoSizedUint8Array, + props: PropDef[], + typeDef: TypeDef, +) => { + props.sort((a, b) => (a.start < b.start ? -1 : a.start === b.start ? 0 : 1)) + if (props.length === typeDef.main.length) { + pushIncludeHeader(buf, { + op: IncludeOp.default, + prop: 0, + propType: PropType.microBuffer, + }) + } else { + pushIncludePartialHeader(buf, { + op: IncludeOp.partial, + prop: MAIN_PROP, + propType: PropType.microBuffer, + amount: props.length, + }) + for (const { start, size } of props) { + pushIncludePartialProp(buf, { + start, + size, + }) + } + } +} + +export const collect = ( + ast: QueryAst, + buf: AutoSizedUint8Array, + typeDef: TypeDef, + path: string[], + ctx: IncludeCtx = { main: [] }, +) => { + for (const field in ast.props) { + const propDef = typeDef.props.get(field) + if (propDef) { + // LANGUAGE + // $EDGE + if (propDef.type === PropType.reference) { + // REFERENCE + } else if (propDef.type === PropType.references) { + // REFERENCES + } else if (propDef.id === 0) { + ctx.main.push(propDef) + } else { + includeProp(buf, propDef) + } + } else { + if ('props' in ast) { + collect(ast, buf, typeDef, [...path, field], ctx) + } else { + throw new Error(`Prop does not exist ${field}`) + } + } + } + return ctx +} + +export const include = ( + ast: QueryAst, + buf: AutoSizedUint8Array, + typeDef: TypeDef, +): number => { + const includeStart = buf.length + const ctx = collect(ast, buf, typeDef, []) + includeMainProps(buf, ctx.main, typeDef) + const includeSize = buf.length - includeStart + return includeSize } diff --git a/src/db-query/toByteCode/multiple.ts b/src/db-query/toByteCode/multiple.ts index 23a091f960..37c0f3546b 100644 --- a/src/db-query/toByteCode/multiple.ts +++ b/src/db-query/toByteCode/multiple.ts @@ -4,13 +4,10 @@ import { pushQueryHeader, QueryType, ID_PROP, - QueryHeaderByteSize, - readQueryHeaderProps, writeQueryHeaderProps, } from '../../zigTsExports.js' -import { QueryAst, QueryAstCtx } from '../ast.js' -import { collect } from './collect.js' -import { includeProps } from './include.js' +import { QueryAst } from '../ast.js' +import { include } from './include.js' import { getIteratorType } from './iteratorType.js' export const defaultMultiple = ( @@ -18,36 +15,27 @@ export const defaultMultiple = ( buf: AutoSizedUint8Array, typeDef: TypeDef, ) => { - let startOffset = buf.length - - const offset = ast.range?.start || 0 - - // now include - - // MAKE THIS DEFAULT + const rangeStart = ast.range?.start || 0 const queryHeaderOffset = pushQueryHeader(buf, { op: QueryType.default, prop: ID_PROP, includeSize: 0, typeId: typeDef.id, - offset, - limit: (ast.range?.end || 1000) + offset, // fix + offset: rangeStart, + limit: (ast.range?.end || 1000) + rangeStart, sort: false, filterSize: 0, searchSize: 0, iteratorType: getIteratorType(), - // make this optional? edgeTypeId: 0, edgeSize: 0, - edgeFilterSize: 0, // this is nice + edgeFilterSize: 0, size: 0, // total size + include // only used in ids now maybe remove? // const buffer = new Uint8Array(QueryHeaderByteSize + searchSize + sortSize) }) - - // make fn for this - const includeStart = buf.length - const ctx = collect(ast, buf, typeDef, []) - includeProps(buf, ctx.main) - const includeSize = buf.length - includeStart - writeQueryHeaderProps.includeSize(buf.data, includeSize, 0) + writeQueryHeaderProps.includeSize( + buf.data, + include(ast, buf, typeDef), + queryHeaderOffset, + ) } diff --git a/src/db-query/toByteCode/toByteCode.ts b/src/db-query/toByteCode/toByteCode.ts index 6619817001..9b16f7c014 100644 --- a/src/db-query/toByteCode/toByteCode.ts +++ b/src/db-query/toByteCode/toByteCode.ts @@ -1,4 +1,5 @@ import { crc32 } from '../../db-client/crc32.js' +import { registerQuery } from '../../db-client/query/registerQuery.js' import { PropDef, SchemaOut } from '../../schema.js' import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' import { TypeDef } from '../../schema/defs/index.js' @@ -9,7 +10,10 @@ import { defaultMultiple } from './multiple.js' export const queryAstToByteCode = ( schema: SchemaOut, ast: QueryAst, + buf: AutoSizedUint8Array, ): Uint8Array => { + buf.length = 0 + if (!ast.type) { throw new Error('Query requires type') } @@ -21,8 +25,6 @@ export const queryAstToByteCode = ( throw new Error('Type does not exist') } - const buf = new AutoSizedUint8Array(100) - const queryIdPos = buf.reserveUint32() if (!ast.target) { @@ -32,7 +34,5 @@ export const queryAstToByteCode = ( buf.pushUint64(schema.hash) buf.writeUint32(crc32(buf.view), queryIdPos) - // buf.pack() - - return buf.view + return buf.view.slice() } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 103439ed7b..b71223c7d5 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,27 +1,61 @@ +import { registerQuery } from '../../src/db-client/query/registerQuery.js' import { QueryAst } from '../../src/db-query/ast.js' import { queryAstToByteCode } from '../../src/db-query/toByteCode/toByteCode.js' import { parseSchema, Schema } from '../../src/schema.js' -import { debugBuffer } from '../../src/sdk.js' +import { BasedDb, debugBuffer } from '../../src/sdk.js' +import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('include', async (t) => { - const schema: Schema = { + const db = new BasedDb({ path: t.tmp }) + await db.start({ clean: true }) + t.after(() => db.destroy()) + const client = await db.setSchema({ types: { user: { name: 'string', + x: 'boolean', + flap: 'uint32', + y: 'uint16', + // object }, }, - } + }) - const strictSchema = parseSchema(schema) + client.create('user', { name: 'AAAAAAAAAA', y: 67, x: true, flap: 9999 }) + client.create('user', { name: 'mr y' }) - const buf = queryAstToByteCode(strictSchema, { - type: 'user', - props: { - name: { include: {} }, - }, - }) + await db.drain() + + console.log('-------') + + let d = Date.now() + + const buf = new AutoSizedUint8Array(1000) + + let astB: Uint8Array = new Uint8Array() + for (let i = 0; i < 1e6; i++) { + // registerQuery(db.query('user').include('name', 'x', 'y')) + astB = queryAstToByteCode( + client.schema!, + { + type: 'user', + props: { + name: { include: {} }, + y: { include: {} }, + x: { include: {} }, + // flap: { include: {} }, + }, + }, + buf, + ) + } + console.log(Date.now() - d, 'ms') + + debugBuffer(astB) - debugBuffer(buf) + // console.log('--------------') + debugBuffer(await db.server.getQueryBuf(astB)) }) From 833863a5dadd4287c38108653f2614d975106fe0 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 17:05:58 +0100 Subject: [PATCH 030/449] fix --- native/query/multiple.zig | 5 +- native/query/single.zig | 4 ++ src/db-query/ast.ts | 33 +++++++---- src/db-query/toByteCode/include.ts | 87 +++++++++++++++++++---------- src/db-query/toByteCode/multiple.ts | 19 ++++--- src/db-query/toByteCode/single.ts | 36 ++++++++++++ src/schema/defs/index.ts | 4 ++ test/query-ast/include.ts | 51 ++++++++++++++--- 8 files changed, 177 insertions(+), 62 deletions(-) create mode 100644 src/db-query/toByteCode/single.ts diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 25191f6634..c4a949ded4 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -148,11 +148,8 @@ pub fn default( q: []u8, ) !void { var i: usize = 0; - // make default header! + // make default header! use :type in iterator const header = utils.readNext(t.QueryHeader, q, &i); - - std.debug.print("flap {any} \n", .{header}); - const sizeIndex = try ctx.thread.query.reserve(4); const typeEntry = try Node.getType(ctx.db, header.typeId); var nodeCnt: u32 = 0; diff --git a/native/query/single.zig b/native/query/single.zig index 011da8575d..79db4311fc 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -105,6 +105,7 @@ pub fn referenceEdge( i: *usize, ) !void { const header = utils.readNext(t.QueryHeaderSingleReference, q, i); + const fs = try Schema.getFieldSchema(fromType, header.prop); if (References.getReference(from, fs)) |ref| { const typeEntry = try Node.getType(ctx.db, header.typeId); @@ -121,6 +122,9 @@ pub fn referenceEdge( const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); + + std.debug.print("DERP ?? {any} mr node -> {any} \n", .{ e, node }); + if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); diff --git a/src/db-query/ast.ts b/src/db-query/ast.ts index 5914967463..7d455a5f5a 100644 --- a/src/db-query/ast.ts +++ b/src/db-query/ast.ts @@ -14,6 +14,15 @@ export type FilterAst = { } or?: FilterAst[] and?: FilterAst[] + edges?: FilterAst +} + +export type Include = { + glob?: '*' | '**' + meta?: true | 'only' | false + maxChars?: number + maxBytes?: number + raw?: boolean } export type QueryAst = { @@ -26,16 +35,11 @@ export type QueryAst = { props?: Record< string, QueryAst & { - include?: { - glob?: '*' | '**' - meta?: true | 'only' | false - maxChars?: number - maxBytes?: number - raw?: boolean - } + include?: Include select?: { start: number; end: number } } > + edges?: QueryAst } // const x: QueryAst = { @@ -53,11 +57,16 @@ export type QueryAst = { // props: { // readBy: { // filter: { -// props: { -// $rating: { -// ops: [{ val: 4, op: '>' }], -// }, -// }, +// edges: { +// $rating: { +// // ops: [{ val: 4, op: '>' }], +// // }, +// } +// props: { +// $rating: { +// ops: [{ val: 4, op: '>' }], +// }, +// }, // }, // props: { // name: { include: { meta: 'only' } }, diff --git a/src/db-query/toByteCode/include.ts b/src/db-query/toByteCode/include.ts index 6632840fa2..b757848c87 100644 --- a/src/db-query/toByteCode/include.ts +++ b/src/db-query/toByteCode/include.ts @@ -1,4 +1,9 @@ -import { PropDef, TypeDef } from '../../schema/defs/index.js' +import { + PropDef, + PropTree, + TypeDef, + isPropDef, +} from '../../schema/defs/index.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { IncludeOp, @@ -8,13 +13,24 @@ import { pushIncludePartialHeader, pushIncludePartialProp, } from '../../zigTsExports.js' -import { QueryAst } from '../ast.js' +import { Include, QueryAst } from '../ast.js' +import { reference } from './single.js' type IncludeCtx = { - main: PropDef[] + tree: PropTree + main: { prop: PropDef; include: Include }[] } -const includeProp = (buf: AutoSizedUint8Array, prop: PropDef) => { +// type IncludeOpts + +const includeProp = ( + buf: AutoSizedUint8Array, + prop: PropDef, + include: Include, +) => { + // ADD TEXT + // OPTS + // META pushIncludeHeader(buf, { op: IncludeOp.default, prop: prop.id, @@ -24,27 +40,30 @@ const includeProp = (buf: AutoSizedUint8Array, prop: PropDef) => { const includeMainProps = ( buf: AutoSizedUint8Array, - props: PropDef[], + props: { prop: PropDef; include: Include }[], typeDef: TypeDef, ) => { - props.sort((a, b) => (a.start < b.start ? -1 : a.start === b.start ? 0 : 1)) + props.sort((a, b) => + a.prop.start < b.prop.start ? -1 : a.prop.start === b.prop.start ? 0 : 1, + ) if (props.length === typeDef.main.length) { pushIncludeHeader(buf, { op: IncludeOp.default, prop: 0, propType: PropType.microBuffer, }) - } else { + } else if (props.length > 0) { pushIncludePartialHeader(buf, { op: IncludeOp.partial, prop: MAIN_PROP, propType: PropType.microBuffer, amount: props.length, }) - for (const { start, size } of props) { + for (const { prop, include } of props) { + // include pushIncludePartialProp(buf, { - start, - size, + start: prop.start, + size: prop.size, }) } } @@ -54,27 +73,35 @@ export const collect = ( ast: QueryAst, buf: AutoSizedUint8Array, typeDef: TypeDef, - path: string[], - ctx: IncludeCtx = { main: [] }, + ctx: IncludeCtx, ) => { + // if ast.include.glob === '*' include all from schema + // same for ast.include.glob === '**' for (const field in ast.props) { - const propDef = typeDef.props.get(field) - if (propDef) { - // LANGUAGE - // $EDGE - if (propDef.type === PropType.reference) { - // REFERENCE - } else if (propDef.type === PropType.references) { - // REFERENCES - } else if (propDef.id === 0) { - ctx.main.push(propDef) - } else { - includeProp(buf, propDef) + const prop = ctx.tree.get(field) + const astProp = ast.props[field] + const include = astProp.include + + if (isPropDef(prop)) { + console.log(prop.path) + + if (prop.type === PropType.reference) { + reference(astProp, buf, prop) + } else if (include) { + if (prop.id === 0) { + ctx.main.push({ prop, include }) + } else { + includeProp(buf, prop, include) + } } } else { - if ('props' in ast) { - collect(ast, buf, typeDef, [...path, field], ctx) + if (prop) { + collect(astProp, buf, typeDef, { + main: ctx.main, + tree: prop, + }) } else { + // if EN, if NL throw new Error(`Prop does not exist ${field}`) } } @@ -88,8 +115,10 @@ export const include = ( typeDef: TypeDef, ): number => { const includeStart = buf.length - const ctx = collect(ast, buf, typeDef, []) + const ctx = collect(ast, buf, typeDef, { + main: [], + tree: typeDef.tree, + }) includeMainProps(buf, ctx.main, typeDef) - const includeSize = buf.length - includeStart - return includeSize + return buf.length - includeStart } diff --git a/src/db-query/toByteCode/multiple.ts b/src/db-query/toByteCode/multiple.ts index 37c0f3546b..3be483dfc0 100644 --- a/src/db-query/toByteCode/multiple.ts +++ b/src/db-query/toByteCode/multiple.ts @@ -4,7 +4,7 @@ import { pushQueryHeader, QueryType, ID_PROP, - writeQueryHeaderProps, + writeQueryHeaderProps as props, } from '../../zigTsExports.js' import { QueryAst } from '../ast.js' import { include } from './include.js' @@ -16,7 +16,7 @@ export const defaultMultiple = ( typeDef: TypeDef, ) => { const rangeStart = ast.range?.start || 0 - const queryHeaderOffset = pushQueryHeader(buf, { + const headerIndex = pushQueryHeader(buf, { op: QueryType.default, prop: ID_PROP, includeSize: 0, @@ -30,12 +30,13 @@ export const defaultMultiple = ( edgeTypeId: 0, edgeSize: 0, edgeFilterSize: 0, - size: 0, // total size + include // only used in ids now maybe remove? - // const buffer = new Uint8Array(QueryHeaderByteSize + searchSize + sortSize) + size: 0, }) - writeQueryHeaderProps.includeSize( - buf.data, - include(ast, buf, typeDef), - queryHeaderOffset, - ) + props.includeSize(buf.data, include(ast, buf, typeDef), headerIndex) } + +export const references = ( + ast: QueryAst, + buf: AutoSizedUint8Array, + prop: PropDef, +) => {} diff --git a/src/db-query/toByteCode/single.ts b/src/db-query/toByteCode/single.ts new file mode 100644 index 0000000000..4526437a0f --- /dev/null +++ b/src/db-query/toByteCode/single.ts @@ -0,0 +1,36 @@ +import { PropDef, TypeDef } from '../../schema/defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { + pushQueryHeaderSingleReference, + QueryType, + writeQueryHeaderSingleReferenceProps as props, +} from '../../zigTsExports.js' +import { QueryAst } from '../ast.js' +import { include } from './include.js' + +export const reference = ( + ast: QueryAst, + buf: AutoSizedUint8Array, + prop: PropDef, +) => { + const headerIndex = pushQueryHeaderSingleReference(buf, { + op: QueryType.reference, + typeId: prop.typeDef.id, + includeSize: 0, + edgeTypeId: 0, + edgeSize: 0, + prop: prop.id, + }) + const size = include(ast, buf, prop.typeDef) + props.includeSize(buf.data, size, headerIndex) + if (ast.edges) { + const edges = prop.edges + if (!edges) { + throw new Error('Ref does not have edges') + } + props.op(buf.data, QueryType.referenceEdge, headerIndex) + props.edgeTypeId(buf.data, edges.id, headerIndex) + const size = include(ast.edges, buf, edges) + props.edgeSize(buf.data, size, headerIndex) + } +} diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 6a81a7d191..3a455dd119 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -39,6 +39,10 @@ export type PropDef = { ): void } +export const isPropDef = (p: any): p is PropDef => { + return p && 'pushValue' in p && typeof p.pushValue === 'function' +} + export type PropDefClass = { new (prop: SchemaProp, path: string[], typeDef: TypeDef): PropDef } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index b71223c7d5..33362627ce 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -19,13 +19,34 @@ await test('include', async (t) => { x: 'boolean', flap: 'uint32', y: 'uint16', - // object + cook: { + type: 'object', + props: { + cookie: 'number', + }, + }, + mrFriend: { + ref: 'user', + prop: 'mrFriend', + $level: 'uint32', + }, }, }, }) - client.create('user', { name: 'AAAAAAAAAA', y: 67, x: true, flap: 9999 }) - client.create('user', { name: 'mr y' }) + const a = client.create('user', { + name: 'AAAAAAAAAA', + y: 67, + x: true, + flap: 9999, + }) + client.create('user', { + name: 'mr y', + cook: { + cookie: 1234, + }, + mrFriend: { id: a, $level: 67 }, + }) await db.drain() @@ -36,17 +57,31 @@ await test('include', async (t) => { const buf = new AutoSizedUint8Array(1000) let astB: Uint8Array = new Uint8Array() - for (let i = 0; i < 1e6; i++) { + for (let i = 0; i < 1; i++) { // registerQuery(db.query('user').include('name', 'x', 'y')) astB = queryAstToByteCode( client.schema!, { type: 'user', props: { - name: { include: {} }, - y: { include: {} }, - x: { include: {} }, - // flap: { include: {} }, + // name: { include: {} }, + // y: { include: {} }, + // x: { include: {} }, + // cook: { + // props: { + // cookie: { include: {} }, + // }, + // }, + mrFriend: { + // props: { + // name: { include: {} }, + // }, + edges: { + props: { + $level: { include: {} }, + }, + }, + }, }, }, buf, From a6469d24ec05afcff6efbeaadb379bf1497f75d8 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 17:07:42 +0100 Subject: [PATCH 031/449] make --- native/query/single.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index 79db4311fc..eede2ca15f 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -123,8 +123,6 @@ pub fn referenceEdge( const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); - std.debug.print("DERP ?? {any} mr node -> {any} \n", .{ e, node }); - if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); From 4dc8955fa56f0dc7d3d33515e67115b5655c5b4f Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 19:45:58 +0100 Subject: [PATCH 032/449] main and simple props in read schema --- src/db-query/ast.ts | 9 +- src/db-query/readSchema/astToReadSchema.ts | 114 ++++++++++++++++++ src/db-query/readSchema/propDef.ts | 78 ++++++++++++ .../{toByteCode.ts => astToByteCode.ts} | 0 src/db-query/toByteCode/include.ts | 18 +-- src/db-query/utils.ts | 15 +++ test/query-ast/include.ts | 98 ++++++++++----- 7 files changed, 285 insertions(+), 47 deletions(-) create mode 100644 src/db-query/readSchema/astToReadSchema.ts create mode 100644 src/db-query/readSchema/propDef.ts rename src/db-query/toByteCode/{toByteCode.ts => astToByteCode.ts} (100%) create mode 100644 src/db-query/utils.ts diff --git a/src/db-query/ast.ts b/src/db-query/ast.ts index 7d455a5f5a..8cb9c0af82 100644 --- a/src/db-query/ast.ts +++ b/src/db-query/ast.ts @@ -1,4 +1,4 @@ -import { PropDef } from '../schema/defs/index.js' +import { PropDef, PropTree } from '../schema/defs/index.js' export type FilterOp = { op: '=' | '<' | '>' | '..' | 'includes' | 'exists' | 'exist' @@ -25,6 +25,11 @@ export type Include = { raw?: boolean } +export type IncludeCtx = { + tree: PropTree + main: { start?: number; prop: PropDef; include: Include }[] +} + export type QueryAst = { locale?: string range?: { start: number; end: number } @@ -58,10 +63,12 @@ export type QueryAst = { // readBy: { // filter: { // edges: { +// props: { // $rating: { // // ops: [{ val: 4, op: '>' }], // // }, // } +// } // props: { // $rating: { // ops: [{ val: 4, op: '>' }], diff --git a/src/db-query/readSchema/astToReadSchema.ts b/src/db-query/readSchema/astToReadSchema.ts new file mode 100644 index 0000000000..fd6b3d30c0 --- /dev/null +++ b/src/db-query/readSchema/astToReadSchema.ts @@ -0,0 +1,114 @@ +import { create } from 'domain' +import { + ReaderSchemaEnum, + type ReaderLocales, + type ReaderSchema, +} from '../../protocol/index.js' +import { SchemaOut } from '../../schema.js' +import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' +import { isPropDef, PropDef, TypeDef } from '../../schema/defs/index.js' +import { LangCode, PropType } from '../../zigTsExports.js' +import { Include, IncludeCtx, QueryAst } from '../ast.js' +import { prepMain } from '../utils.js' +import { createReaderPropDef } from './propDef.js' + +export const collect = ( + ast: QueryAst, + readSchema: ReaderSchema, + typeDef: TypeDef, + locales: ReaderLocales, + ctx: IncludeCtx, +) => { + // if ast.include.glob === '*' include all from schema + // same for ast.include.glob === '**' + for (const field in ast.props) { + const prop = ctx.tree.get(field) + const astProp = ast.props[field] + const include = astProp.include + if (isPropDef(prop)) { + if (prop.type === PropType.reference) { + // reference(astProp, buf, prop) + } else if (include) { + if (prop.id === 0) { + ctx.main.push({ prop, include, start: prop.start }) + } else { + readSchema.props[prop.id] = createReaderPropDef( + prop, + locales, + include, + ) + } + } + } else { + if (prop) { + collect(astProp, readSchema, typeDef, locales, { + main: ctx.main, + tree: prop, + }) + } else { + // if EN, if NL + throw new Error(`Prop does not exist ${field}`) + } + } + } + return ctx +} + +const toReadSchema = ( + ast: QueryAst, + type: ReaderSchemaEnum, + locales: ReaderLocales, + typeDef: TypeDef, +): ReaderSchema => { + const readSchema: ReaderSchema = { + readId: 0, + props: {}, + search: false, + main: { len: 0, props: {} }, + refs: {}, + type, + } + const ctx = collect(ast, readSchema, typeDef, locales, { + tree: typeDef.tree, + main: [], + }) + prepMain(ctx.main) + for (const { prop, include, start } of ctx.main) { + readSchema.main.props[start ?? 0] = createReaderPropDef( + prop, + locales, + include, + ) + console.log('start', start, 'X', prop.start) + readSchema.main.len += prop.size + } + return readSchema +} + +export const queryAstToReadSchema = ( + schema: SchemaOut, + ast: QueryAst, +): ReaderSchema => { + const locales: ReaderLocales = {} + for (const lang in schema.locales) { + locales[LangCode[lang]] = lang + } + + if (!ast.type) { + throw new Error('Query requires type') + } + + const typeDefs = getTypeDefs(schema) + const typeDef = typeDefs.get(ast.type) + + if (!typeDef) { + throw new Error('Type does not exist') + } + + if (!ast.target) { + console.info('--------------------') + return toReadSchema(ast, ReaderSchemaEnum.default, locales, typeDef) + } + + throw new Error('not handled yet...') +} diff --git a/src/db-query/readSchema/propDef.ts b/src/db-query/readSchema/propDef.ts new file mode 100644 index 0000000000..170097b634 --- /dev/null +++ b/src/db-query/readSchema/propDef.ts @@ -0,0 +1,78 @@ +import { + ReaderMeta, + ReaderSchemaEnum, + type ReaderLocales, + type ReaderPropDef, + type ReaderSchema, +} from '../../protocol/index.js' +import { SchemaOut } from '../../schema.js' +import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' +import { + isPropDef, + PropDef, + PropTree, + TypeDef, +} from '../../schema/defs/index.js' +import { LangCode, PropType } from '../../zigTsExports.js' +import { Include, IncludeCtx, QueryAst } from '../ast.js' + +export const createReaderPropDef = ( + p: PropDef, + locales: ReaderLocales, + include?: Include, +): ReaderPropDef => { + const readerPropDef: ReaderPropDef = { + path: p.isEdge ? p.path.slice(1) : p.path, + typeIndex: include?.raw ? PropType.binary : p.type, + readBy: 0, + } + // if (opts?.meta) { + // if (opts?.codes?.size === 1 && opts.codes.has(opts.localeFromDef!)) { + // readerPropDef.meta = + // opts?.meta === 'only' + // ? ReaderMeta.onlyFallback + // : ReaderMeta.combinedFallback + // } else { + // readerPropDef.meta = + // opts?.meta === 'only' ? ReaderMeta.only : ReaderMeta.combined + // } + // } + if (p.type === PropType.enum) { + // console.log(p) + // readerPropDef.enum = p.prop.enum + } + // if (p.type === PropType.vector || p.type === PropType.colVec) { + // readerPropDef.vectorBaseType = p.vectorBaseType + // readerPropDef.len = p.len + // } + // if (p.type === PropType.cardinality) { + // readerPropDef.cardinalityMode = p.cardinalityMode + // readerPropDef.cardinalityPrecision = p.cardinalityPrecision + // } + // if (p.type === PropType.text && opts?.codes) { + // if (opts.codes.has(0)) { + // readerPropDef.locales = locales + // } else { + // if (opts.codes.size === 1 && opts.codes.has(opts.localeFromDef!)) { + // if (readerPropDef.meta) { + // readerPropDef.locales = {} + // for (const code of opts.codes) { + // readerPropDef.locales[code] = LangCodeInverse[code] + // } + // if (opts.fallBacks) { + // for (const code of opts.fallBacks) { + // readerPropDef.locales[code] = LangCodeInverse[code] + // } + // } + // } + // // dont add locales - interpets it as a normal prop + // } else { + // readerPropDef.locales = {} + // for (const code of opts.codes) { + // readerPropDef.locales[code] = LangCodeInverse[code] + // } + // } + // } + // } + return readerPropDef +} diff --git a/src/db-query/toByteCode/toByteCode.ts b/src/db-query/toByteCode/astToByteCode.ts similarity index 100% rename from src/db-query/toByteCode/toByteCode.ts rename to src/db-query/toByteCode/astToByteCode.ts diff --git a/src/db-query/toByteCode/include.ts b/src/db-query/toByteCode/include.ts index b757848c87..728e7ac795 100644 --- a/src/db-query/toByteCode/include.ts +++ b/src/db-query/toByteCode/include.ts @@ -13,14 +13,10 @@ import { pushIncludePartialHeader, pushIncludePartialProp, } from '../../zigTsExports.js' -import { Include, QueryAst } from '../ast.js' +import { Include, IncludeCtx, QueryAst } from '../ast.js' +import { prepMain } from '../utils.js' import { reference } from './single.js' -type IncludeCtx = { - tree: PropTree - main: { prop: PropDef; include: Include }[] -} - // type IncludeOpts const includeProp = ( @@ -43,9 +39,7 @@ const includeMainProps = ( props: { prop: PropDef; include: Include }[], typeDef: TypeDef, ) => { - props.sort((a, b) => - a.prop.start < b.prop.start ? -1 : a.prop.start === b.prop.start ? 0 : 1, - ) + prepMain(props) if (props.length === typeDef.main.length) { pushIncludeHeader(buf, { op: IncludeOp.default, @@ -60,7 +54,6 @@ const includeMainProps = ( amount: props.length, }) for (const { prop, include } of props) { - // include pushIncludePartialProp(buf, { start: prop.start, size: prop.size, @@ -81,15 +74,12 @@ export const collect = ( const prop = ctx.tree.get(field) const astProp = ast.props[field] const include = astProp.include - if (isPropDef(prop)) { - console.log(prop.path) - if (prop.type === PropType.reference) { reference(astProp, buf, prop) } else if (include) { if (prop.id === 0) { - ctx.main.push({ prop, include }) + ctx.main.push({ prop, include, start: prop.start }) } else { includeProp(buf, prop, include) } diff --git a/src/db-query/utils.ts b/src/db-query/utils.ts new file mode 100644 index 0000000000..68e75a9de0 --- /dev/null +++ b/src/db-query/utils.ts @@ -0,0 +1,15 @@ +import { PropDef } from '../schema/defs/index.js' +import { Include } from './ast.js' + +export const prepMain = ( + props: { prop: PropDef; include: Include; start?: number }[], +) => { + props.sort((a, b) => + a.prop.start < b.prop.start ? -1 : a.prop.start === b.prop.start ? 0 : 1, + ) + let i = 0 + for (const prop of props) { + prop.start = i + i += prop.prop.size + } +} diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 33362627ce..446c3a13b2 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,9 +1,17 @@ +import { deSerializeSchema } from '../../dist/protocol/db-read/schema/deserialize.js' +import { convertToReaderSchema } from '../../src/db-client/query/queryDefToReadSchema.js' import { registerQuery } from '../../src/db-client/query/registerQuery.js' import { QueryAst } from '../../src/db-query/ast.js' -import { queryAstToByteCode } from '../../src/db-query/toByteCode/toByteCode.js' +import { queryAstToReadSchema } from '../../src/db-query/readSchema/astToReadSchema.js' +import { queryAstToByteCode } from '../../src/db-query/toByteCode/astToByteCode.js' +import { + resultToObject, + serializeReaderSchema, +} from '../../src/protocol/index.js' import { parseSchema, Schema } from '../../src/schema.js' import { BasedDb, debugBuffer } from '../../src/sdk.js' import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' +import deepEqual from '../../src/utils/deepEqual.js' import { testDb } from '../shared/index.js' import test from '../shared/test.js' @@ -39,14 +47,17 @@ await test('include', async (t) => { y: 67, x: true, flap: 9999, - }) - client.create('user', { - name: 'mr y', cook: { cookie: 1234, }, - mrFriend: { id: a, $level: 67 }, }) + // client.create('user', { + // name: 'mr y', + // cook: { + // cookie: 1234, + // }, + // mrFriend: { id: a, $level: 67 }, + // }) await db.drain() @@ -57,40 +68,63 @@ await test('include', async (t) => { const buf = new AutoSizedUint8Array(1000) let astB: Uint8Array = new Uint8Array() - for (let i = 0; i < 1; i++) { - // registerQuery(db.query('user').include('name', 'x', 'y')) - astB = queryAstToByteCode( - client.schema!, - { - type: 'user', + + const ast = { + type: 'user', + props: { + name: { include: {} }, + y: { include: {} }, + x: { include: {} }, + cook: { props: { - // name: { include: {} }, - // y: { include: {} }, - // x: { include: {} }, - // cook: { - // props: { - // cookie: { include: {} }, - // }, - // }, - mrFriend: { - // props: { - // name: { include: {} }, - // }, - edges: { - props: { - $level: { include: {} }, - }, - }, - }, + cookie: { include: {} }, }, }, - buf, - ) + // mrFriend: { + // // props: { + // // name: { include: {} }, + // // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, + // }, + }, + } + + const readSchema = queryAstToReadSchema(client.schema!, ast) + + const bufS = serializeReaderSchema(readSchema) + + console.log(readSchema, bufS) + + console.log('derp') + + for (let i = 0; i < 1; i++) { + // registerQuery(db.query('user').include('name', 'x', 'y')) + astB = queryAstToByteCode(client.schema!, ast, buf) } console.log(Date.now() - d, 'ms') debugBuffer(astB) // console.log('--------------') - debugBuffer(await db.server.getQueryBuf(astB)) + const result = await db.server.getQueryBuf(astB) + debugBuffer(result) + + const x = deSerializeSchema(bufS, 0) + + const yyy = db.query('user').include('name') + const xxxx = registerQuery(yyy) + + const x2 = convertToReaderSchema(yyy.def!) + + console.dir({ x }, { depth: 10 }) + + console.dir(x2, { depth: 10 }) + + // console.log('====', deepEqual(x, x2)) + + console.log(resultToObject(x, result, result.byteLength - 4)) }) From a730996bc7e1dc0f2d5048103f84360734d20c42 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 19:49:10 +0100 Subject: [PATCH 033/449] read schema --- src/db-query/readSchema/astToReadSchema.ts | 31 ++++++---------------- src/db-query/readSchema/propDef.ts | 2 +- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/db-query/readSchema/astToReadSchema.ts b/src/db-query/readSchema/astToReadSchema.ts index fd6b3d30c0..1361750104 100644 --- a/src/db-query/readSchema/astToReadSchema.ts +++ b/src/db-query/readSchema/astToReadSchema.ts @@ -1,4 +1,3 @@ -import { create } from 'domain' import { ReaderSchemaEnum, type ReaderLocales, @@ -6,11 +5,14 @@ import { } from '../../protocol/index.js' import { SchemaOut } from '../../schema.js' import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' -import { isPropDef, PropDef, TypeDef } from '../../schema/defs/index.js' +import { isPropDef, TypeDef } from '../../schema/defs/index.js' import { LangCode, PropType } from '../../zigTsExports.js' -import { Include, IncludeCtx, QueryAst } from '../ast.js' +import { IncludeCtx, QueryAst } from '../ast.js' import { prepMain } from '../utils.js' -import { createReaderPropDef } from './propDef.js' +import { readPropDef } from './propDef.js' + +// put all files in here +// rename all this stuff DbQueryReadSchema export const collect = ( ast: QueryAst, @@ -32,11 +34,7 @@ export const collect = ( if (prop.id === 0) { ctx.main.push({ prop, include, start: prop.start }) } else { - readSchema.props[prop.id] = createReaderPropDef( - prop, - locales, - include, - ) + readSchema.props[prop.id] = readPropDef(prop, locales, include) } } } else { @@ -45,9 +43,6 @@ export const collect = ( main: ctx.main, tree: prop, }) - } else { - // if EN, if NL - throw new Error(`Prop does not exist ${field}`) } } } @@ -74,12 +69,7 @@ const toReadSchema = ( }) prepMain(ctx.main) for (const { prop, include, start } of ctx.main) { - readSchema.main.props[start ?? 0] = createReaderPropDef( - prop, - locales, - include, - ) - console.log('start', start, 'X', prop.start) + readSchema.main.props[start ?? 0] = readPropDef(prop, locales, include) readSchema.main.len += prop.size } return readSchema @@ -93,22 +83,17 @@ export const queryAstToReadSchema = ( for (const lang in schema.locales) { locales[LangCode[lang]] = lang } - if (!ast.type) { throw new Error('Query requires type') } - const typeDefs = getTypeDefs(schema) const typeDef = typeDefs.get(ast.type) - if (!typeDef) { throw new Error('Type does not exist') } - if (!ast.target) { console.info('--------------------') return toReadSchema(ast, ReaderSchemaEnum.default, locales, typeDef) } - throw new Error('not handled yet...') } diff --git a/src/db-query/readSchema/propDef.ts b/src/db-query/readSchema/propDef.ts index 170097b634..80e942d1bb 100644 --- a/src/db-query/readSchema/propDef.ts +++ b/src/db-query/readSchema/propDef.ts @@ -16,7 +16,7 @@ import { import { LangCode, PropType } from '../../zigTsExports.js' import { Include, IncludeCtx, QueryAst } from '../ast.js' -export const createReaderPropDef = ( +export const readPropDef = ( p: PropDef, locales: ReaderLocales, include?: Include, From e4db6e07386350f13381fb0e9873e6858133c7b2 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 21:03:22 +0100 Subject: [PATCH 034/449] combine read schema + ast to query --- src/db-query/{ => ast}/ast.ts | 12 ++- src/db-query/{toByteCode => ast}/include.ts | 71 ++++++------- .../{toByteCode => ast}/iteratorType.ts | 0 src/db-query/{toByteCode => ast}/multiple.ts | 19 +--- .../propDef.ts => ast/readSchema.ts} | 38 ++++--- src/db-query/{toByteCode => ast}/single.ts | 22 ++--- src/db-query/ast/toCtx.ts | 57 +++++++++++ src/db-query/readSchema/astToReadSchema.ts | 99 ------------------- src/db-query/toByteCode/astToByteCode.ts | 38 ------- src/db-query/utils.ts | 15 --- test/query-ast/include.ts | 48 +++------ 11 files changed, 154 insertions(+), 265 deletions(-) rename src/db-query/{ => ast}/ast.ts (85%) rename src/db-query/{toByteCode => ast}/include.ts (55%) rename src/db-query/{toByteCode => ast}/iteratorType.ts (100%) rename src/db-query/{toByteCode => ast}/multiple.ts (60%) rename src/db-query/{readSchema/propDef.ts => ast/readSchema.ts} (77%) rename src/db-query/{toByteCode => ast}/single.ts (53%) create mode 100644 src/db-query/ast/toCtx.ts delete mode 100644 src/db-query/readSchema/astToReadSchema.ts delete mode 100644 src/db-query/toByteCode/astToByteCode.ts delete mode 100644 src/db-query/utils.ts diff --git a/src/db-query/ast.ts b/src/db-query/ast/ast.ts similarity index 85% rename from src/db-query/ast.ts rename to src/db-query/ast/ast.ts index 8cb9c0af82..943fb454ce 100644 --- a/src/db-query/ast.ts +++ b/src/db-query/ast/ast.ts @@ -1,4 +1,6 @@ -import { PropDef, PropTree } from '../schema/defs/index.js' +import { ReaderLocales, ReaderSchema } from '../../protocol/index.js' +import { PropDef, PropTree } from '../../schema/defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' export type FilterOp = { op: '=' | '<' | '>' | '..' | 'includes' | 'exists' | 'exist' @@ -27,7 +29,7 @@ export type Include = { export type IncludeCtx = { tree: PropTree - main: { start?: number; prop: PropDef; include: Include }[] + main: { prop: PropDef; include: Include }[] } export type QueryAst = { @@ -46,6 +48,12 @@ export type QueryAst = { > edges?: QueryAst } +export type Ctx = { + query: AutoSizedUint8Array + readSchema: ReaderSchema + sub: AutoSizedUint8Array + locales: ReaderLocales +} // const x: QueryAst = { // type: 'user', diff --git a/src/db-query/toByteCode/include.ts b/src/db-query/ast/include.ts similarity index 55% rename from src/db-query/toByteCode/include.ts rename to src/db-query/ast/include.ts index 728e7ac795..e55e6e8a01 100644 --- a/src/db-query/toByteCode/include.ts +++ b/src/db-query/ast/include.ts @@ -13,48 +13,54 @@ import { pushIncludePartialHeader, pushIncludePartialProp, } from '../../zigTsExports.js' -import { Include, IncludeCtx, QueryAst } from '../ast.js' -import { prepMain } from '../utils.js' +import { Ctx, Include, IncludeCtx, QueryAst } from './ast.js' +import { readPropDef } from './readSchema.js' import { reference } from './single.js' // type IncludeOpts -const includeProp = ( - buf: AutoSizedUint8Array, - prop: PropDef, - include: Include, -) => { - // ADD TEXT - // OPTS - // META - pushIncludeHeader(buf, { +const includeProp = (ctx: Ctx, prop: PropDef, include: Include) => { + pushIncludeHeader(ctx.query, { op: IncludeOp.default, prop: prop.id, propType: prop.type, }) + ctx.readSchema.props[prop.id] = readPropDef(prop, ctx.locales, include) } const includeMainProps = ( - buf: AutoSizedUint8Array, + ctx: Ctx, props: { prop: PropDef; include: Include }[], typeDef: TypeDef, ) => { - prepMain(props) + props.sort((a, b) => + a.prop.start < b.prop.start ? -1 : a.prop.start === b.prop.start ? 0 : 1, + ) + let i = 0 + for (const { include, prop } of props) { + i += prop.size + ctx.readSchema.main.props[prop.start ?? 0] = readPropDef( + prop, + ctx.locales, + include, + ) + ctx.readSchema.main.len += prop.size + } if (props.length === typeDef.main.length) { - pushIncludeHeader(buf, { + pushIncludeHeader(ctx.query, { op: IncludeOp.default, prop: 0, propType: PropType.microBuffer, }) } else if (props.length > 0) { - pushIncludePartialHeader(buf, { + pushIncludePartialHeader(ctx.query, { op: IncludeOp.partial, prop: MAIN_PROP, propType: PropType.microBuffer, amount: props.length, }) for (const { prop, include } of props) { - pushIncludePartialProp(buf, { + pushIncludePartialProp(ctx.query, { start: prop.start, size: prop.size, }) @@ -64,30 +70,31 @@ const includeMainProps = ( export const collect = ( ast: QueryAst, - buf: AutoSizedUint8Array, + ctx: Ctx, typeDef: TypeDef, - ctx: IncludeCtx, + includeCtx: IncludeCtx, ) => { + const { main, tree } = includeCtx // if ast.include.glob === '*' include all from schema // same for ast.include.glob === '**' for (const field in ast.props) { - const prop = ctx.tree.get(field) + const prop = tree.get(field) const astProp = ast.props[field] const include = astProp.include if (isPropDef(prop)) { if (prop.type === PropType.reference) { - reference(astProp, buf, prop) + reference(astProp, ctx, prop) } else if (include) { if (prop.id === 0) { - ctx.main.push({ prop, include, start: prop.start }) + main.push({ prop, include }) } else { - includeProp(buf, prop, include) + includeProp(ctx, prop, include) } } } else { if (prop) { - collect(astProp, buf, typeDef, { - main: ctx.main, + collect(astProp, ctx, typeDef, { + main, tree: prop, }) } else { @@ -96,19 +103,15 @@ export const collect = ( } } } - return ctx + return includeCtx } -export const include = ( - ast: QueryAst, - buf: AutoSizedUint8Array, - typeDef: TypeDef, -): number => { - const includeStart = buf.length - const ctx = collect(ast, buf, typeDef, { +export const include = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef): number => { + const includeStart = ctx.query.length + const { main } = collect(ast, ctx, typeDef, { main: [], tree: typeDef.tree, }) - includeMainProps(buf, ctx.main, typeDef) - return buf.length - includeStart + includeMainProps(ctx, main, typeDef) + return ctx.query.length - includeStart } diff --git a/src/db-query/toByteCode/iteratorType.ts b/src/db-query/ast/iteratorType.ts similarity index 100% rename from src/db-query/toByteCode/iteratorType.ts rename to src/db-query/ast/iteratorType.ts diff --git a/src/db-query/toByteCode/multiple.ts b/src/db-query/ast/multiple.ts similarity index 60% rename from src/db-query/toByteCode/multiple.ts rename to src/db-query/ast/multiple.ts index 3be483dfc0..8147d492c0 100644 --- a/src/db-query/toByteCode/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -1,22 +1,17 @@ import { PropDef, TypeDef } from '../../schema/defs/index.js' -import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { pushQueryHeader, QueryType, ID_PROP, writeQueryHeaderProps as props, } from '../../zigTsExports.js' -import { QueryAst } from '../ast.js' +import { Ctx, QueryAst } from './ast.js' import { include } from './include.js' import { getIteratorType } from './iteratorType.js' -export const defaultMultiple = ( - ast: QueryAst, - buf: AutoSizedUint8Array, - typeDef: TypeDef, -) => { +export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { const rangeStart = ast.range?.start || 0 - const headerIndex = pushQueryHeader(buf, { + const headerIndex = pushQueryHeader(ctx.query, { op: QueryType.default, prop: ID_PROP, includeSize: 0, @@ -32,11 +27,7 @@ export const defaultMultiple = ( edgeFilterSize: 0, size: 0, }) - props.includeSize(buf.data, include(ast, buf, typeDef), headerIndex) + props.includeSize(ctx.query.data, include(ast, ctx, typeDef), headerIndex) } -export const references = ( - ast: QueryAst, - buf: AutoSizedUint8Array, - prop: PropDef, -) => {} +export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => {} diff --git a/src/db-query/readSchema/propDef.ts b/src/db-query/ast/readSchema.ts similarity index 77% rename from src/db-query/readSchema/propDef.ts rename to src/db-query/ast/readSchema.ts index 80e942d1bb..f3d3f39118 100644 --- a/src/db-query/readSchema/propDef.ts +++ b/src/db-query/ast/readSchema.ts @@ -1,24 +1,36 @@ import { - ReaderMeta, + ReaderLocales, + ReaderPropDef, + ReaderSchema, ReaderSchemaEnum, - type ReaderLocales, - type ReaderPropDef, - type ReaderSchema, } from '../../protocol/index.js' import { SchemaOut } from '../../schema.js' -import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' -import { - isPropDef, - PropDef, - PropTree, - TypeDef, -} from '../../schema/defs/index.js' +import { PropDef } from '../../schema/defs/index.js' import { LangCode, PropType } from '../../zigTsExports.js' -import { Include, IncludeCtx, QueryAst } from '../ast.js' +import { Include } from './ast.js' + +export const readSchema = (type?: ReaderSchemaEnum): ReaderSchema => { + return { + readId: 0, + props: {}, + search: false, + main: { len: 0, props: {} }, + refs: {}, + type: type ?? ReaderSchemaEnum.default, + } +} + +export const getReaderLocales = (schema: SchemaOut): ReaderLocales => { + const locales: ReaderLocales = {} + for (const lang in schema.locales) { + locales[LangCode[lang]] = lang + } + return locales +} export const readPropDef = ( p: PropDef, - locales: ReaderLocales, + locales: ReaderLocales, //add in ctx include?: Include, ): ReaderPropDef => { const readerPropDef: ReaderPropDef = { diff --git a/src/db-query/toByteCode/single.ts b/src/db-query/ast/single.ts similarity index 53% rename from src/db-query/toByteCode/single.ts rename to src/db-query/ast/single.ts index 4526437a0f..5e337a8b1d 100644 --- a/src/db-query/toByteCode/single.ts +++ b/src/db-query/ast/single.ts @@ -5,15 +5,11 @@ import { QueryType, writeQueryHeaderSingleReferenceProps as props, } from '../../zigTsExports.js' -import { QueryAst } from '../ast.js' +import { Ctx, QueryAst } from './ast.js' import { include } from './include.js' -export const reference = ( - ast: QueryAst, - buf: AutoSizedUint8Array, - prop: PropDef, -) => { - const headerIndex = pushQueryHeaderSingleReference(buf, { +export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { + const headerIndex = pushQueryHeaderSingleReference(ctx.query, { op: QueryType.reference, typeId: prop.typeDef.id, includeSize: 0, @@ -21,16 +17,16 @@ export const reference = ( edgeSize: 0, prop: prop.id, }) - const size = include(ast, buf, prop.typeDef) - props.includeSize(buf.data, size, headerIndex) + const size = include(ast, ctx, prop.typeDef) + props.includeSize(ctx.query.data, size, headerIndex) if (ast.edges) { const edges = prop.edges if (!edges) { throw new Error('Ref does not have edges') } - props.op(buf.data, QueryType.referenceEdge, headerIndex) - props.edgeTypeId(buf.data, edges.id, headerIndex) - const size = include(ast.edges, buf, edges) - props.edgeSize(buf.data, size, headerIndex) + props.op(ctx.query.data, QueryType.referenceEdge, headerIndex) + props.edgeTypeId(ctx.query.data, edges.id, headerIndex) + const size = include(ast.edges, ctx, edges) + props.edgeSize(ctx.query.data, size, headerIndex) } } diff --git a/src/db-query/ast/toCtx.ts b/src/db-query/ast/toCtx.ts new file mode 100644 index 0000000000..5b1661e474 --- /dev/null +++ b/src/db-query/ast/toCtx.ts @@ -0,0 +1,57 @@ +import { crc32 } from '../../db-client/crc32.js' +import { registerQuery } from '../../db-client/query/registerQuery.js' +import { ReaderSchema, ReaderSchemaEnum } from '../../protocol/index.js' +import { PropDef, SchemaOut } from '../../schema.js' +import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' +import { TypeDef } from '../../schema/defs/index.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { Ctx, QueryAst } from './ast.js' +import { defaultMultiple } from './multiple.js' +import { getReaderLocales, readSchema } from './readSchema.js' + +export const astToQueryCtx = ( + schema: SchemaOut, + ast: QueryAst, + query: AutoSizedUint8Array, + sub: AutoSizedUint8Array, +): { + query: Uint8Array + readSchema: ReaderSchema + subscription: Uint8Array // make this optional ? +} => { + query.length = 0 + + if (!ast.type) { + throw new Error('Query requires type') + } + + const typeDefs = getTypeDefs(schema) + const typeDef = typeDefs.get(ast.type) + + if (!typeDef) { + throw new Error('Type does not exist') + } + + const queryIdPos = query.reserveUint32() + + const ctx: Ctx = { + query, + sub, + readSchema: readSchema(), + locales: getReaderLocales(schema), + } + + if (!ast.target) { + defaultMultiple(ast, ctx, typeDef) + } + + query.pushUint64(schema.hash) + query.writeUint32(crc32(query.view), queryIdPos) + + // can use same buf for sub + return { + query: query.view.slice(), + readSchema: ctx.readSchema, + subscription: new Uint8Array(0), + } +} diff --git a/src/db-query/readSchema/astToReadSchema.ts b/src/db-query/readSchema/astToReadSchema.ts deleted file mode 100644 index 1361750104..0000000000 --- a/src/db-query/readSchema/astToReadSchema.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - ReaderSchemaEnum, - type ReaderLocales, - type ReaderSchema, -} from '../../protocol/index.js' -import { SchemaOut } from '../../schema.js' -import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' -import { isPropDef, TypeDef } from '../../schema/defs/index.js' -import { LangCode, PropType } from '../../zigTsExports.js' -import { IncludeCtx, QueryAst } from '../ast.js' -import { prepMain } from '../utils.js' -import { readPropDef } from './propDef.js' - -// put all files in here -// rename all this stuff DbQueryReadSchema - -export const collect = ( - ast: QueryAst, - readSchema: ReaderSchema, - typeDef: TypeDef, - locales: ReaderLocales, - ctx: IncludeCtx, -) => { - // if ast.include.glob === '*' include all from schema - // same for ast.include.glob === '**' - for (const field in ast.props) { - const prop = ctx.tree.get(field) - const astProp = ast.props[field] - const include = astProp.include - if (isPropDef(prop)) { - if (prop.type === PropType.reference) { - // reference(astProp, buf, prop) - } else if (include) { - if (prop.id === 0) { - ctx.main.push({ prop, include, start: prop.start }) - } else { - readSchema.props[prop.id] = readPropDef(prop, locales, include) - } - } - } else { - if (prop) { - collect(astProp, readSchema, typeDef, locales, { - main: ctx.main, - tree: prop, - }) - } - } - } - return ctx -} - -const toReadSchema = ( - ast: QueryAst, - type: ReaderSchemaEnum, - locales: ReaderLocales, - typeDef: TypeDef, -): ReaderSchema => { - const readSchema: ReaderSchema = { - readId: 0, - props: {}, - search: false, - main: { len: 0, props: {} }, - refs: {}, - type, - } - const ctx = collect(ast, readSchema, typeDef, locales, { - tree: typeDef.tree, - main: [], - }) - prepMain(ctx.main) - for (const { prop, include, start } of ctx.main) { - readSchema.main.props[start ?? 0] = readPropDef(prop, locales, include) - readSchema.main.len += prop.size - } - return readSchema -} - -export const queryAstToReadSchema = ( - schema: SchemaOut, - ast: QueryAst, -): ReaderSchema => { - const locales: ReaderLocales = {} - for (const lang in schema.locales) { - locales[LangCode[lang]] = lang - } - if (!ast.type) { - throw new Error('Query requires type') - } - const typeDefs = getTypeDefs(schema) - const typeDef = typeDefs.get(ast.type) - if (!typeDef) { - throw new Error('Type does not exist') - } - if (!ast.target) { - console.info('--------------------') - return toReadSchema(ast, ReaderSchemaEnum.default, locales, typeDef) - } - throw new Error('not handled yet...') -} diff --git a/src/db-query/toByteCode/astToByteCode.ts b/src/db-query/toByteCode/astToByteCode.ts deleted file mode 100644 index 9b16f7c014..0000000000 --- a/src/db-query/toByteCode/astToByteCode.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { crc32 } from '../../db-client/crc32.js' -import { registerQuery } from '../../db-client/query/registerQuery.js' -import { PropDef, SchemaOut } from '../../schema.js' -import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' -import { TypeDef } from '../../schema/defs/index.js' -import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import { QueryAst } from '../ast.js' -import { defaultMultiple } from './multiple.js' - -export const queryAstToByteCode = ( - schema: SchemaOut, - ast: QueryAst, - buf: AutoSizedUint8Array, -): Uint8Array => { - buf.length = 0 - - if (!ast.type) { - throw new Error('Query requires type') - } - - const typeDefs = getTypeDefs(schema) - const typeDef = typeDefs.get(ast.type) - - if (!typeDef) { - throw new Error('Type does not exist') - } - - const queryIdPos = buf.reserveUint32() - - if (!ast.target) { - defaultMultiple(ast, buf, typeDef) - } - - buf.pushUint64(schema.hash) - buf.writeUint32(crc32(buf.view), queryIdPos) - - return buf.view.slice() -} diff --git a/src/db-query/utils.ts b/src/db-query/utils.ts deleted file mode 100644 index 68e75a9de0..0000000000 --- a/src/db-query/utils.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { PropDef } from '../schema/defs/index.js' -import { Include } from './ast.js' - -export const prepMain = ( - props: { prop: PropDef; include: Include; start?: number }[], -) => { - props.sort((a, b) => - a.prop.start < b.prop.start ? -1 : a.prop.start === b.prop.start ? 0 : 1, - ) - let i = 0 - for (const prop of props) { - prop.start = i - i += prop.prop.size - } -} diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 446c3a13b2..4e94ae74e2 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,18 +1,13 @@ import { deSerializeSchema } from '../../dist/protocol/db-read/schema/deserialize.js' import { convertToReaderSchema } from '../../src/db-client/query/queryDefToReadSchema.js' import { registerQuery } from '../../src/db-client/query/registerQuery.js' -import { QueryAst } from '../../src/db-query/ast.js' -import { queryAstToReadSchema } from '../../src/db-query/readSchema/astToReadSchema.js' -import { queryAstToByteCode } from '../../src/db-query/toByteCode/astToByteCode.js' +import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { resultToObject, serializeReaderSchema, } from '../../src/protocol/index.js' -import { parseSchema, Schema } from '../../src/schema.js' import { BasedDb, debugBuffer } from '../../src/sdk.js' import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' -import deepEqual from '../../src/utils/deepEqual.js' -import { testDb } from '../shared/index.js' import test from '../shared/test.js' @@ -65,10 +60,6 @@ await test('include', async (t) => { let d = Date.now() - const buf = new AutoSizedUint8Array(1000) - - let astB: Uint8Array = new Uint8Array() - const ast = { type: 'user', props: { @@ -93,38 +84,21 @@ await test('include', async (t) => { }, } - const readSchema = queryAstToReadSchema(client.schema!, ast) - - const bufS = serializeReaderSchema(readSchema) + const ctx = astToQueryCtx( + client.schema!, + ast, + new AutoSizedUint8Array(1000), + new AutoSizedUint8Array(1000), + ) - console.log(readSchema, bufS) - - console.log('derp') - - for (let i = 0; i < 1; i++) { - // registerQuery(db.query('user').include('name', 'x', 'y')) - astB = queryAstToByteCode(client.schema!, ast, buf) - } - console.log(Date.now() - d, 'ms') + console.dir(ctx, { depth: 10 }) - debugBuffer(astB) + // debugBuffer(ctx.query) - // console.log('--------------') - const result = await db.server.getQueryBuf(astB) + const result = await db.server.getQueryBuf(ctx.query) debugBuffer(result) - const x = deSerializeSchema(bufS, 0) - - const yyy = db.query('user').include('name') - const xxxx = registerQuery(yyy) - - const x2 = convertToReaderSchema(yyy.def!) - - console.dir({ x }, { depth: 10 }) - - console.dir(x2, { depth: 10 }) - // console.log('====', deepEqual(x, x2)) - console.log(resultToObject(x, result, result.byteLength - 4)) + console.log(resultToObject(ctx.readSchema, result, result.byteLength - 4)) }) From 9d77717e56a9894f210ce332e5f68143f630f07b Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 21:05:10 +0100 Subject: [PATCH 035/449] fix --- test/query-ast/include.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 4e94ae74e2..a6400296bd 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -87,18 +87,14 @@ await test('include', async (t) => { const ctx = astToQueryCtx( client.schema!, ast, - new AutoSizedUint8Array(1000), - new AutoSizedUint8Array(1000), + new AutoSizedUint8Array(1000), // this will be shared + new AutoSizedUint8Array(1000), // this will be shared ) console.dir(ctx, { depth: 10 }) - // debugBuffer(ctx.query) - const result = await db.server.getQueryBuf(ctx.query) debugBuffer(result) - // console.log('====', deepEqual(x, x2)) - console.log(resultToObject(ctx.readSchema, result, result.byteLength - 4)) }) From 5fbfd99dc756a19ca83aa271f1bc8a80ab429de7 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 21:06:28 +0100 Subject: [PATCH 036/449] fix --- test/query-ast/include.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index a6400296bd..b6e20c8b57 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -71,6 +71,9 @@ await test('include', async (t) => { cookie: { include: {} }, }, }, + + // NOW ADD MR FRIEND! + // mrFriend: { // // props: { // // name: { include: {} }, From 9d48122bb9b47d558d9a6c5882aea3a46258817f Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 29 Jan 2026 21:13:54 +0100 Subject: [PATCH 037/449] ast --- src/db-query/ast/ast.ts | 79 +---------------------------------- src/db-query/ast/include.ts | 10 +---- src/db-query/ast/single.ts | 3 +- src/protocol/db-read/types.ts | 4 +- test/query-ast/include.ts | 13 +++++- 5 files changed, 15 insertions(+), 94 deletions(-) diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 943fb454ce..8d7a5e832e 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -36,7 +36,7 @@ export type QueryAst = { locale?: string range?: { start: number; end: number } type?: string - target?: string | number | (number | string)[] // '[id]' + target?: string | number | (number | string)[] filter?: FilterAst sort?: { prop: string; order: 'asc' | 'desc' } props?: Record< @@ -54,80 +54,3 @@ export type Ctx = { sub: AutoSizedUint8Array locales: ReaderLocales } - -// const x: QueryAst = { -// type: 'user', -// locale: 'nl', -// filter: { -// props: { -// articlesWritten: { -// ops: [{ op: '>', val: 5 }], -// }, -// }, -// }, -// props: { -// articles: { -// props: { -// readBy: { -// filter: { -// edges: { -// props: { -// $rating: { -// // ops: [{ val: 4, op: '>' }], -// // }, -// } -// } -// props: { -// $rating: { -// ops: [{ val: 4, op: '>' }], -// }, -// }, -// }, -// props: { -// name: { include: { meta: 'only' } }, -// email: { include: {} }, -// }, -// }, -// }, -// }, -// }, -// } - -/* -db.query('user') - .locale('nl') - .filter('articlesWritten', '>', 5) - .include((select) => - select('articles.readBy') - .filter('$rating', '>', 4) - .include('name', 'email', { meta: 'only' }), - ) - */ - -// query('user').include('address', 'address.city.**') - -// { -// props: { -// address: { -// opts: { include: '*' }, -// props: { -// city: { -// include: '**' -// } -// } -// } -// } -// } - -// { -// props: { -// address: { -// include: '*', -// props: { -// city: { -// include: '*' -// } -// } -// } -// } -// } diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index e55e6e8a01..415f9c47fa 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -1,10 +1,4 @@ -import { - PropDef, - PropTree, - TypeDef, - isPropDef, -} from '../../schema/defs/index.js' -import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { PropDef, TypeDef, isPropDef } from '../../schema/defs/index.js' import { IncludeOp, MAIN_PROP, @@ -17,8 +11,6 @@ import { Ctx, Include, IncludeCtx, QueryAst } from './ast.js' import { readPropDef } from './readSchema.js' import { reference } from './single.js' -// type IncludeOpts - const includeProp = (ctx: Ctx, prop: PropDef, include: Include) => { pushIncludeHeader(ctx.query, { op: IncludeOp.default, diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index 5e337a8b1d..e03f2372a4 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -1,5 +1,4 @@ -import { PropDef, TypeDef } from '../../schema/defs/index.js' -import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { PropDef } from '../../schema/defs/index.js' import { pushQueryHeaderSingleReference, QueryType, diff --git a/src/protocol/db-read/types.ts b/src/protocol/db-read/types.ts index ced25e63ee..ca5a21ddc4 100644 --- a/src/protocol/db-read/types.ts +++ b/src/protocol/db-read/types.ts @@ -77,14 +77,12 @@ export type ReaderGroupBy = { typeIndex: PropTypeEnum stepRange?: number stepType?: boolean - display?: Intl.DateTimeFormat // find a way for this -- shitty + display?: Intl.DateTimeFormat enum?: any[] } -// Move these types to seperate pkg including query def agg export type ReaderSchema = { readId: number - // maybe current read id that you add props: { [prop: string]: ReaderPropDef } main: { props: { [start: string]: ReaderPropDef }; len: number } type: ReaderSchemaEnum diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index b6e20c8b57..6e3b8f4f16 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -58,7 +58,7 @@ await test('include', async (t) => { console.log('-------') - let d = Date.now() + // let d = Date.now() const ast = { type: 'user', @@ -99,5 +99,14 @@ await test('include', async (t) => { const result = await db.server.getQueryBuf(ctx.query) debugBuffer(result) - console.log(resultToObject(ctx.readSchema, result, result.byteLength - 4)) + const readSchemaBuf = serializeReaderSchema(ctx.readSchema) + + const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + console.log(obj) + + console.log( + JSON.stringify(obj).length, + readSchemaBuf.byteLength + result.byteLength, + ) }) From d657ea11cdd66382ce7de2a542507ea5f419e485 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Fri, 30 Jan 2026 09:31:51 +0100 Subject: [PATCH 038/449] single ref edge --- native/query/filter/compare.zig | 106 +++++++++++---------------- native/query/filter/filter.zig | 12 ++- native/query/filter/instruction.zig | 34 ++++----- native/types.zig | 16 ++-- src/db-client/query/include/props.ts | 7 +- src/db-query/ast/ast.ts | 10 +-- src/db-query/ast/single.ts | 28 ++++++- test/query-ast/include.ts | 34 ++++----- 8 files changed, 119 insertions(+), 128 deletions(-) diff --git a/native/query/filter/compare.zig b/native/query/filter/compare.zig index 2f6c9be381..ea481735f5 100644 --- a/native/query/filter/compare.zig +++ b/native/query/filter/compare.zig @@ -6,92 +6,70 @@ const Schema = @import("../../selva/schema.zig"); const Fields = @import("../../selva/fields.zig"); const t = @import("../../types.zig"); -// Fns here are non-inline to avoid a too long switch statement in filter - -pub const Op = enum(u8) { - eq = 1, - lt = 2, - gt = 3, - le = 4, - ge = 5, +pub const Function = enum(u8) { + eq, + lt, + gt, + le, + ge, + range, + eqBatch, + eqBatchSmall, }; -pub const Function = enum { Single, Range, Batch, BatchSmall }; - -pub fn batch(comptime op: Op, T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn eqBatch(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { const size = utils.sizeOf(T); const vectorLen = 16 / size; const value = utils.readPtr(T, v, c.start).*; const values = utils.toSlice(T, q[i + size - c.offset .. c.size + @alignOf(T) - c.offset]); const len = values.len / size; var j: usize = 0; - switch (op) { - .eq => { - while (j <= (len)) : (j += vectorLen) { - const vec2: @Vector(vectorLen, T) = values[j..][0..vectorLen].*; - if (std.simd.countElementsWithValue(vec2, value) != 0) { - return true; - } - } - }, - else => { - return false; - }, + while (j <= (len)) : (j += vectorLen) { + const vec2: @Vector(vectorLen, T) = values[j..][0..vectorLen].*; + if (std.simd.countElementsWithValue(vec2, value) != 0) { + return true; + } } return false; } -pub fn batchSmall(comptime op: Op, T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn eqBatchSmall(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { const size = utils.sizeOf(T); const vectorLen = 16 / size; const value = utils.readPtr(T, v, c.start).*; const values = utils.toSlice(T, q[i + size - c.offset .. c.size + @alignOf(T) - c.offset]); const vec: @Vector(vectorLen, T) = values[0..][0..vectorLen].*; - switch (op) { - .eq => { - return (std.simd.countElementsWithValue(vec, value) != 0); - }, - .lt => { - const valueSplat: @Vector(vectorLen, T) = @splat(value); - return @reduce(.Or, valueSplat > vec); - }, - .gt => { - const valueSplat: @Vector(vectorLen, T) = @splat(value); - return @reduce(.Or, valueSplat < vec); - }, - .le => { - const valueSplat: @Vector(vectorLen, T) = @splat(value); - return @reduce(.Or, valueSplat >= vec); - }, - .ge => { - const valueSplat: @Vector(vectorLen, T) = @splat(value); - return @reduce(.Or, valueSplat <= vec); - }, - } + return (std.simd.countElementsWithValue(vec, value) != 0); } -pub fn single(comptime op: Op, T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { - @setEvalBranchQuota(10000); +pub fn eq(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { + const val = utils.readPtr(T, v, c.start).*; + const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; + return val == target; +} +pub fn lt(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { const val = utils.readPtr(T, v, c.start).*; const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; - switch (op) { - .eq => { - return val == target; - }, - .lt => { - return val < target; - }, - .gt => { - return val > target; - }, - .le => { - return val <= target; - }, - .ge => { - return val >= target; - }, - } + return val < target; +} + +pub fn gt(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { + const val = utils.readPtr(T, v, c.start).*; + const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; + return val > target; +} + +pub fn le(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { + const val = utils.readPtr(T, v, c.start).*; + const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; + return val <= target; +} + +pub fn ge(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { + const val = utils.readPtr(T, v, c.start).*; + const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; + return val >= target; } pub fn range(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index df490bbd13..9d86adcdb6 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -86,10 +86,14 @@ inline fn compare( c: *t.FilterCondition, ) bool { const res = switch (meta.func) { - .Single => Compare.single(meta.cmp, T, q, v, index, c), - .Range => Compare.range(T, q, v, index, c), - .Batch => Compare.batch(meta.cmp, T, q, v, index, c), - .BatchSmall => Compare.batchSmall(meta.cmp, T, q, v, index, c), + .eq => Compare.eq(T, q, v, index, c), + .le => Compare.le(T, q, v, index, c), + .lt => Compare.lt(T, q, v, index, c), + .ge => Compare.ge(T, q, v, index, c), + .gt => Compare.gt(T, q, v, index, c), + .range => Compare.range(T, q, v, index, c), + .eqBatch => Compare.eqBatch(T, q, v, index, c), + .eqBatchSmall => Compare.eqBatchSmall(T, q, v, index, c), }; return if (meta.invert) !res else res; } diff --git a/native/query/filter/instruction.zig b/native/query/filter/instruction.zig index 3a5ecf07d3..fc3766e3f0 100644 --- a/native/query/filter/instruction.zig +++ b/native/query/filter/instruction.zig @@ -2,34 +2,28 @@ const std = @import("std"); const t = @import("../../types.zig"); const Compare = @import("compare.zig"); -pub const OpMeta = struct { - invert: bool = false, - cmp: Compare.Op = .eq, - func: Compare.Function = .Single, -}; - -fn getCmp(comptime tag: t.FilterOpCompare) Compare.Op { - return switch (tag) { - .lt, .ltBatch, .ltBatchSmall => .lt, - .le, .leBatch, .leBatchSmall => .le, - .gt, .gtBatch, .gtBatchSmall => .gt, - .ge, .geBatch, .geBatchSmall => .ge, - else => .eq, - }; -} +pub const OpMeta = struct { invert: bool = false, func: Compare.Function }; fn getFunc(comptime tag: t.FilterOpCompare) Compare.Function { return switch (tag) { - .range, .nrange => .Range, - .eqBatch, .neqBatch, .ltBatch, .leBatch, .gtBatch, .geBatch => .Batch, - .eqBatchSmall, .neqBatchSmall, .ltBatchSmall, .leBatchSmall, .gtBatchSmall, .geBatchSmall => .BatchSmall, - else => .Single, + .range, .nrange => Compare.Function.range, + .eqBatch, + .neqBatch, + => Compare.Function.eqBatch, + .eqBatchSmall, + .neqBatchSmall, + => Compare.Function.eqBatchSmall, + .eq, .neq => Compare.Function.eq, + .le => Compare.Function.le, + .lt => Compare.Function.le, + .ge => Compare.Function.ge, + .gt => Compare.Function.gt, + else => Compare.Function.eq, }; } pub fn parseOp(comptime op: t.FilterOpCompare) OpMeta { return .{ - .cmp = getCmp(op), .func = getFunc(op), .invert = switch (op) { .neq, .neqBatch, .neqBatchSmall, .nrange => true, diff --git a/native/types.zig b/native/types.zig index 22e049306b..94a6218f78 100644 --- a/native/types.zig +++ b/native/types.zig @@ -838,17 +838,17 @@ pub const FilterOpCompare = enum(u8) { // ----------- gt = 14, lt = 15, - gtBatch = 16, - ltBatch = 17, - gtBatchSmall = 18, - ltBatchSmall = 19, + // gtBatch = 16, + // ltBatch = 17, + // gtBatchSmall = 18, + // ltBatchSmall = 19, // ----------- ge = 20, le = 21, - geBatch = 22, - leBatch = 23, - geBatchSmall = 24, - leBatchSmall = 25, + // geBatch = 22, + // leBatch = 23, + // geBatchSmall = 24, + // leBatchSmall = 25, // ----------- // eq = 12, // will become quite a lot :L > , < <=, >= diff --git a/src/db-client/query/include/props.ts b/src/db-client/query/include/props.ts index 8537f5dc7b..8579ccbf72 100644 --- a/src/db-client/query/include/props.ts +++ b/src/db-client/query/include/props.ts @@ -1,10 +1,5 @@ import type { PropDef, PropDefEdge } from '../../../schema/index.js' -import { - LangCode, - LangCodeEnum, - LangCodeInverse, - PropType, -} from '../../../zigTsExports.js' +import { PropType } from '../../../zigTsExports.js' import { IncludeField, IncludeOpts, QueryDef, QueryDefType } from '../types.js' export const getAll = ( diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 8d7a5e832e..67a91b9974 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -33,19 +33,15 @@ export type IncludeCtx = { } export type QueryAst = { + include?: Include + select?: { start: number; end: number } locale?: string range?: { start: number; end: number } type?: string target?: string | number | (number | string)[] filter?: FilterAst sort?: { prop: string; order: 'asc' | 'desc' } - props?: Record< - string, - QueryAst & { - include?: Include - select?: { start: number; end: number } - } - > + props?: Record edges?: QueryAst } export type Ctx = { diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index e03f2372a4..7d5d6b5dcd 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -1,3 +1,4 @@ +import { ReaderSchemaEnum } from '../../protocol/index.js' import { PropDef } from '../../schema/defs/index.js' import { pushQueryHeaderSingleReference, @@ -6,6 +7,7 @@ import { } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { include } from './include.js' +import { readPropDef, readSchema } from './readSchema.js' export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { const headerIndex = pushQueryHeaderSingleReference(ctx.query, { @@ -16,16 +18,38 @@ export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { edgeSize: 0, prop: prop.id, }) - const size = include(ast, ctx, prop.typeDef) + + const schema = readSchema(ReaderSchemaEnum.default) + ctx.readSchema.refs[prop.id] = { + schema, + prop: readPropDef(prop, ctx.locales, ast.include), + } + const size = include( + ast, + { + ...ctx, + readSchema: schema, + }, + prop.typeDef, + ) props.includeSize(ctx.query.data, size, headerIndex) + if (ast.edges) { const edges = prop.edges if (!edges) { throw new Error('Ref does not have edges') } + schema.edges = readSchema(ReaderSchemaEnum.edge) props.op(ctx.query.data, QueryType.referenceEdge, headerIndex) props.edgeTypeId(ctx.query.data, edges.id, headerIndex) - const size = include(ast.edges, ctx, edges) + const size = include( + ast.edges, + { + ...ctx, + readSchema: schema.edges, + }, + edges, + ) props.edgeSize(ctx.query.data, size, headerIndex) } } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 6e3b8f4f16..acaeebbc63 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -46,13 +46,13 @@ await test('include', async (t) => { cookie: 1234, }, }) - // client.create('user', { - // name: 'mr y', - // cook: { - // cookie: 1234, - // }, - // mrFriend: { id: a, $level: 67 }, - // }) + client.create('user', { + name: 'mr y', + cook: { + cookie: 1234, + }, + mrFriend: { id: a, $level: 67 }, + }) await db.drain() @@ -74,16 +74,16 @@ await test('include', async (t) => { // NOW ADD MR FRIEND! - // mrFriend: { - // // props: { - // // name: { include: {} }, - // // }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, - // }, + mrFriend: { + props: { + name: { include: {} }, + }, + edges: { + props: { + $level: { include: {} }, + }, + }, + }, }, } From e1e48d6f67a35b9f9c3a5cc216e5334a1f5722d1 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Fri, 30 Jan 2026 10:01:17 +0100 Subject: [PATCH 039/449] fix --- src/db-query/ast/include.ts | 5 ++- src/db-query/ast/multiple.ts | 60 +++++++++++++++++++++++++++- src/db-query/ast/single.ts | 2 +- test/query-ast/include.ts | 77 +++++++++++++++++++++++++----------- 4 files changed, 118 insertions(+), 26 deletions(-) diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index 415f9c47fa..1821fe32df 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -8,6 +8,7 @@ import { pushIncludePartialProp, } from '../../zigTsExports.js' import { Ctx, Include, IncludeCtx, QueryAst } from './ast.js' +import { references } from './multiple.js' import { readPropDef } from './readSchema.js' import { reference } from './single.js' @@ -74,7 +75,9 @@ export const collect = ( const astProp = ast.props[field] const include = astProp.include if (isPropDef(prop)) { - if (prop.type === PropType.reference) { + if (prop.type === PropType.references) { + references(astProp, ctx, prop) + } else if (prop.type === PropType.reference) { reference(astProp, ctx, prop) } else if (include) { if (prop.id === 0) { diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 8147d492c0..9d8b8740fe 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -1,13 +1,16 @@ +import { ReaderSchemaEnum } from '../../protocol/index.js' import { PropDef, TypeDef } from '../../schema/defs/index.js' import { pushQueryHeader, QueryType, ID_PROP, writeQueryHeaderProps as props, + QueryHeaderByteSize, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { include } from './include.js' import { getIteratorType } from './iteratorType.js' +import { readPropDef, readSchema } from './readSchema.js' export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { const rangeStart = ast.range?.start || 0 @@ -30,4 +33,59 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { props.includeSize(ctx.query.data, include(ast, ctx, typeDef), headerIndex) } -export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => {} +export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { + const rangeStart = ast.range?.start || 0 + const headerIndex = pushQueryHeader(ctx.query, { + op: QueryType.references, + prop: prop.id, + includeSize: 0, + typeId: prop.typeDef.id, + offset: rangeStart, + // good default? + limit: (ast.range?.end || 100) + rangeStart, + sort: false, + filterSize: 0, + searchSize: 0, + iteratorType: getIteratorType(), + edgeTypeId: 0, + edgeSize: 0, + edgeFilterSize: 0, + size: 0, + }) + + const schema = readSchema() + ctx.readSchema.refs[prop.id] = { + schema, + prop: readPropDef(prop, ctx.locales, ast.include), + } + const size = include( + ast, + { + ...ctx, + readSchema: schema, + }, + prop.typeDef, + ) + props.includeSize(ctx.query.data, size, headerIndex) + // QueryHeaderByteSize + searchSize + sortSize + includeSize + props.size(ctx.query.data, QueryHeaderByteSize + size, headerIndex) + + if (ast.edges) { + // const edges = prop.edges + // if (!edges) { + // throw new Error('Ref does not have edges') + // } + // schema.edges = readSchema(ReaderSchemaEnum.edge) + // props.op(ctx.query.data, QueryType.referenceEdge, headerIndex) + // props.edgeTypeId(ctx.query.data, edges.id, headerIndex) + // const size = include( + // ast.edges, + // { + // ...ctx, + // readSchema: schema.edges, + // }, + // edges, + // ) + // props.edgeSize(ctx.query.data, size, headerIndex) + } +} diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index 7d5d6b5dcd..2ace77cb6c 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -19,7 +19,7 @@ export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { prop: prop.id, }) - const schema = readSchema(ReaderSchemaEnum.default) + const schema = readSchema() ctx.readSchema.refs[prop.id] = { schema, prop: readPropDef(prop, ctx.locales, ast.include), diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index acaeebbc63..528e6e81d6 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -33,11 +33,18 @@ await test('include', async (t) => { prop: 'mrFriend', $level: 'uint32', }, + friends: { + items: { + ref: 'user', + prop: 'friends', + }, + // $level: 'uint32', + }, }, }, }) - const a = client.create('user', { + const a = await client.create('user', { name: 'AAAAAAAAAA', y: 67, x: true, @@ -46,12 +53,24 @@ await test('include', async (t) => { cookie: 1234, }, }) + + const b = await client.create('user', { + name: 'BBBBBBBBB', + y: 67, + x: true, + flap: 9999, + cook: { + cookie: 1234, + }, + }) + client.create('user', { - name: 'mr y', + name: 'CCCCCCCCC', cook: { cookie: 1234, }, - mrFriend: { id: a, $level: 67 }, + // mrFriend: { id: a, $level: 67 }, + friends: [a, b], }) await db.drain() @@ -63,26 +82,32 @@ await test('include', async (t) => { const ast = { type: 'user', props: { - name: { include: {} }, - y: { include: {} }, - x: { include: {} }, - cook: { - props: { - cookie: { include: {} }, - }, - }, - - // NOW ADD MR FRIEND! - - mrFriend: { + // name: { include: {} }, + // y: { include: {} }, + // x: { include: {} }, + // cook: { + // props: { + // cookie: { include: {} }, + // }, + // }, + + // // NOW ADD MR FRIEND! + + // mrFriend: { + // props: { + // name: { include: {} }, + // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, + // }, + + friends: { props: { name: { include: {} }, }, - edges: { - props: { - $level: { include: {} }, - }, - }, }, }, } @@ -90,10 +115,16 @@ await test('include', async (t) => { const ctx = astToQueryCtx( client.schema!, ast, - new AutoSizedUint8Array(1000), // this will be shared - new AutoSizedUint8Array(1000), // this will be shared + new AutoSizedUint8Array(1000), + new AutoSizedUint8Array(1000), ) + // TODO + // references + // edges + // TEXT - make this localized true + // OPTS + console.dir(ctx, { depth: 10 }) const result = await db.server.getQueryBuf(ctx.query) @@ -103,7 +134,7 @@ await test('include', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - console.log(obj) + console.dir(obj, { depth: 10 }) console.log( JSON.stringify(obj).length, From fd44984424d8fad668b11082cf84174286d3ebdd Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Fri, 30 Jan 2026 10:28:12 +0100 Subject: [PATCH 040/449] edge prop --- native/query/include/include.zig | 2 + native/query/multiple.zig | 2 + src/db-client/query/toByteCode/toByteCode.ts | 2 +- src/db-query/ast/iteratorType.ts | 11 +++-- src/db-query/ast/multiple.ts | 46 ++++++++++---------- src/protocol/db-read/read.ts | 2 + test/query-ast/include.ts | 9 +++- 7 files changed, 43 insertions(+), 31 deletions(-) diff --git a/native/query/include/include.zig b/native/query/include/include.zig index 6e5a57d60a..75e1fed15b 100644 --- a/native/query/include/include.zig +++ b/native/query/include/include.zig @@ -132,6 +132,8 @@ pub fn include( try append.stripCrc32(ctx.thread, header.prop, value); }, t.PropType.microBuffer, t.PropType.vector, t.PropType.colVec => { + std.debug.print("flap flap {any} \n", .{value}); + // Fixed size try ctx.thread.query.append(header.prop); try ctx.thread.query.append(value); diff --git a/native/query/multiple.zig b/native/query/multiple.zig index c4a949ded4..2bce53f35e 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -79,6 +79,7 @@ fn iteratorEdge( _ = it.next() orelse return 0; offset -= 1; } + while (it.nextRef()) |ref| { try ctx.thread.query.append(t.ReadOp.id); try ctx.thread.query.append(Node.getNodeId(ref.node)); @@ -217,6 +218,7 @@ pub fn references( switch (header.iteratorType) { .edgeInclude => { + std.debug.print("hello start IT => \n", .{}); var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); }, diff --git a/src/db-client/query/toByteCode/toByteCode.ts b/src/db-client/query/toByteCode/toByteCode.ts index 9a9b59d041..2248a6aa4a 100644 --- a/src/db-client/query/toByteCode/toByteCode.ts +++ b/src/db-client/query/toByteCode/toByteCode.ts @@ -181,7 +181,7 @@ export function defToBuffer( edgeTypeId, edgeSize, edgeFilterSize: 0, // this is nice - size: buffer.byteLength + includeSize, + size: buffer.byteLength + includeSize, // is this used? QueryHeaderByteSize + searchSize + sortSize + includeSize }, 0, ) diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index 42e245c761..b9f6f2069f 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -10,24 +10,23 @@ import { } from '../../zigTsExports.js' import { QueryDef, QueryDefType } from '../../db-client/query/types.js' -export const getIteratorType = (): QueryIteratorTypeEnum => { +export const getIteratorType = ( + edge: boolean, + edgeInclude: boolean, +): QueryIteratorTypeEnum => { const hasFilter: boolean = false const hasSearch = false //def.search?.size && def.search.size > 0 const isVector = false // hasSearch && def.search!.isVector // const hasFilter = def.filter.size > 0 const isDesc = false // def.order === Order.desc - const edgeInclude = false // def.edges const hasSort = false // // def.sort && // (def.sort.prop !== ID_PROP || def.type === QueryDefType.References) - const hasEdges = false // def.type === QueryDefType.References && - // @ts-ignore - // def.target.propDef.edgeNodeTypeId > 0 let base = QUERY_ITERATOR_DEFAULT - if (hasEdges && !edgeInclude) { + if (edge && !edgeInclude) { base = QUERY_ITERATOR_EDGE } diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 9d8b8740fe..38967d14af 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -24,7 +24,7 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { sort: false, filterSize: 0, searchSize: 0, - iteratorType: getIteratorType(), + iteratorType: getIteratorType(false, false), edgeTypeId: 0, edgeSize: 0, edgeFilterSize: 0, @@ -41,16 +41,15 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { includeSize: 0, typeId: prop.typeDef.id, offset: rangeStart, - // good default? limit: (ast.range?.end || 100) + rangeStart, sort: false, filterSize: 0, searchSize: 0, - iteratorType: getIteratorType(), + iteratorType: getIteratorType(false, false), edgeTypeId: 0, edgeSize: 0, edgeFilterSize: 0, - size: 0, + size: 0, // this is only used for [IDS] handle this differently }) const schema = readSchema() @@ -58,6 +57,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { schema, prop: readPropDef(prop, ctx.locales, ast.include), } + const size = include( ast, { @@ -67,25 +67,27 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { prop.typeDef, ) props.includeSize(ctx.query.data, size, headerIndex) - // QueryHeaderByteSize + searchSize + sortSize + includeSize - props.size(ctx.query.data, QueryHeaderByteSize + size, headerIndex) if (ast.edges) { - // const edges = prop.edges - // if (!edges) { - // throw new Error('Ref does not have edges') - // } - // schema.edges = readSchema(ReaderSchemaEnum.edge) - // props.op(ctx.query.data, QueryType.referenceEdge, headerIndex) - // props.edgeTypeId(ctx.query.data, edges.id, headerIndex) - // const size = include( - // ast.edges, - // { - // ...ctx, - // readSchema: schema.edges, - // }, - // edges, - // ) - // props.edgeSize(ctx.query.data, size, headerIndex) + const edges = prop.edges + if (!edges) { + throw new Error('Ref does not have edges') + } + schema.edges = readSchema(ReaderSchemaEnum.edge) + props.edgeTypeId(ctx.query.data, edges.id, headerIndex) + const size = include( + ast.edges, + { + ...ctx, + readSchema: schema.edges, + }, + edges, + ) + props.iteratorType( + ctx.query.data, + getIteratorType(true, size > 0), + headerIndex, + ) + props.edgeSize(ctx.query.data, size, headerIndex) } } diff --git a/src/protocol/db-read/read.ts b/src/protocol/db-read/read.ts index bca63289b9..9f6458c338 100644 --- a/src/protocol/db-read/read.ts +++ b/src/protocol/db-read/read.ts @@ -109,6 +109,7 @@ const references: ReadInstruction = (q, result, i, item) => { } const edge: ReadInstruction = (q, result, i, item) => { + console.log('edge', i) return readInstruction(result[i], q.edges!, result, i + 1, item) } @@ -130,6 +131,7 @@ const readInstruction = ( } else if (instruction === ReadOp.edge) { return edge(q, result, i, item) } else if (instruction === 0) { + console.log('DERP', i) return readMain(q, result, i, item) } else { return readProp(instruction, q, result, i, item) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 528e6e81d6..cff939d126 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -37,8 +37,8 @@ await test('include', async (t) => { items: { ref: 'user', prop: 'friends', + $level: 'uint32', }, - // $level: 'uint32', }, }, }, @@ -70,7 +70,7 @@ await test('include', async (t) => { cookie: 1234, }, // mrFriend: { id: a, $level: 67 }, - friends: [a, b], + friends: [{ id: a, $level: 250 }, b], }) await db.drain() @@ -108,6 +108,11 @@ await test('include', async (t) => { props: { name: { include: {} }, }, + edges: { + props: { + $level: { include: {} }, + }, + }, }, }, } From 7b59c690a55be26c06daff66a2045e5be174aa1a Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 30 Jan 2026 11:21:06 +0100 Subject: [PATCH 041/449] wip --- native/modify/modify.zig | 321 +++------------------------- native/types.zig | 11 +- src/db-client/modify/index.ts | 18 +- src/db-client/modify/types.ts | 18 +- src/schema/def/typeDef.ts | 2 +- src/schema/defs/getTypeDefs.ts | 2 - src/schema/defs/props/base.ts | 11 - src/schema/defs/props/fixed.ts | 224 ++++++++++++++++++- src/schema/defs/props/references.ts | 41 ++-- src/schema/defs/props/separate.ts | 88 ++++++-- src/zigTsExports.ts | 50 ++++- test/modify/numbers.ts | 147 +++++++++++++ test/modify/references.ts | 100 +++++++++ test/modify/string.ts | 73 +++++++ test/modify/timestamp.ts | 97 +++++++++ test/youzi.ts | 21 +- 16 files changed, 840 insertions(+), 384 deletions(-) create mode 100644 test/modify/numbers.ts create mode 100644 test/modify/references.ts create mode 100644 test/modify/string.ts create mode 100644 test/modify/timestamp.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 2a4f4e571d..17c58f9da8 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -5,33 +5,25 @@ const Schema = @import("../selva/schema.zig"); const Node = @import("../selva/node.zig"); const Fields = @import("../selva/fields.zig"); const References = @import("../selva/references.zig"); -const Modify = @import("common.zig"); -const createField = @import("create.zig").createField; -const deleteFieldSortIndex = @import("delete.zig").deleteFieldSortIndex; -const deleteField = @import("delete.zig").deleteField; -const deleteTextLang = @import("delete.zig").deleteTextLang; -const addEmptyToSortIndex = @import("sort.zig").addEmptyToSortIndex; -const addEmptyTextToSortIndex = @import("sort.zig").addEmptyTextToSortIndex; const utils = @import("../utils.zig"); -const Update = @import("update.zig"); -const dbSort = @import("../sort/sort.zig"); -const config = @import("config"); const Thread = @import("../thread/thread.zig"); const t = @import("../types.zig"); const DbCtx = @import("../db/ctx.zig").DbCtx; - -const updateField = Update.updateField; -const updatePartialField = Update.updatePartialField; -const increment = Update.increment; -const read = utils.read; -const write = utils.write; -const assert = std.debug.assert; -const ModifyCtx = Modify.ModifyCtx; - const subs = @import("subscription.zig"); pub const subscription = subs.suscription; +inline fn applyInc(comptime T: type, current: []u8, value: []u8, start: u16, op: t.ModifyIncrement) void { + const curr = utils.read(T, current, start); + const inc = utils.read(T, value, 0); + const res = switch (op) { + .increment => if (@typeInfo(T) == .float) curr + inc else curr +% inc, + .decrement => if (@typeInfo(T) == .float) curr - inc else curr -% inc, + else => return, + }; + utils.write(value, res, 0); +} + // ----------NAPI------------- pub fn modifyThread(env: napi.Env, info: napi.Info) callconv(.c) napi.Value { modifyInternalThread( @@ -47,271 +39,6 @@ fn modifyInternalThread(env: napi.Env, info: napi.Info) !void { const dbCtx = try napi.get(*DbCtx, env, args[1]); try dbCtx.threads.modify(batch); } -// ----------------------- - -fn switchType(ctx: *ModifyCtx, typeId: u16) !void { - ctx.typeId = typeId; - ctx.typeEntry = try Node.getType(ctx.db, ctx.typeId); - ctx.typeSortIndex = dbSort.getTypeSortIndexes(ctx.db, ctx.typeId); - // ctx.subTypes = ctx.thread.subscriptions.types.get(ctx.typeId); - // if (ctx.subTypes) |st| { - // st.typeModified = true; - // } - ctx.node = null; -} - -fn writeoutPrevNodeId(ctx: *ModifyCtx, resultLen: *u32, prevNodeId: u32, result: []u8) void { - if (prevNodeId != 0) { - utils.write(result, prevNodeId, resultLen.*); - utils.writeAs(u8, result, ctx.err, resultLen.* + 4); - ctx.err = t.ModifyError.null; - resultLen.* += 5; - } -} - -fn newNode(ctx: *ModifyCtx) !void { - const id = ctx.db.ids[ctx.typeId - 1] + 1; - ctx.node = try Node.upsertNode(ctx, ctx.typeEntry.?, id); - ctx.id = id; - ctx.db.ids[ctx.typeId - 1] = id; - selva.markDirty(ctx, ctx.typeId, id); -} - -fn newNodeRing(ctx: *ModifyCtx, maxId: u32) !void { - const nextId = ctx.db.ids[ctx.typeId - 1] % maxId + 1; - ctx.node = Node.getNode(ctx.typeEntry.?, nextId); - - if (ctx.node) |oldNode| { - Node.flushNode(ctx, ctx.typeEntry.?, oldNode); - } else { - ctx.node = try Node.upsertNode(ctx, ctx.typeEntry.?, nextId); - } - - ctx.id = nextId; - ctx.db.ids[ctx.typeId - 1] = nextId; - selva.markDirty(ctx, ctx.typeId, nextId); -} - -fn getLargeRef(db: *DbCtx, node: Node.Node, fs: Schema.FieldSchema, dstId: u32) ?References.ReferenceLarge { - if (dstId == 0) { // assume reference - return References.getReference(node, fs); - } else { // references - if (References.getReferences(false, true, db, node, fs)) |iterator| { - const refs = iterator.refs; - const any = References.referencesGet(refs, dstId); - if (any.type == selva.c.SELVA_NODE_REFERENCE_LARGE) { - return any.p.large; - } - } - } - return null; -} - -pub fn switchEdgeId(ctx: *ModifyCtx, srcId: u32, dstId: u32, refField: u8) anyerror!u32 { - var prevNodeId: u32 = 0; - if (srcId == 0 or ctx.node == null) { - return 0; - } - const fs = Schema.getFieldSchema(ctx.typeEntry, refField) catch { - return 0; - }; - ctx.fieldSchema = fs; - if (getLargeRef(ctx.db, ctx.node.?, fs, dstId)) |ref| { - const efc = Schema.getEdgeFieldConstraint(fs); - - const edgeNode = Node.ensureRefEdgeNode(ctx, ctx.node.?, efc, ref) catch { - return 0; - }; - - const edgeId = ref.*.edge; - - // if its zero then we don't want to switch (for upsert) - prevNodeId = ctx.id; - - switchType(ctx, efc.edge_node_type) catch { - return 0; - }; - ctx.id = edgeId; - ctx.node = edgeNode; - if (ctx.node == null) { - ctx.err = t.ModifyError.nx; - } else { - // try subs.checkId(ctx); - // It would be even better if we'd mark it dirty only in the case - // something was actually changed. - selva.markDirty(ctx, ctx.typeId, ctx.id); - } - } - - return prevNodeId; -} - -pub fn writeData(ctx: *ModifyCtx, buf: []u8) anyerror!usize { - var i: usize = 0; - while (i < buf.len) { - const op: t.ModOp = @enumFromInt(buf[i]); - // TODO set i += 1; HERE and remove from each individual thing - const data: []u8 = buf[i + 1 ..]; - - switch (op) { - .padding => { - i += 1; - }, - .switchProp => { - ctx.field = data[0]; - i += 3; - ctx.fieldSchema = try Schema.getFieldSchema(ctx.typeEntry.?, ctx.field); - ctx.fieldType = @enumFromInt(data[1]); - if (ctx.field != 0) { - ctx.currentSortIndex = dbSort.getSortIndex( - ctx.typeSortIndex, - ctx.field, - 0, - t.LangCode.none, - ); - } else { - ctx.currentSortIndex = null; - } - }, - .deleteNode => { - if (ctx.node) |node| { - // subs.stage(ctx, subs.Op.deleteNode); - Node.deleteNode(ctx, ctx.typeEntry.?, node) catch {}; - ctx.node = null; - } - i += 1; - }, - .deleteTextField => { - const lang: t.LangCode = @enumFromInt(data[0]); - deleteTextLang(ctx, lang); - i += 2; - }, - .switchIdCreate => { - writeoutPrevNodeId(ctx, &ctx.resultLen, ctx.id, ctx.result); - try newNode(ctx); - i += 1; - }, - .switchIdCreateRing => { - writeoutPrevNodeId(ctx, &ctx.resultLen, ctx.id, ctx.result); - const maxNodeId = read(u32, data, 0); - try newNodeRing(ctx, maxNodeId); - i += 5; - }, - .switchIdCreateUnsafe => { - writeoutPrevNodeId(ctx, &ctx.resultLen, ctx.id, ctx.result); - ctx.id = read(u32, data, 0); - if (ctx.id > ctx.db.ids[ctx.typeId - 1]) { - ctx.db.ids[ctx.typeId - 1] = ctx.id; - } - ctx.node = try Node.upsertNode(ctx, ctx.typeEntry.?, ctx.id); - selva.markDirty(ctx, ctx.typeId, ctx.id); - i += 5; - }, - .switchIdUpdate => { - const id = read(u32, data, 0); - if (id != 0) { - writeoutPrevNodeId(ctx, &ctx.resultLen, ctx.id, ctx.result); - // if its zero then we don't want to switch (for upsert) - ctx.id = id; - ctx.node = Node.getNode(ctx.typeEntry.?, ctx.id); - if (ctx.node == null) { - ctx.err = t.ModifyError.nx; - } else { - // try subs.checkId(ctx); - // It would be even better if we'd mark it dirty only in the case - // something was actually changed. - selva.markDirty(ctx, ctx.typeId, ctx.id); - } - } - i += 5; - }, - // .switchEdgeId => { - // const srcId = read(u32, data, 0); - // const dstId = read(u32, data, 4); - // const refField = read(u8, data, 8); - // const prevNodeId = try switchEdgeId(ctx, srcId, dstId, refField); - // writeoutPrevNodeId(ctx, &ctx.resultLen, prevNodeId, ctx.result); - // i += 10; - // }, - .upsert => { - const writeIndex = read(u32, data, 0); - const updateIndex = read(u32, data, 4); - var nextIndex: u32 = writeIndex; - var j: u32 = 8; - while (j < writeIndex) { - const prop = read(u8, data, j); - const len = read(u32, data, j + 1); - const val = data[j + 5 .. j + 5 + len]; - if (Fields.getAliasByName(ctx.typeEntry.?, prop, val)) |node| { - write(data, Node.getNodeId(node), updateIndex + 1); - nextIndex = updateIndex; - break; - } - j = j + 5 + len; - } - i += nextIndex + 1; - }, - .insert => { - const writeIndex = read(u32, data, 0); - const endIndex = read(u32, data, 4); - var nextIndex: u32 = writeIndex; - var j: u32 = 8; - while (j < writeIndex) { - const prop = read(u8, data, j); - const len = read(u32, data, j + 1); - const val = data[j + 5 .. j + 5 + len]; - if (Fields.getAliasByName(ctx.typeEntry.?, prop, val)) |node| { - const id = Node.getNodeId(node); - write(buf, id, ctx.resultLen); - write(buf, t.ModifyError.null, ctx.resultLen + 4); - ctx.resultLen += 5; - nextIndex = endIndex; - break; - } - j = j + 5 + len; - } - i += nextIndex + 1; - }, - .switchType => { - try switchType(ctx, read(u16, data, 0)); - i += 3; - }, - .addEmptySort => { - i += try addEmptyToSortIndex(ctx, data) + 1; - }, - .addEmptySortText => { - i += try addEmptyTextToSortIndex(ctx, data) + 1; - }, - .delete => { - i += try deleteField(ctx) + 1; - }, - .deleteSortIndex => { - i += try deleteFieldSortIndex(ctx) + 1; - }, - .createProp => { - i += try createField(ctx, data) + 1; - }, - .updateProp => { - i += try updateField(ctx, data) + 1; - }, - .updatePartial => { - i += try updatePartialField(ctx, data) + 1; - }, - .increment, .decrement => { - i += try increment(ctx, data, op) + 1; - }, - .expire => { - Node.expireNode(ctx, ctx.typeId, ctx.id, std.time.timestamp() + read(u32, data, 0)); - i += 5; - }, - .end => { - i += 1; - break; - }, - } - } - return i; -} pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []t.ModifyResultItem) !void { var j: usize = 0; @@ -321,10 +48,21 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u if (propId == 0) { // main handling const main = utils.readNext(t.ModifyMainHeader, data, &j); - const value = data[j .. j + main.size]; const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); + const size = main.type.size(); + const value = data[j .. j + size]; + if (main.increment != .none) { + switch (main.type) { + .number => applyInc(f64, current, value, main.start, main.increment), + .timestamp => applyInc(i64, current, value, main.start, main.increment), + .int8, .uint8 => applyInc(u8, current, value, main.start, main.increment), + .int16, .uint16 => applyInc(u16, current, value, main.start, main.increment), + .int32, .uint32 => applyInc(u32, current, value, main.start, main.increment), + else => {}, + } + } utils.copy(u8, current, value, main.start); - j += main.size; + j += size; } else { // separate handling const prop = utils.readNext(t.ModifyPropHeader, data, &j); @@ -336,7 +74,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u const hll = try Fields.ensurePropTypeString(node, propSchema); selva.c.hll_init(hll, cardinality.precision, cardinality.sparse); while (k < value.len) { - const hash = read(u64, value, k); + const hash = utils.read(u64, value, k); selva.c.hll_add(hll, hash); k += 8; } @@ -347,12 +85,13 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u var k: usize = 0; const meta = utils.readNext(t.ModifyReferenceMetaHeader, value, &k); var refId = meta.id; - + std.debug.print("reference id: {any}, istmp: {any}, refType: {any}\n", .{ refId, meta.isTmp, refTypeEntry }); if (meta.isTmp) { refId = items[refId].id; } if (Node.getNode(refTypeEntry, refId)) |dst| { + std.debug.print("write reference id: {any}, istmp: {any}", .{ refId, meta.isTmp }); _ = try References.writeReference(db, node, propSchema, dst); if (meta.size != 0) { const edgeProps = value[k .. k + meta.size]; @@ -374,12 +113,12 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u switch (references.op) { .ids => { const offset = utils.alignLeft(u32, refs); - const u32Ids = read([]u32, refs[4 - offset .. refs.len - offset], 0); + const u32Ids = utils.read([]u32, refs[4 - offset .. refs.len - offset], 0); try References.putReferences(db, node, propSchema, u32Ids); }, .tmpIds => { const offset = utils.alignLeft(u32, refs); - const u32Ids = read([]u32, refs[4 - offset .. refs.len - offset], 0); + const u32Ids = utils.read([]u32, refs[4 - offset .. refs.len - offset], 0); for (u32Ids) |*id| { id.* = items[id.*].id; } @@ -388,7 +127,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u .idsWithMeta => { const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); const refTypeEntry = try Node.getType(db, refTypeId); - const count = read(u32, refs, 0); + const count = utils.read(u32, refs, 0); var x: usize = 4; _ = selva.c.selva_fields_prealloc_refs(db.selva, node, propSchema, count); while (x < refs.len) { diff --git a/native/types.zig b/native/types.zig index 22e049306b..2567a08e79 100644 --- a/native/types.zig +++ b/native/types.zig @@ -113,10 +113,17 @@ pub const ModifyCreateHeader = packed struct { size: u32, }; +pub const ModifyIncrement = enum(u8) { + none = 0, + increment = 1, + decrement = 2, +}; + pub const ModifyMainHeader = packed struct { id: u8, + type: PropType, start: u16, - size: u16, + increment: ModifyIncrement, }; pub const ModifyPropHeader = packed struct { @@ -266,8 +273,6 @@ pub const PropType = enum(u8) { pub fn size(self: PropType) u8 { switch (self) { .timestamp, - // .created, - // .updated, .number, => return 8, .int8, diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 97bb8fbc08..f148966e6d 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -1,6 +1,7 @@ import { SchemaOut } from '../../schema.js' import { Modify, + ModifyIncrement, pushModifyCreateHeader, pushModifyDeleteHeader, pushModifyHeader, @@ -41,9 +42,22 @@ export const serializeProps = ( } else { const prop = def as PropDef if (prop.size) { - pushModifyMainHeader(buf, prop) - prop.pushValue(buf, val, op, lang) + // main + const increment = + val !== null && typeof val === 'object' && (val as any).increment + pushModifyMainHeader(buf, { + id: 0, + start: prop.start, + type: prop.type, + increment: increment + ? increment < 0 + ? ModifyIncrement.decrement + : ModifyIncrement.increment + : ModifyIncrement.none, + }) + prop.pushValue(buf, increment ? Math.abs(increment) : val, op, lang) } else { + // separate const index = pushModifyPropHeader(buf, prop) const start = buf.length prop.pushValue(buf, val, op, lang) diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 7340211db0..06846b8170 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -13,19 +13,21 @@ type TypedArray = | Float32Array | Float64Array +type NumInc = number | { increment: number } + type TypeMap = { string: string - number: number - int8: number - uint8: number - int16: number - uint16: number - int32: number - uint32: number + number: NumInc + int8: NumInc + uint8: NumInc + int16: NumInc + uint16: NumInc + int32: NumInc + uint32: NumInc boolean: boolean text: string json: any - timestamp: number | string | Date + timestamp: NumInc | string | Date binary: Uint8Array alias: string vector: TypedArray diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index 6ec05066b2..c527309e78 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -36,7 +36,7 @@ export const updateTypeDefs = (schema: SchemaOut) => { const schemaTypesParsedById: { [id: number]: SchemaTypeDef } = {} let typeIdCnt = 1 - for (const typeName in schema.types) { + for (const typeName of Object.keys(schema.types).sort()) { const type = schema.types[typeName] const locales = schema.locales ?? { en: {} } const result = createEmptyDef(typeName, type, locales) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 9dae75b781..ffbe3fb113 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -151,7 +151,5 @@ export const getTypeDefs = (schema: SchemaOut): Map => { // ----------- add to cache -------- cache.set(schema, typeDefs) - - console.dir(typeDefs) return typeDefs } diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index bb3af5f223..f5605140e4 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -30,14 +30,3 @@ export class BasePropDef implements PropDef { // To be implemented by subclasses } } - -const filter = [ - { - field: 'nr', - op: '<', - value: 10, - and: { - field: 'nr', - }, - }, -] diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 1ff79320d5..f3d43efbe4 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -8,52 +8,245 @@ import type { TypeDef } from '../index.js' export const number = class Number extends BasePropDef { override type: PropTypeEnum = PropType.number override size = 8 - override pushValue(buf: AutoSizedUint8Array, value: number) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number') { + throw new Error('Invalid type for number ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } buf.pushDoubleLE(value) } } export const timestamp = class Timestamp extends number { override type = PropType.timestamp - override pushValue(buf: AutoSizedUint8Array, value: number | string) { - buf.pushInt64(convertToTimestamp(value)) + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number | string { + const ts = convertToTimestamp(value as any) + const prop = this.prop as any + if (prop.min !== undefined && ts < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && ts > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushInt64(ts) } } export const uint8 = class Uint8 extends BasePropDef { override type: PropTypeEnum = PropType.uint8 override size = 1 - override pushValue(buf: AutoSizedUint8Array, value: number): void { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for uint8 ' + this.path.join('.')) + } + if (value < 0 || value > 255) { + throw new Error('Value out of range for uint8 ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } buf.pushUint8(value) } } export const int8 = class Int8 extends uint8 { override type = PropType.int8 + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for int8 ' + this.path.join('.')) + } + if (value < -128 || value > 127) { + throw new Error('Value out of range for int8 ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint8(value) + } } export const uint16 = class Uint16 extends BasePropDef { override type: PropTypeEnum = PropType.uint16 override size = 2 - override pushValue(buf: AutoSizedUint8Array, value: number): void { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for uint16 ' + this.path.join('.')) + } + if (value < 0 || value > 65535) { + throw new Error('Value out of range for uint16 ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } buf.pushUint16(value) } } export const int16 = class Int16 extends uint16 { override type = PropType.int16 + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for int16 ' + this.path.join('.')) + } + if (value < -32768 || value > 32767) { + throw new Error('Value out of range for int16 ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint16(value) + } } export const uint32 = class Uint32 extends BasePropDef { override type: PropTypeEnum = PropType.uint32 override size = 4 - override pushValue(buf: AutoSizedUint8Array, value: number): void { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for uint32 ' + this.path.join('.')) + } + if (value < 0 || value > 4294967295) { + throw new Error('Value out of range for uint32 ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } buf.pushUint32(value) } } export const int32 = class Int32 extends uint32 { override type = PropType.int32 + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for int32 ' + this.path.join('.')) + } + if (value < -2147483648 || value > 2147483647) { + throw new Error('Value out of range for int32 ' + this.path.join('.')) + } + const prop = this.prop as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint32(value) + } } export const enum_ = class Enum extends uint8 { @@ -69,7 +262,16 @@ export const enum_ = class Enum extends uint8 { enum: Record = {} vals = new Map() - override pushValue(buf: AutoSizedUint8Array, value: EnumItem): void { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is EnumItem { + if (typeof value !== 'string' && typeof value !== 'number') { + throw new Error('Invalid type for enum ' + this.path.join('.')) + } + if (!this.vals.has(value)) { + throw new Error(`Invalid enum value ${value} for ${this.path.join('.')}`) + } buf.pushUint8(this.vals.get(value) ?? 0) } } @@ -77,7 +279,13 @@ export const enum_ = class Enum extends uint8 { export const boolean = class Boolean extends BasePropDef { override type = PropType.boolean override size = 1 - override pushValue(buf: AutoSizedUint8Array, value: boolean): void { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is boolean { + if (typeof value !== 'boolean') { + throw new Error('Invalid type for boolean ' + this.path.join('.')) + } buf.pushUint8(value ? 1 : 0) } } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 6c5b15b6d9..0d3e5d8af1 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -132,7 +132,8 @@ const setReferences = ( lang: LangCodeEnum, ) => { let offset = 0 - while (offset < value.length) { + const len = value.length + while (offset < len) { const item = value[offset] if (getRealId(item)) { const index = pushModifyReferencesHeader(buf, { @@ -201,27 +202,34 @@ export const references = class References extends BasePropDef { override type: PropTypeEnum = PropType.references override pushValue( buf: AutoSizedUint8Array, - value: any, + value: unknown, op: ModifyEnum, lang: LangCodeEnum, - ) { + ): asserts value is any { if (typeof value !== 'object' || value === null) { throw new Error('References value must be an object and not null') } + + const val = value as { + add?: any[] + update?: any[] + delete?: any[] + } + if (Array.isArray(value)) { if (op === Modify.update) { buf.push(ModifyReferences.clear) } setReferences(buf, value, this, op, lang) } - if (value.add) { - setReferences(buf, value.add, this, op, lang) + if (val.add) { + setReferences(buf, val.add, this, op, lang) } - if (value.update) { - setReferences(buf, value.update, this, op, lang) + if (val.update) { + setReferences(buf, val.update, this, op, lang) } - if (value.delete) { - deleteReferences(buf, value) + if (val.delete) { + deleteReferences(buf, val.delete) } } } @@ -230,10 +238,10 @@ export const reference = class Reference extends BasePropDef { override type: PropTypeEnum = PropType.reference override pushValue( buf: AutoSizedUint8Array, - value: any, + value: unknown, lang: LangCodeEnum, op: ModifyEnum, - ) { + ): asserts value is any { const id = getRealId(value) if (id) { pushModifyReferenceMetaHeader(buf, { @@ -258,8 +266,9 @@ export const reference = class Reference extends BasePropDef { } if (typeof value === 'object' && value !== null) { - const realId = getRealId(value.id) - const id = realId || getTmpId(value.id) + const val = value as { id: any } + const realId = getRealId(val.id) + const id = realId || getTmpId(val.id) if (id !== undefined) { const index = pushModifyReferenceMetaHeader(buf, { id, @@ -268,7 +277,7 @@ export const reference = class Reference extends BasePropDef { }) const prop: PropDef = this if (prop.edges) { - const edges = getEdges(value) + const edges = getEdges(val) if (edges) { const start = buf.length serializeProps(prop.edges.tree, edges, buf, op, lang) @@ -282,8 +291,8 @@ export const reference = class Reference extends BasePropDef { return } - if (value.id instanceof BasedModify) { - throw value.id + if (val.id instanceof BasedModify) { + throw val.id } } } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 13b352188e..3324efafa9 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -27,21 +27,49 @@ export const string = class String extends BasePropDef { } if (this.size) { this.type = PropType.stringFixed - this.pushValue = this.pushFixedValue + this.pushValue = this.pushFixedValue as any } } override type: PropTypeEnum = PropType.string override pushValue( buf: AutoSizedUint8Array, - val: string, + val: unknown, lang: LangCodeEnum, - ) { + ): asserts val is string { + if (typeof val !== 'string') { + throw new Error('Invalid type for string ' + this.path.join('.')) + } + const prop = this.prop as SchemaString + if (prop.min !== undefined && val.length < prop.min) { + throw new Error( + `Length ${val.length} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && val.length > prop.max) { + throw new Error( + `Length ${val.length} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } const normalized = val.normalize('NFKD') // TODO make header! // TODO compression buf.pushUint8(lang) buf.pushUint8(NOT_COMPRESSED) const written = buf.pushString(normalized) + + if (prop.maxBytes !== undefined) { + if (written > prop.maxBytes) { + throw new Error( + `Byte length ${written} is larger than maxBytes ${ + prop.maxBytes + } for ${this.path.join('.')}`, + ) + } + } const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } @@ -55,21 +83,45 @@ export const text = class Text extends string { export const json = class Json extends string { override type = PropType.json - override pushValue(buf: AutoSizedUint8Array, value: any, lang: LangCodeEnum) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + lang: LangCodeEnum, + ) { + if (value === undefined) { + throw new Error('Invalid undefined value for json ' + this.path.join('.')) + } super.pushValue(buf, JSON.stringify(value), lang) } } export const binary = class Binary extends BasePropDef { override type = PropType.binary - override pushValue(buf: AutoSizedUint8Array, value: Uint8Array) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is Uint8Array { + if (!(value instanceof Uint8Array)) { + throw new Error('Invalid type for binary ' + this.path.join('.')) + } + const prop = this.prop as SchemaString + if (prop.maxBytes !== undefined && value.byteLength > prop.maxBytes) { + throw new Error( + `Byte length ${value.byteLength} is larger than maxBytes ${ + prop.maxBytes + } for ${this.path.join('.')}`, + ) + } buf.set(value, buf.length) } } export const alias = class Alias extends BasePropDef { override type = PropType.alias - override pushValue(buf: AutoSizedUint8Array, value: any) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is any { throw new Error('Serialize alias not implemented') } } @@ -83,7 +135,10 @@ export const cardinality = class Cardinality extends BasePropDef { sparse: boolean precision: number override type = PropType.cardinality - override pushValue(buf: AutoSizedUint8Array, value: any) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is any { if (value instanceof Uint8Array && value.byteLength !== 8) { // buf.set(value, buf.length) throw new Error('unhandled error cardi') @@ -93,11 +148,13 @@ export const cardinality = class Cardinality extends BasePropDef { value = [value] } - if (value.length === 0) return + const items = value as any[] + + if (items.length === 0) return pushModifyCardinalityHeader(buf, this) - for (const item of value) { + for (const item of items) { // validate(item, def) if (typeof item === 'string') { buf.reserveUint64() @@ -105,8 +162,7 @@ export const cardinality = class Cardinality extends BasePropDef { } else if (item instanceof Uint8Array && item.byteLength === 8) { buf.set(item, buf.length) } else { - throw new Error('unhandled error cardi') - // throw [def, val] + throw new Error('Invalid value for cardinality ' + this.path.join('.')) } } } @@ -119,7 +175,10 @@ export const vector = class Vector extends BasePropDef { } vectorSize: number override type: PropTypeEnum = PropType.vector - override pushValue(buf: AutoSizedUint8Array, value: any) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is any { throw new Error('Serialize vector not implemented') } } @@ -131,7 +190,10 @@ export const colvec = class ColVec extends BasePropDef { } colvecSize: number override type = PropType.colVec - override pushValue(buf: AutoSizedUint8Array, value: any) { + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is any { throw new Error('Serialize colvec not implemented') } } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index f4c2ffc689..031340a16d 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -543,10 +543,30 @@ export const pushModifyCreateHeader = ( return index } +export const ModifyIncrement = { + none: 0, + increment: 1, + decrement: 2, +} as const + +export const ModifyIncrementInverse = { + 0: 'none', + 1: 'increment', + 2: 'decrement', +} as const + +/** + none, + increment, + decrement + */ +export type ModifyIncrementEnum = (typeof ModifyIncrement)[keyof typeof ModifyIncrement] + export type ModifyMainHeader = { id: number + type: PropTypeEnum start: number - size: number + increment: ModifyIncrementEnum } export const ModifyMainHeaderByteSize = 5 @@ -560,10 +580,12 @@ export const writeModifyMainHeader = ( ): number => { buf[offset] = Number(header.id) offset += 1 + buf[offset] = Number(header.type) + offset += 1 writeUint16(buf, Number(header.start), offset) offset += 2 - writeUint16(buf, Number(header.size), offset) - offset += 2 + buf[offset] = Number(header.increment) + offset += 1 return offset } @@ -571,11 +593,14 @@ export const writeModifyMainHeaderProps = { id: (buf: Uint8Array, value: number, offset: number) => { buf[offset] = Number(value) }, + type: (buf: Uint8Array, value: PropTypeEnum, offset: number) => { + buf[offset + 1] = Number(value) + }, start: (buf: Uint8Array, value: number, offset: number) => { - writeUint16(buf, Number(value), offset + 1) + writeUint16(buf, Number(value), offset + 2) }, - size: (buf: Uint8Array, value: number, offset: number) => { - writeUint16(buf, Number(value), offset + 3) + increment: (buf: Uint8Array, value: ModifyIncrementEnum, offset: number) => { + buf[offset + 4] = Number(value) }, } @@ -585,16 +610,18 @@ export const readModifyMainHeader = ( ): ModifyMainHeader => { const value: ModifyMainHeader = { id: buf[offset], - start: readUint16(buf, offset + 1), - size: readUint16(buf, offset + 3), + type: (buf[offset + 1]) as PropTypeEnum, + start: readUint16(buf, offset + 2), + increment: (buf[offset + 4]) as ModifyIncrementEnum, } return value } export const readModifyMainHeaderProps = { id: (buf: Uint8Array, offset: number) => buf[offset], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + increment: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyIncrementEnum, } export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { @@ -609,8 +636,9 @@ export const pushModifyMainHeader = ( ): number => { const index = buf.length buf.pushUint8(Number(header.id)) + buf.pushUint8(Number(header.type)) buf.pushUint16(Number(header.start)) - buf.pushUint16(Number(header.size)) + buf.pushUint8(Number(header.increment)) return index } diff --git a/test/modify/numbers.ts b/test/modify/numbers.ts new file mode 100644 index 0000000000..d8e9acf2c3 --- /dev/null +++ b/test/modify/numbers.ts @@ -0,0 +1,147 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('modify numbers', async (t) => { + const db = await testDb(t, { + types: { + thing: { + n: 'number', + u8: 'uint8', + i8: 'int8', + u16: 'uint16', + i16: 'int16', + u32: 'uint32', + i32: 'int32', + }, + }, + }) + + const id1 = await db.create('thing', { + n: 1.5, + u8: 10, + i8: -10, + u16: 1000, + i16: -1000, + u32: 100000, + i32: -100000, + }) + + // Border values + const id2 = await db.create('thing', { + n: 100.5, + u8: 255, + i8: 127, + u16: 65535, + i16: 32767, + u32: 4294967295, + i32: 2147483647, + }) + + // Min values (for signed) + const id3 = await db.create('thing', { + n: -100.5, + u8: 0, + i8: -128, + u16: 0, + i16: -32768, + u32: 0, + i32: -2147483648, + }) + + deepEqual(await db.query('thing').get(), [ + { + id: id1, + n: 1.5, + u8: 10, + i8: -10, + u16: 1000, + i16: -1000, + u32: 100000, + i32: -100000, + }, + { + id: id2, + n: 100.5, + u8: 255, + i8: 127, + u16: 65535, + i16: 32767, + u32: 4294967295, + i32: 2147483647, + }, + { + id: id3, + n: -100.5, + u8: 0, + i8: -128, + u16: 0, + i16: -32768, + u32: 0, + i32: -2147483648, + }, + ]) + + db.update('thing', id1, { + n: 2.5, + u8: 11, + i8: -11, + u16: 1001, + i16: -1001, + u32: 100001, + i32: -100001, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + n: 2.5, + u8: 11, + i8: -11, + u16: 1001, + i16: -1001, + u32: 100001, + i32: -100001, + }) + + db.update('thing', id1, { + n: { increment: 2.5 }, + u8: { increment: 1 }, + i8: { increment: 1 }, + u16: { increment: 1 }, + i16: { increment: 1 }, + u32: { increment: 1 }, + i32: { increment: 1 }, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + n: 5, + u8: 12, + i8: -10, + u16: 1002, + i16: -1000, + u32: 100002, + i32: -100000, + }) + + db.update('thing', id1, { + n: { increment: -2.5 }, + u8: { increment: -1 }, + i8: { increment: -1 }, + u16: { increment: -1 }, + i16: { increment: -1 }, + u32: { increment: -1 }, + i32: { increment: -1 }, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + n: 2.5, + u8: 11, + i8: -11, + u16: 1001, + i16: -1001, + u32: 100001, + i32: -100001, + }) +}) diff --git a/test/modify/references.ts b/test/modify/references.ts new file mode 100644 index 0000000000..8658e9ef06 --- /dev/null +++ b/test/modify/references.ts @@ -0,0 +1,100 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('modify single reference', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + holder: { + dest: { type: 'reference', ref: 'thing', prop: 'refHolders' }, + }, + }, + }) + + const t1 = await db.create('thing', { name: 't1' }) + const t2 = await db.create('thing', { name: 't2' }) + const h1 = await db.create('holder', { dest: t1 }) + + { + const res = await db.query('holder', h1).include('dest').get().toObject() + deepEqual(res, { + id: h1, + dest: { id: t1 }, + }) + } + + await db.update('holder', h1, { dest: t2 }) + + { + const res = await db.query('holder', h1).include('dest').get().toObject() + deepEqual(res, { + id: h1, + dest: { id: t2 }, + }) + } + + await db.update('holder', h1, { dest: { id: t1 } }) + + { + const res = await db.query('holder', h1).include('dest').get().toObject() + deepEqual(res, { + id: h1, + dest: { id: t1 }, + }) + } +}) + +await test('modify references', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + holder: { + dests: { + type: 'references', + items: { + ref: 'thing', + prop: 'refsHolders', + }, + }, + }, + }, + }) + + const t1 = await db.create('thing', { name: 't1' }) + const t2 = await db.create('thing', { name: 't2' }) + const t3 = await db.create('thing', { name: 't3' }) + + // Test set (create) + const h1 = await db.create('holder', { dests: [t1, t2] }) + + const check = async (ids: number[], msg) => { + const res = await db.query('holder', h1).include('dests').get().toObject() + const currentIds = res.dests?.map((v: any) => v.id) || [] + currentIds.sort() + ids.sort() + deepEqual(currentIds, ids, msg) + } + + await check([t1, t2], 'simple') + + // Test add + await db.update('holder', h1, { dests: { add: [t3] } }) + await check([t1, t2, t3], 'add') + + // Test delete + await db.update('holder', h1, { dests: { delete: [t2] } }) + await check([t1, t3], 'delete') + + // Test replace (array) + await db.update('holder', h1, { dests: [t2] }) + await check([t2], 'replace') + + // Test update (acts as add/upsert for references list) + await db.update('holder', h1, { dests: { update: [t3] } }) + await check([t2, t3], 'update') +}) diff --git a/test/modify/string.ts b/test/modify/string.ts new file mode 100644 index 0000000000..00380b3657 --- /dev/null +++ b/test/modify/string.ts @@ -0,0 +1,73 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('modify string', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + }, + }) + + // Basic string + const s1 = 'hello' + const id1 = await db.create('thing', { + name: s1, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s1, + }) + + // Update to another string + const s2 = 'world' + await db.update('thing', id1, { + name: s2, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s2, + }) + + // String with spaces + const s3 = 'foo bar' + await db.update('thing', id1, { + name: s3, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s3, + }) + + // Empty string + const s4 = '' + await db.update('thing', id1, { + name: s4, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s4, + }) + + // Unicode / Special characters + const s5 = 'ñàéïô SPECIAL !@#$%^&*()_+ 123' + await db.update('thing', id1, { + name: s5, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s5.normalize('NFD'), + }) + + // Long string + const s6 = 'a'.repeat(1000) + await db.update('thing', id1, { + name: s6, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s6, + }) +}) diff --git a/test/modify/timestamp.ts b/test/modify/timestamp.ts new file mode 100644 index 0000000000..254f8e2cfe --- /dev/null +++ b/test/modify/timestamp.ts @@ -0,0 +1,97 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('modify timestamp', async (t) => { + const db = await testDb(t, { + types: { + event: { + ts: 'timestamp', + }, + }, + }) + + const t1 = Date.now() + const t2 = t1 + 1000 + const t3 = t1 + 2000 + + const id1 = await db.create('event', { + ts: t1, + }) + + deepEqual(await db.query('event', id1).get(), { + id: id1, + ts: t1, + }) + + await db.update('event', id1, { + ts: t2, + }) + + deepEqual(await db.query('event', id1).get(), { + id: id1, + ts: t2, + }) + + await db.update('event', id1, { + ts: t3, + }) + + deepEqual(await db.query('event', id1).get(), { + id: id1, + ts: t3, + }) + + // Edge cases + await db.update('event', id1, { ts: 0 }) + deepEqual(await db.query('event', id1).get(), { id: id1, ts: 0 }) + + const farFuture = 8640000000000000 // Max JS Date timestamp + await db.update('event', id1, { ts: farFuture }) + deepEqual(await db.query('event', id1).get(), { id: id1, ts: farFuture }) + + // Increment + await db.update('event', id1, { ts: 1000 }) + await db.update('event', id1, { + ts: { increment: 1000 }, + }) + deepEqual(await db.query('event', id1).get(), { id: id1, ts: 2000 }) + + await db.update('event', id1, { + ts: { increment: -500 }, + }) + deepEqual(await db.query('event', id1).get(), { id: id1, ts: 1500 }) + + // String formats + const now = Date.now() + await db.update('event', id1, { ts: 'now' }) + const r1: any = await db.query('event', id1).get() + if (Math.abs(r1.ts - now) > 200) { + throw new Error(`Timestamp 'now' is too far off: ${r1.ts} vs ${now}`) + } + + await db.update('event', id1, { ts: 'now + 1h' }) + const r2: any = await db.query('event', id1).get() + const t2Expr = now + 1000 * 60 * 60 + if (Math.abs(r2.ts - t2Expr) > 200) { + throw new Error( + `Timestamp 'now + 1h' is too far off: ${r2.ts} vs ${t2Expr}`, + ) + } + + await db.update('event', id1, { ts: 'now - 1d' }) + const r3: any = await db.query('event', id1).get() + const t3Expr = now - 1000 * 60 * 60 * 24 + if (Math.abs(r3.ts - t3Expr) > 200) { + throw new Error( + `Timestamp 'now - 1d' is too far off: ${r3.ts} vs ${t3Expr}`, + ) + } + + // Explicit date string + const dateStr = '2025-01-01T00:00:00.000Z' + const dateTs = new Date(dateStr).valueOf() + await db.update('event', id1, { ts: dateStr }) + const r4: any = await db.query('event', id1).get() + deepEqual(r4, { id: id1, ts: dateTs }) +}) diff --git a/test/youzi.ts b/test/youzi.ts index 0b60b6a016..e96d3301c9 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -9,7 +9,7 @@ import { import { parseSchema } from '../src/schema.js' import test from './shared/test.js' -await test('schema defs', async (t) => { +await test('schema-defs', async (t) => { const schema = parseSchema({ types: { user: { @@ -27,25 +27,10 @@ await test('schema defs', async (t) => { }, }, }, - // article: { - // user: { - // ref: 'user', - // prop: 'articles', - // $rating: 'number', - // $role: { - // ref: 'role', - // }, - // }, - // users: { - // items: { - // ref: 'user', - // prop: 'favourites', - // }, - // }, - // }, }, }) const defs = getTypeDefs(schema) + console.dir(defs, { depth: 3 }) }) await test.skip('modify raw', async (t) => { @@ -110,7 +95,7 @@ await test.skip('modify raw', async (t) => { console.dir(res, { depth: null }) }) -await test('modify client', async (t) => { +await test.skip('modify client', async (t) => { const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) From 0842926c2e6d30df8122b6e5cce40dadc1576892 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 30 Jan 2026 11:21:50 +0100 Subject: [PATCH 042/449] update --- native/modify/modify.zig | 2 -- src/zigTsExports.ts | 24 ------------------------ test/modify/references.ts | 6 +++--- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 17c58f9da8..7301fbc150 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -85,13 +85,11 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u var k: usize = 0; const meta = utils.readNext(t.ModifyReferenceMetaHeader, value, &k); var refId = meta.id; - std.debug.print("reference id: {any}, istmp: {any}, refType: {any}\n", .{ refId, meta.isTmp, refTypeEntry }); if (meta.isTmp) { refId = items[refId].id; } if (Node.getNode(refTypeEntry, refId)) |dst| { - std.debug.print("write reference id: {any}, istmp: {any}", .{ refId, meta.isTmp }); _ = try References.writeReference(db, node, propSchema, dst); if (meta.size != 0) { const edgeProps = value[k .. k + meta.size]; diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 031340a16d..70992572e9 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -3833,16 +3833,8 @@ export const FilterOpCompare = { nrange: 11, gt: 14, lt: 15, - gtBatch: 16, - ltBatch: 17, - gtBatchSmall: 18, - ltBatchSmall: 19, ge: 20, le: 21, - geBatch: 22, - leBatch: 23, - geBatchSmall: 24, - leBatchSmall: 25, selectLargeRefs: 203, selectRef: 204, selectSmallRefs: 205, @@ -3862,16 +3854,8 @@ export const FilterOpCompareInverse = { 11: 'nrange', 14: 'gt', 15: 'lt', - 16: 'gtBatch', - 17: 'ltBatch', - 18: 'gtBatchSmall', - 19: 'ltBatchSmall', 20: 'ge', 21: 'le', - 22: 'geBatch', - 23: 'leBatch', - 24: 'geBatchSmall', - 25: 'leBatchSmall', 203: 'selectLargeRefs', 204: 'selectRef', 205: 'selectSmallRefs', @@ -3891,16 +3875,8 @@ export const FilterOpCompareInverse = { nrange, gt, lt, - gtBatch, - ltBatch, - gtBatchSmall, - ltBatchSmall, ge, le, - geBatch, - leBatch, - geBatchSmall, - leBatchSmall, selectLargeRefs, selectRef, selectSmallRefs, diff --git a/test/modify/references.ts b/test/modify/references.ts index 8658e9ef06..e48fcd4d2f 100644 --- a/test/modify/references.ts +++ b/test/modify/references.ts @@ -19,7 +19,7 @@ await test('modify single reference', async (t) => { const h1 = await db.create('holder', { dest: t1 }) { - const res = await db.query('holder', h1).include('dest').get().toObject() + const res = await db.query('holder', h1).include('dest.id').get().toObject() deepEqual(res, { id: h1, dest: { id: t1 }, @@ -29,7 +29,7 @@ await test('modify single reference', async (t) => { await db.update('holder', h1, { dest: t2 }) { - const res = await db.query('holder', h1).include('dest').get().toObject() + const res = await db.query('holder', h1).include('dest.id').get().toObject() deepEqual(res, { id: h1, dest: { id: t2 }, @@ -39,7 +39,7 @@ await test('modify single reference', async (t) => { await db.update('holder', h1, { dest: { id: t1 } }) { - const res = await db.query('holder', h1).include('dest').get().toObject() + const res = await db.query('holder', h1).include('dest.id').get().toObject() deepEqual(res, { id: h1, dest: { id: t1 }, From ffe3f772cba10659cc92b024356a23089f91cb47 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 30 Jan 2026 13:44:04 +0100 Subject: [PATCH 043/449] fix tmpids --- native/modify/modify.zig | 80 +++++++++++++----------- native/selva/references.zig | 6 +- native/utils.zig | 2 +- src/db-server/index.ts | 3 +- src/schema/defs/props/references.ts | 15 +++-- test/modify/references.ts | 96 ++++++++++++++++++++++++----- 6 files changed, 134 insertions(+), 68 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 7301fbc150..8a79bdb652 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -12,7 +12,7 @@ const DbCtx = @import("../db/ctx.zig").DbCtx; const subs = @import("subscription.zig"); pub const subscription = subs.suscription; - +const resItemSize = utils.sizeOf(t.ModifyResultItem); inline fn applyInc(comptime T: type, current: []u8, value: []u8, start: u16, op: t.ModifyIncrement) void { const curr = utils.read(T, current, start); const inc = utils.read(T, value, 0); @@ -24,6 +24,12 @@ inline fn applyInc(comptime T: type, current: []u8, value: []u8, start: u16, op: utils.write(value, res, 0); } +inline fn writeResult(res: *t.ModifyResultItem, id: u32, err: t.ModifyError) void { + const ptr = @as([*]u8, @ptrCast(res)); + utils.write(ptr[0..4], id, 0); + ptr[4] = @intFromEnum(err); +} + // ----------NAPI------------- pub fn modifyThread(env: napi.Env, info: napi.Info) callconv(.c) napi.Value { modifyInternalThread( @@ -40,7 +46,7 @@ fn modifyInternalThread(env: napi.Env, info: napi.Info) !void { try dbCtx.threads.modify(batch); } -pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []t.ModifyResultItem) !void { +pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []u8) !void { var j: usize = 0; while (j < data.len) { const propId = data[j]; @@ -85,10 +91,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u var k: usize = 0; const meta = utils.readNext(t.ModifyReferenceMetaHeader, value, &k); var refId = meta.id; - if (meta.isTmp) { - refId = items[refId].id; - } - + if (meta.isTmp) refId = utils.read(u32, items, refId * resItemSize); if (Node.getNode(refTypeEntry, refId)) |dst| { _ = try References.writeReference(db, node, propSchema, dst); if (meta.size != 0) { @@ -117,9 +120,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u .tmpIds => { const offset = utils.alignLeft(u32, refs); const u32Ids = utils.read([]u32, refs[4 - offset .. refs.len - offset], 0); - for (u32Ids) |*id| { - id.* = items[id.*].id; - } + for (u32Ids) |*id| id.* = utils.read(u32, items, id.* * resItemSize); try References.putReferences(db, node, propSchema, u32Ids); }, .idsWithMeta => { @@ -131,11 +132,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u while (x < refs.len) { const meta = utils.readNext(t.ModifyReferencesMetaHeader, refs, &x); var refId = meta.id; - - if (meta.isTmp) { - refId = items[refId].id; - } - + if (meta.isTmp) refId = utils.read(u32, items, refId * resItemSize); if (Node.getNode(refTypeEntry, refId)) |dst| { const ref = try References.insertReference(db, node, propSchema, dst, meta.index, meta.withIndex); if (meta.size != 0) { @@ -150,6 +147,19 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u x += meta.size; } }, + .delIds => { + const offset = utils.alignLeft(u32, refs); + const u32Ids = utils.read([]u32, refs[4 - offset .. refs.len - offset], 0); + for (u32Ids) |id| try References.deleteReference(db, node, propSchema, id); + }, + .delTmpIds => { + const offset = utils.alignLeft(u32, refs); + const u32Ids = utils.read([]u32, refs[4 - offset .. refs.len - offset], 0); + for (u32Ids) |*id| { + const realId = utils.read(u32, items, id.* * resItemSize); + try References.deleteReference(db, node, propSchema, realId); + } + }, else => {}, } @@ -172,18 +182,13 @@ pub fn modify( db: *DbCtx, ) !void { var i: usize = 0; - var j: u32 = 5 + @alignOf(t.ModifyResultItem); + var j: u32 = 4; const header = utils.readNext(t.ModifyHeader, buf, &i); - const size = j + header.count * 5; - const result = try thread.modify.result(size, header.opId, header.opType); - const alignIndex = thread.modify.index - size + j; - const offset: u8 = @truncate(alignIndex % @alignOf(t.ModifyResultItem)); - result[4] = @alignOf(t.ModifyResultItem) - offset; - j -= offset; - const items = utils.toSlice(t.ModifyResultItem, result[j..]); + const size = header.count * resItemSize; + const result = try thread.modify.result(j + size, header.opId, header.opType); + const items = result[j..]; while (i < buf.len) { const op: t.Modify = @enumFromInt(buf[i]); - // std.debug.print("op: {any}\n", .{op}); switch (op) { .create => { const create = utils.read(t.ModifyCreateHeader, buf, i); @@ -192,57 +197,58 @@ pub fn modify( const data: []u8 = buf[i .. i + create.size]; const id = db.ids[create.type - 1] + 1; const node = try Node.upsertNode(typeEntry, id); - // std.debug.print("create id: {any}\n", .{id}); modifyProps(db, typeEntry, node, data, items) catch { // handle errors }; db.ids[create.type - 1] = id; utils.write(result, id, j); - utils.writeAs(u8, result, t.ModifyError.null, j + 4); + utils.write(result, t.ModifyError.null, j + 4); i += create.size; - j += 5; }, .update => { const update = utils.read(t.ModifyUpdateHeader, buf, i); i += utils.sizeOf(t.ModifyUpdateHeader); const typeEntry = try Node.getType(db, update.type); var id = update.id; - if (update.isTmp) id = items[id].id; - utils.write(result, id, j); + if (update.isTmp) id = utils.read(u32, items, id * resItemSize); if (Node.getNode(typeEntry, id)) |node| { const data: []u8 = buf[i .. i + update.size]; modifyProps(db, typeEntry, node, data, items) catch { // handle errors }; - utils.writeAs(u8, result, t.ModifyError.null, j + 4); + utils.write(result, id, j); + utils.write(result, t.ModifyError.null, j + 4); } else { - utils.writeAs(u8, result, t.ModifyError.nx, j + 4); + utils.write(result, id, j); + utils.write(result, t.ModifyError.nx, j + 4); } + // std.debug.print("- update id: {any} res: {any}\n", .{ id, res }); i += update.size; - j += 5; }, .delete => { const delete = utils.read(t.ModifyDeleteHeader, buf, i); i += utils.sizeOf(t.ModifyDeleteHeader); const typeEntry = try Node.getType(db, delete.type); var id = delete.id; - if (delete.isTmp) id = items[id].id; - utils.write(result, id, j); + if (delete.isTmp) id = utils.read(u32, items, id * resItemSize); if (Node.getNode(typeEntry, id)) |node| { Node.deleteNode(db, typeEntry, node) catch { // handle errors }; - utils.writeAs(u8, result, t.ModifyError.null, j + 4); + utils.write(result, id, j); + utils.write(result, t.ModifyError.null, j + 4); } else { - utils.writeAs(u8, result, t.ModifyError.nx, j + 4); + utils.write(result, id, j); + utils.write(result, t.ModifyError.nx, j + 4); } - j += 5; + + // std.debug.print("- delete id: {any} res: {any}\n", .{ id, res }); }, } + j += resItemSize; } Node.expire(db); utils.write(result, j, 0); - if (j < size) @memset(result[j..size], 0); } diff --git a/native/selva/references.zig b/native/selva/references.zig index 688153531e..3d30115a3b 100644 --- a/native/selva/references.zig +++ b/native/selva/references.zig @@ -20,9 +20,9 @@ pub inline fn getReference(node: Node.Node, fieldSchema: Schema.FieldSchema) ?Re return selva.c.selva_fields_get_reference(node, fieldSchema); } -pub fn deleteReference(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, id: u32) !void { +pub fn deleteReference(db: *DbCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, id: u32) !void { try errors.selva(selva.c.selva_fields_del_ref( - ctx.db.selva, + db.selva, node, fieldSchema, id, @@ -30,7 +30,7 @@ pub fn deleteReference(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Sch const efc = selva.c.selva_get_edge_field_constraint(fieldSchema); const dstType = efc.*.dst_node_type; - selva.markDirty(ctx.db, dstType, id); + selva.markDirty(db, dstType, id); } pub fn referencesHas(refs: References, dstNodeId: u32) bool { diff --git a/native/utils.zig b/native/utils.zig index b8ac28a424..bd82d11bea 100644 --- a/native/utils.zig +++ b/native/utils.zig @@ -83,7 +83,7 @@ pub inline fn writeNext(comptime T: type, buffer: []u8, value: T, offset: *usize } pub inline fn toSlice(comptime T: type, value: []u8) []T { - const x: []T = @as([*]T, @ptrCast(@alignCast(value.ptr)))[0..@divFloor(value.len, @sizeOf(T))]; + const x: []T = @as([*]T, @ptrCast(@alignCast(value.ptr)))[0..@divFloor(value.len, sizeOf(T))]; return x; } diff --git a/src/db-server/index.ts b/src/db-server/index.ts index 50ba811326..937cd65de9 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -194,8 +194,7 @@ export class DbServer extends DbShared { native.modify(payload, this.dbCtxExternal) this.addOpOnceListener(OpType.modify, id, (v) => { const end = readUint32(v, 0) - const alignOffset = v[4] - resolve(v.subarray(5 + alignOffset, end)) + resolve(v.subarray(4, end)) }) }) } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 0d3e5d8af1..c1ff50a79c 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -164,7 +164,6 @@ const setReferences = ( } else if (typeof item === 'object' && item?.id instanceof BasedModify) { throw item.id } else { - console.log('??', item, value) throw 'bad ref!' } } @@ -174,7 +173,7 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { let offset = 0 while (offset < value.length) { const item = value[offset] - if (typeof item === 'number') { + if (getRealId(item)) { const index = pushModifyReferencesHeader(buf, { op: ModifyReferences.delIds, size: 0, @@ -182,19 +181,19 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { const start = buf.length offset = serializeIds(buf, value, offset) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - continue - } - if (typeof item === 'object' && item !== null && item.tmpId) { + } else if (getTmpId(item) !== undefined) { const index = pushModifyReferencesHeader(buf, { - op: ModifyReferences.tmpIds, + op: ModifyReferences.delTmpIds, size: 0, }) const start = buf.length offset = serializeTmpIds(buf, value, offset) writeModifyReferencesHeaderProps.size(buf.data, buf.length - start, index) - continue + } else if (item instanceof BasedModify) { + throw item + } else { + throw 'bad ref' } - throw 'bad ref' } } diff --git a/test/modify/references.ts b/test/modify/references.ts index e48fcd4d2f..cd7e2af6a0 100644 --- a/test/modify/references.ts +++ b/test/modify/references.ts @@ -14,35 +14,41 @@ await test('modify single reference', async (t) => { }, }) - const t1 = await db.create('thing', { name: 't1' }) - const t2 = await db.create('thing', { name: 't2' }) + // Test passing BasedModify (promise) directly + const t1 = db.create('thing', { name: 't1' }) + const t2 = db.create('thing', { name: 't2' }) const h1 = await db.create('holder', { dest: t1 }) + const realT1 = await t1 + const realT2 = await t2 + { const res = await db.query('holder', h1).include('dest.id').get().toObject() deepEqual(res, { id: h1, - dest: { id: t1 }, + dest: { id: realT1 }, }) } + // Update with promise await db.update('holder', h1, { dest: t2 }) { const res = await db.query('holder', h1).include('dest.id').get().toObject() deepEqual(res, { id: h1, - dest: { id: t2 }, + dest: { id: realT2 }, }) } + // Update with object format containing promise await db.update('holder', h1, { dest: { id: t1 } }) { const res = await db.query('holder', h1).include('dest.id').get().toObject() deepEqual(res, { id: h1, - dest: { id: t1 }, + dest: { id: realT1 }, }) } }) @@ -65,12 +71,15 @@ await test('modify references', async (t) => { }, }) + // Mixed awaited and not awaited const t1 = await db.create('thing', { name: 't1' }) - const t2 = await db.create('thing', { name: 't2' }) - const t3 = await db.create('thing', { name: 't3' }) + const t2Promise = db.create('thing', { name: 't2' }) + const t2 = await t2Promise + const t3Promise = db.create('thing', { name: 't3' }) + const t3 = await t3Promise - // Test set (create) - const h1 = await db.create('holder', { dests: [t1, t2] }) + // Test set (create) with mixed + const h1 = await db.create('holder', { dests: [t1, t2Promise] }) const check = async (ids: number[], msg) => { const res = await db.query('holder', h1).include('dests').get().toObject() @@ -82,19 +91,72 @@ await test('modify references', async (t) => { await check([t1, t2], 'simple') - // Test add - await db.update('holder', h1, { dests: { add: [t3] } }) + // Test add with promise + await db.update('holder', h1, { dests: { add: [t3Promise] } }) await check([t1, t2, t3], 'add') - // Test delete - await db.update('holder', h1, { dests: { delete: [t2] } }) + // Test delete with promise + await db.update('holder', h1, { dests: { delete: [t2Promise] } }) await check([t1, t3], 'delete') - // Test replace (array) - await db.update('holder', h1, { dests: [t2] }) + // Test replace (array) with promise + await db.update('holder', h1, { dests: [t2Promise] }) await check([t2], 'replace') - // Test update (acts as add/upsert for references list) - await db.update('holder', h1, { dests: { update: [t3] } }) + // Test update (acts as add/upsert for references list) with promise + await db.update('holder', h1, { dests: { update: [t3Promise] } }) await check([t2, t3], 'update') }) + +await test('modify references no await', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + holder: { + dests: { + type: 'references', + items: { + ref: 'thing', + prop: 'refsHolders', + }, + }, + }, + }, + }) + + // No await on creates + const t1 = db.create('thing', { name: 't1' }) + const t2 = db.create('thing', { name: 't2' }) + const t3 = db.create('thing', { name: 't3' }) + + // Use unawaited t1, t2 in create + const h1 = db.create('holder', { dests: [t1, t2] }) + + // Use unawaited t3 in update, on unawaited h1 + const updateP = db.update('holder', h1, { dests: { add: [t3] } }) + + // Also delete t2 (unawaited) from unawaited h1 + const deleteP = db.update('holder', h1, { dests: { delete: [t2] } }) + + // Now we wait for the final state to settle. + await Promise.all([t1, t2, t3, h1, updateP, deleteP]) + // Get real IDs for assertion + const id1 = await t1 + const id3 = await t3 // t2 was deleted + + // Verify + const res = await db + .query('holder', await h1) + .include('dests.id') + .get() + .toObject() + + const currentIds = res.dests?.map((v: any) => v.id) || [] + currentIds.sort() + const expected = [id1, id3] + expected.sort() + + deepEqual(currentIds, expected, 'no await sequence') +}) From 150ff2d6e65b5cf347fec32865ccde35eff6051a Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 30 Jan 2026 15:24:34 +0100 Subject: [PATCH 044/449] add prop.schema and type.schema --- native/modify/modify.zig | 2 + native/query/include/include.zig | 3 +- src/db-server/schema.ts | 24 +++- src/protocol/db-read/read.ts | 1 - src/schema/defs/getTypeDefs.ts | 13 +- src/schema/defs/index.ts | 7 +- src/schema/defs/props/base.ts | 6 +- src/schema/defs/props/fixed.ts | 16 +-- src/schema/defs/props/separate.ts | 4 +- test/modify/boolean.ts | 110 ++++++++++++++++ test/modify/numbers.ts | 200 ++++++++++++++++++++++++++++++ test/modify/references.ts | 136 ++++++++++++++++++++ test/modify/string.ts | 110 ++++++++++++++++ test/modify/timestamp.ts | 119 ++++++++++++++++++ test/query-ast/include.ts | 2 +- 15 files changed, 726 insertions(+), 27 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 8a79bdb652..5550f2530b 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -57,6 +57,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); const size = main.type.size(); const value = data[j .. j + size]; + std.debug.print("main: size {any} value {any} current {any}\n", .{ size, value, current }); if (main.increment != .none) { switch (main.type) { .number => applyInc(f64, current, value, main.start, main.increment), @@ -94,6 +95,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u if (meta.isTmp) refId = utils.read(u32, items, refId * resItemSize); if (Node.getNode(refTypeEntry, refId)) |dst| { _ = try References.writeReference(db, node, propSchema, dst); + std.debug.print("meta.size {any}\n", .{meta.size}); if (meta.size != 0) { const edgeProps = value[k .. k + meta.size]; const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); diff --git a/native/query/include/include.zig b/native/query/include/include.zig index 75e1fed15b..1d00118621 100644 --- a/native/query/include/include.zig +++ b/native/query/include/include.zig @@ -119,6 +119,7 @@ pub fn include( .default => { const header = utils.readNext(t.IncludeHeader, q, &i); const value = try get(typeEntry, node, &header); + std.debug.print("??? value {any} - {any}\n", .{ value, header }); switch (header.propType) { t.PropType.text, => { @@ -132,8 +133,6 @@ pub fn include( try append.stripCrc32(ctx.thread, header.prop, value); }, t.PropType.microBuffer, t.PropType.vector, t.PropType.colVec => { - std.debug.print("flap flap {any} \n", .{value}); - // Fixed size try ctx.thread.query.append(header.prop); try ctx.thread.query.append(value); diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index ebcbc74757..3f0ed7148a 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -4,9 +4,11 @@ import { writeFile } from 'node:fs/promises' import native, { idGenerator } from '../native.js' import { schemaToSelvaBuffer } from './schemaSelvaBuffer.js' import { readUint32, writeUint32 } from '../utils/index.js' -import { OpType } from '../zigTsExports.js' +import { OpType, pushSelvaSchemaHeader } from '../zigTsExports.js' import { serialize, updateTypeDefs, type SchemaOut } from '../schema/index.js' import { SCHEMA_FILE } from '../index.js' +import { getTypeDefs } from '../schema/defs/getTypeDefs.js' +import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' const schemaOpId = idGenerator() @@ -103,7 +105,8 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { const types = Object.keys(server.schemaTypesParsed) const s = schemaToSelvaBuffer(server.schemaTypesParsed) let maxTid = 0 - + // console.log('-----', schemaToSelvaBufferNew(server.schema!)) + // process.exit() await Promise.all( s.map(async (buf, i) => { const type = server.schemaTypesParsed[types[i]] @@ -124,3 +127,20 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { server.save({ skipDirtyCheck: true }).catch(console.error) } } + +// function schemaToSelvaBufferNew(schema: SchemaOut): Uint8Array[] { +// const typeDefs = getTypeDefs(schema) +// const buf = new AutoSizedUint8Array(4, 65536) +// for (const [, typeDef] of typeDefs) { +// for (const propDef of typeDef.main) { +// } +// } + +// pushSelvaSchemaHeader(buf, { +// blockCapacity: t.blockCapacity, +// nrFields: 1 + typeDef.separate.length, +// nrFixedFields, +// nrVirtualFields: virtualFields, +// sdbVersion: 8, +// }) +// } diff --git a/src/protocol/db-read/read.ts b/src/protocol/db-read/read.ts index 9f6458c338..18b0b07d3e 100644 --- a/src/protocol/db-read/read.ts +++ b/src/protocol/db-read/read.ts @@ -131,7 +131,6 @@ const readInstruction = ( } else if (instruction === ReadOp.edge) { return edge(q, result, i, item) } else if (instruction === 0) { - console.log('DERP', i) return readMain(q, result, i, item) } else { return readProp(instruction, q, result, i, item) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index ffbe3fb113..c2eafd4781 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -20,15 +20,15 @@ const propIndexOffset = (prop: PropDef) => { case PropType.microBuffer: case PropType.vector: // microbuffers first - return 'default' in prop.prop ? -600 : 0 + return 'default' in prop.schema ? -600 : 0 case PropType.string: case PropType.binary: case PropType.json: // then strings - return 'default' in prop.prop ? -500 : 0 + return 'default' in prop.schema ? -500 : 0 // then text case PropType.text: - return 'default' in prop.prop ? -400 : 0 + return 'default' in prop.schema ? -400 : 0 // References go behind the defaults case PropType.references: case PropType.reference: @@ -45,13 +45,15 @@ const propIndexOffset = (prop: PropDef) => { const separateSorter = (a, b) => propIndexOffset(a) - propIndexOffset(b) -const getTypeDef = ({ props }: SchemaType): TypeDef => { +const getTypeDef = (schema: SchemaType): TypeDef => { + const { props } = schema const typeDef: TypeDef = { id: 0, separate: [], props: new Map(), main: [], tree: new Map(), + schema, } const walk = ( @@ -123,7 +125,8 @@ export const getTypeDefs = (schema: SchemaOut): Map => { for (const [typeName, typeDef] of typeDefs) { typeDef.id = typeId++ for (const [propPath, def] of typeDef.props) { - const prop = def.prop.type === 'references' ? def.prop.items : def.prop + const prop = + def.schema.type === 'references' ? def.schema.items : def.schema if (prop.type !== 'reference') continue def.ref = typeDefs.get(prop.ref)! if (!prop.prop) { diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 3a455dd119..8e76b0a17d 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -1,4 +1,4 @@ -import type { SchemaProp } from '../../schema.js' +import type { SchemaProp, SchemaType } from '../../schema.js' import type { LangCodeEnum, ModifyEnum, @@ -17,6 +17,7 @@ export type TypeDef = { separate: PropDef[] props: Map tree: PropTree + schema: SchemaType } export type PropDef = { @@ -25,7 +26,7 @@ export type PropDef = { start: number path: string[] size: number - prop: SchemaProp + schema: SchemaProp edges?: TypeDef ref?: TypeDef refProp?: PropDef @@ -44,7 +45,7 @@ export const isPropDef = (p: any): p is PropDef => { } export type PropDefClass = { - new (prop: SchemaProp, path: string[], typeDef: TypeDef): PropDef + new (schema: SchemaProp, path: string[], typeDef: TypeDef): PropDef } export const defs: Record< diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index f5605140e4..5e3971f80a 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -8,8 +8,8 @@ import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { PropDef, TypeDef } from '../index.js' export class BasePropDef implements PropDef { - constructor(prop: SchemaProp, path: string[], typeDef: TypeDef) { - this.prop = prop + constructor(schema: SchemaProp, path: string[], typeDef: TypeDef) { + this.schema = schema this.path = path this.typeDef = typeDef } @@ -17,7 +17,7 @@ export class BasePropDef implements PropDef { start = 0 size = 0 type: PropTypeEnum = 0 as PropTypeEnum - prop: SchemaProp + schema: SchemaProp path: string[] isEdge: boolean = false typeDef: TypeDef diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index f3d43efbe4..abc587a63f 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -15,7 +15,7 @@ export const number = class Number extends BasePropDef { if (typeof value !== 'number') { throw new Error('Invalid type for number ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -41,7 +41,7 @@ export const timestamp = class Timestamp extends number { value: unknown, ): asserts value is number | string { const ts = convertToTimestamp(value as any) - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && ts < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -73,7 +73,7 @@ export const uint8 = class Uint8 extends BasePropDef { if (value < 0 || value > 255) { throw new Error('Value out of range for uint8 ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -104,7 +104,7 @@ export const int8 = class Int8 extends uint8 { if (value < -128 || value > 127) { throw new Error('Value out of range for int8 ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -136,7 +136,7 @@ export const uint16 = class Uint16 extends BasePropDef { if (value < 0 || value > 65535) { throw new Error('Value out of range for uint16 ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -167,7 +167,7 @@ export const int16 = class Int16 extends uint16 { if (value < -32768 || value > 32767) { throw new Error('Value out of range for int16 ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -199,7 +199,7 @@ export const uint32 = class Uint32 extends BasePropDef { if (value < 0 || value > 4294967295) { throw new Error('Value out of range for uint32 ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( @@ -230,7 +230,7 @@ export const int32 = class Int32 extends uint32 { if (value < -2147483648 || value > 2147483647) { throw new Error('Value out of range for int32 ' + this.path.join('.')) } - const prop = this.prop as any + const prop = this.schema as any if (prop.min !== undefined && value < prop.min) { throw new Error( `Value ${value} is smaller than min ${prop.min} for ${this.path.join( diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 3324efafa9..617071b5f4 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -39,7 +39,7 @@ export const string = class String extends BasePropDef { if (typeof val !== 'string') { throw new Error('Invalid type for string ' + this.path.join('.')) } - const prop = this.prop as SchemaString + const prop = this.schema as SchemaString if (prop.min !== undefined && val.length < prop.min) { throw new Error( `Length ${val.length} is smaller than min ${prop.min} for ${this.path.join( @@ -104,7 +104,7 @@ export const binary = class Binary extends BasePropDef { if (!(value instanceof Uint8Array)) { throw new Error('Invalid type for binary ' + this.path.join('.')) } - const prop = this.prop as SchemaString + const prop = this.schema as SchemaString if (prop.maxBytes !== undefined && value.byteLength > prop.maxBytes) { throw new Error( `Byte length ${value.byteLength} is larger than maxBytes ${ diff --git a/test/modify/boolean.ts b/test/modify/boolean.ts index 4597abcd4c..2dd358effb 100644 --- a/test/modify/boolean.ts +++ b/test/modify/boolean.ts @@ -41,3 +41,113 @@ await test('modify boolean', async (t) => { { id: 3, isNice: false }, ]) }) + +await test('modify boolean on edge', async (t) => { + const db = await testDb(t, { + types: { + user: { + isNice: 'boolean', + }, + holder: { + toUser: { + ref: 'user', + prop: 'holders', + $edgeBool: 'boolean', + }, + }, + }, + }) + + const u1 = await db.create('user', { isNice: true }) + + const a = db.create('holder', { + toUser: { + id: u1, + }, + }) + const b = db.create('holder', { + toUser: { + id: u1, + $edgeBool: true, + }, + }) + const c = db.create('holder', { + toUser: { + id: u1, + $edgeBool: false, + }, + }) + + // Basic creates + // Check a (default false?) + const resA = await db + .query('holder', await a) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resA.toUser?.$edgeBool, false) + + // Check b (true) + const resB = await db + .query('holder', await b) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resB.toUser?.$edgeBool, true) + + // Check c (false) + const resC = await db + .query('holder', await c) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resC.toUser?.$edgeBool, false) + + // Updates to true + db.update('holder', await a, { toUser: { id: u1, $edgeBool: true } }) + db.update('holder', await b, { toUser: { id: u1, $edgeBool: true } }) + db.update('holder', await c, { toUser: { id: u1, $edgeBool: true } }) + + const resA2 = await db + .query('holder', await a) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resA2.toUser?.$edgeBool, true) + const resB2 = await db + .query('holder', await b) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resB2.toUser?.$edgeBool, true) + const resC2 = await db + .query('holder', await c) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resC2.toUser?.$edgeBool, true) + + // Updates to false + db.update('holder', await a, { toUser: { id: u1, $edgeBool: false } }) + db.update('holder', await b, { toUser: { id: u1, $edgeBool: false } }) + db.update('holder', await c, { toUser: { id: u1, $edgeBool: false } }) + + const resA3 = await db + .query('holder', await a) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resA3.toUser?.$edgeBool, false) + const resB3 = await db + .query('holder', await b) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resB3.toUser?.$edgeBool, false) + const resC3 = await db + .query('holder', await c) + .include('toUser.$edgeBool') + .get() + .toObject() + deepEqual(resC3.toUser?.$edgeBool, false) +}) diff --git a/test/modify/numbers.ts b/test/modify/numbers.ts index d8e9acf2c3..4cc8589c42 100644 --- a/test/modify/numbers.ts +++ b/test/modify/numbers.ts @@ -145,3 +145,203 @@ await test('modify numbers', async (t) => { i32: -100001, }) }) + +await test('modify numbers on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + n: 'number', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeN: 'number', + $edgeU8: 'uint8', + $edgeI8: 'int8', + $edgeU16: 'uint16', + $edgeI16: 'int16', + $edgeU32: 'uint32', + $edgeI32: 'int32', + }, + }, + }, + }) + + const targetId = await db.create('thing', { n: 1 }) + + // 1. Initial values + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeN: 1.5, + $edgeU8: 10, + $edgeI8: -10, + $edgeU16: 1000, + $edgeI16: -1000, + $edgeU32: 100000, + $edgeI32: -100000, + }, + }) + + // Border values + const id2 = await db.create('holder', { + toThing: { + id: targetId, + $edgeN: 100.5, + $edgeU8: 255, + $edgeI8: 127, + $edgeU16: 65535, + $edgeI16: 32767, + $edgeU32: 4294967295, + $edgeI32: 2147483647, + }, + }) + + // Min values + const id3 = await db.create('holder', { + toThing: { + id: targetId, + $edgeN: -100.5, + $edgeU8: 0, + $edgeI8: -128, + $edgeU16: 0, + $edgeI16: -32768, + $edgeU32: 0, + $edgeI32: -2147483648, + }, + }) + + // Helper to get edge props + const getEdgeProps = async (id: number) => { + const res = await db + .query('holder', id) + .include( + 'toThing.$edgeN', + 'toThing.$edgeU8', + 'toThing.$edgeI8', + 'toThing.$edgeU16', + 'toThing.$edgeI16', + 'toThing.$edgeU32', + 'toThing.$edgeI32', + ) + .get() + .toObject() + + if (!res.toThing || Array.isArray(res.toThing)) { + return {} + } + + return { + edgeN: res.toThing.$edgeN, + edgeU8: res.toThing.$edgeU8, + edgeI8: res.toThing.$edgeI8, + edgeU16: res.toThing.$edgeU16, + edgeI16: res.toThing.$edgeI16, + edgeU32: res.toThing.$edgeU32, + edgeI32: res.toThing.$edgeI32, + } + } + + deepEqual(await getEdgeProps(id1), { + edgeN: 1.5, + edgeU8: 10, + edgeI8: -10, + edgeU16: 1000, + edgeI16: -1000, + edgeU32: 100000, + edgeI32: -100000, + }) + + deepEqual(await getEdgeProps(id2), { + edgeN: 100.5, + edgeU8: 255, + edgeI8: 127, + edgeU16: 65535, + edgeI16: 32767, + edgeU32: 4294967295, + edgeI32: 2147483647, + }) + + deepEqual(await getEdgeProps(id3), { + edgeN: -100.5, + edgeU8: 0, + edgeI8: -128, + edgeU16: 0, + edgeI16: -32768, + edgeU32: 0, + edgeI32: -2147483648, + }) + + // Update + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeN: 2.5, + $edgeU8: 11, + $edgeI8: -11, + $edgeU16: 1001, + $edgeI16: -1001, + $edgeU32: 100001, + $edgeI32: -100001, + }, + }) + + deepEqual(await getEdgeProps(id1), { + edgeN: 2.5, + edgeU8: 11, + edgeI8: -11, + edgeU16: 1001, + edgeI16: -1001, + edgeU32: 100001, + edgeI32: -100001, + }) + + // Increment + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeN: { increment: 2.5 }, + $edgeU8: { increment: 1 }, + $edgeI8: { increment: 1 }, + $edgeU16: { increment: 1 }, + $edgeI16: { increment: 1 }, + $edgeU32: { increment: 1 }, + $edgeI32: { increment: 1 }, + }, + }) + + deepEqual(await getEdgeProps(id1), { + edgeN: 5, + edgeU8: 12, + edgeI8: -10, + edgeU16: 1002, + edgeI16: -1000, + edgeU32: 100002, + edgeI32: -100000, + }) + + // Decrement + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeN: { increment: -2.5 }, + $edgeU8: { increment: -1 }, + $edgeI8: { increment: -1 }, + $edgeU16: { increment: -1 }, + $edgeI16: { increment: -1 }, + $edgeU32: { increment: -1 }, + $edgeI32: { increment: -1 }, + }, + }) + + deepEqual(await getEdgeProps(id1), { + edgeN: 2.5, + edgeU8: 11, + edgeI8: -11, + edgeU16: 1001, + edgeI16: -1001, + edgeU32: 100001, + edgeI32: -100001, + }) +}) diff --git a/test/modify/references.ts b/test/modify/references.ts index cd7e2af6a0..c34e7eb99a 100644 --- a/test/modify/references.ts +++ b/test/modify/references.ts @@ -160,3 +160,139 @@ await test('modify references no await', async (t) => { deepEqual(currentIds, expected, 'no await sequence') }) + +await test('modify single reference on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeRef: { + type: 'reference', + ref: 'thing', + prop: 'edgeRefHolders', + }, + }, + }, + }, + }) + + const t1 = await db.create('thing', { name: 't1' }) + const t2 = await db.create('thing', { name: 't2' }) + const target = await db.create('thing', { name: 'target' }) + + const h1 = await db.create('holder', { + toThing: { + id: target, + $edgeRef: t1, + }, + }) + + // Verify + const getEdgeRef = async (id: number) => { + const res = await db + .query('holder', id) + .include('toThing.$edgeRef.id') + .get() + .toObject() + return res.toThing && !Array.isArray(res.toThing) + ? res.toThing.$edgeRef + : undefined + } + + deepEqual((await getEdgeRef(h1))?.id, t1) + + // Update + await db.update('holder', h1, { + toThing: { + id: target, + $edgeRef: t2, + }, + }) + deepEqual((await getEdgeRef(h1))?.id, t2) + + // Update with object format + await db.update('holder', h1, { + toThing: { + id: target, + $edgeRef: { id: t1 }, + }, + }) + deepEqual((await getEdgeRef(h1))?.id, t1) +}) + +await test('modify references on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeRefs: { + type: 'references', + items: { + ref: 'thing', + prop: 'edgeRefsHolders', + }, + }, + }, + }, + }, + }) + + // Mixed awaited and not awaited + const t1 = await db.create('thing', { name: 't1' }) + const t2Promise = db.create('thing', { name: 't2' }) + const t2 = await t2Promise + const t3Promise = db.create('thing', { name: 't3' }) + const t3 = await t3Promise + const target = await db.create('thing', { name: 'target' }) + + const h1 = await db.create('holder', { + toThing: { + id: target, + $edgeRefs: [t1, t2Promise], + }, + }) + + const check = async (ids: number[], msg) => { + const res = await db + .query('holder', h1) + .include('toThing.$edgeRefs.id') + .get() + .toObject() + + const edge = res.toThing && !Array.isArray(res.toThing) ? res.toThing : {} + const currentIds = edge.$edgeRefs?.map((v: any) => v.id) || [] + currentIds.sort() + ids.sort() + deepEqual(currentIds, ids, msg) + } + + await check([t1, t2], 'simple') + + // Test add with promise + await db.update('holder', h1, { + toThing: { id: target, $edgeRefs: { add: [t3Promise] } }, + }) + await check([t1, t2, t3], 'add') + + // Test delete with promise + await db.update('holder', h1, { + toThing: { id: target, $edgeRefs: { delete: [t2Promise] } }, + }) + await check([t1, t3], 'delete') + + // Test replace (array) with promise + await db.update('holder', h1, { + toThing: { id: target, $edgeRefs: [t2Promise] }, + }) + await check([t2], 'replace') +}) diff --git a/test/modify/string.ts b/test/modify/string.ts index 00380b3657..a5f9a18cc2 100644 --- a/test/modify/string.ts +++ b/test/modify/string.ts @@ -71,3 +71,113 @@ await test('modify string', async (t) => { name: s6, }) }) + +await test('modify string on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeName: 'string', + }, + }, + }, + }) + + // Basic string + const s1 = 'hello' + const targetId = await db.create('thing', { name: 'target' }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeName: s1, + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeName') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeName, s1) + + // Update to another string + const s2 = 'world' + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeName: s2, + }, + }) + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeName') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeName, s2) + + // String with spaces + const s3 = 'foo bar' + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeName: s3, + }, + }) + const res3 = await db + .query('holder', id1) + .include('toThing.$edgeName') + .get() + .toObject() + deepEqual(res3.toThing?.$edgeName, s3) + + // Empty string + const s4 = '' + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeName: s4, + }, + }) + const res4 = await db + .query('holder', id1) + .include('toThing.$edgeName') + .get() + .toObject() + deepEqual(res4.toThing?.$edgeName, s4) + + // Unicode / Special characters + const s5 = 'ñàéïô SPECIAL !@#$%^&*()_+ 123' + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeName: s5, + }, + }) + const res5 = await db + .query('holder', id1) + .include('toThing.$edgeName') + .get() + .toObject() + deepEqual(res5.toThing?.$edgeName, s5.normalize('NFD')) + + // Long string + const s6 = 'a'.repeat(1000) + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeName: s6, + }, + }) + const res6 = await db + .query('holder', id1) + .include('toThing.$edgeName') + .get() + .toObject() + deepEqual(res6.toThing?.$edgeName, s6) +}) diff --git a/test/modify/timestamp.ts b/test/modify/timestamp.ts index 254f8e2cfe..fdff97ea70 100644 --- a/test/modify/timestamp.ts +++ b/test/modify/timestamp.ts @@ -95,3 +95,122 @@ await test('modify timestamp', async (t) => { const r4: any = await db.query('event', id1).get() deepEqual(r4, { id: id1, ts: dateTs }) }) + +await test('modify timestamp on edge', async (t) => { + const db = await testDb(t, { + types: { + event: { + ts: 'timestamp', + }, + holder: { + toEvent: { + ref: 'event', + prop: 'holders', + $edgeTs: 'timestamp', + }, + }, + }, + }) + + const eventId = await db.create('event', { ts: Date.now() }) + + const t1 = Date.now() + const t2 = t1 + 1000 + const t3 = t1 + 2000 + + const id1 = await db.create('holder', { + toEvent: { + id: eventId, + $edgeTs: t1, + }, + }) + + // Helper + const getEdgeTs = async (id: number) => { + const res = await db + .query('holder', id) + .include('toEvent.$edgeTs') + .get() + .toObject() + return res.toEvent?.$edgeTs || 0 + } + + deepEqual(await getEdgeTs(id1), t1) + + // Update + await db.update('holder', id1, { + toEvent: { + id: eventId, + $edgeTs: t2, + }, + }) + deepEqual(await getEdgeTs(id1), t2) + + await db.update('holder', id1, { + toEvent: { + id: eventId, + $edgeTs: t3, + }, + }) + deepEqual(await getEdgeTs(id1), t3) + + // Edge cases + await db.update('holder', id1, { toEvent: { id: eventId, $edgeTs: 0 } }) + deepEqual(await getEdgeTs(id1), 0) + + const farFuture = 8640000000000000 + await db.update('holder', id1, { + toEvent: { id: eventId, $edgeTs: farFuture }, + }) + deepEqual(await getEdgeTs(id1), farFuture) + + // Increment + await db.update('holder', id1, { toEvent: { id: eventId, $edgeTs: 1000 } }) + await db.update('holder', id1, { + toEvent: { + id: eventId, + $edgeTs: { increment: 1000 }, + }, + }) + deepEqual(await getEdgeTs(id1), 2000) + + await db.update('holder', id1, { + toEvent: { + id: eventId, + $edgeTs: { increment: -500 }, + }, + }) + deepEqual(await getEdgeTs(id1), 1500) + + // String formats + const now = Date.now() + await db.update('holder', id1, { toEvent: { id: eventId, $edgeTs: 'now' } }) + const r1 = await getEdgeTs(id1) + if (Math.abs(r1 - now) > 200) { + throw new Error(`Timestamp 'now' is too far off: ${r1} vs ${now}`) + } + + await db.update('holder', id1, { + toEvent: { id: eventId, $edgeTs: 'now + 1h' }, + }) + const r2 = await getEdgeTs(id1) + const t2Expr = now + 1000 * 60 * 60 + if (Math.abs(r2 - t2Expr) > 200) { + throw new Error(`Timestamp 'now + 1h' is too far off: ${r2} vs ${t2Expr}`) + } + + await db.update('holder', id1, { + toEvent: { id: eventId, $edgeTs: 'now - 1d' }, + }) + const r3 = await getEdgeTs(id1) + const t3Expr = now - 1000 * 60 * 60 * 24 + if (Math.abs(r3 - t3Expr) > 200) { + throw new Error(`Timestamp 'now - 1d' is too far off: ${r3} vs ${t3Expr}`) + } + + // Explicit date string + const dateStr = '2025-01-01T00:00:00.000Z' + const dateTs = new Date(dateStr).valueOf() + await db.update('holder', id1, { toEvent: { id: eventId, $edgeTs: dateStr } }) + deepEqual(await getEdgeTs(id1), dateTs) +}) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index cff939d126..2c8ffd5565 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -37,7 +37,7 @@ await test('include', async (t) => { items: { ref: 'user', prop: 'friends', - $level: 'uint32', + $level: 'number', }, }, }, From c756afabda0e1004751bfddcdbb420125e3455f8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 30 Jan 2026 15:23:33 +0100 Subject: [PATCH 045/449] Remove SELVA_FIELD_TYPE_ALIASES field type Allow only one alias per (node, field) tuple. Ref FDN-1853 --- clibs/include/selva/db.h | 7 +--- clibs/include/selva/types.h | 2 +- clibs/lib/selva/alias.c | 77 ++++-------------------------------- clibs/lib/selva/db.c | 10 ++--- clibs/lib/selva/fields.c | 6 +-- clibs/lib/selva/include/db.h | 11 ++---- clibs/lib/selva/include/io.h | 1 + clibs/lib/selva/io/dump.c | 49 +++++++---------------- clibs/lib/selva/schema.c | 17 -------- clibs/lib/selva/types.c | 5 +-- 10 files changed, 35 insertions(+), 150 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 38bd497f7b..612d72b94e 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -561,16 +561,11 @@ struct SelvaNodeRes selva_get_alias(struct SelvaTypeEntry *type, struct SelvaAli /** * Get alias by destination id. - * This may not seem very useful but this is actually the way that allows you to - * traverse all aliases to the given node_id by following the `next` pointer or - * by calling selva_get_next_alias(). */ SELVA_EXPORT const struct SelvaAlias *selva_get_alias_by_dest(struct SelvaAliases *aliases, node_id_t dest); -SELVA_EXPORT -const struct SelvaAlias *selva_get_next_alias(const struct SelvaAlias *alias); - +/* TODO Is this needed as a separate func? */ SELVA_EXPORT const char *selva_get_alias_name(const struct SelvaAlias *alias, size_t *len) __attribute__((nonnull, pure)); diff --git a/clibs/include/selva/types.h b/clibs/include/selva/types.h index 5b5e25e895..e5a5a7c703 100644 --- a/clibs/include/selva/types.h +++ b/clibs/include/selva/types.h @@ -41,7 +41,7 @@ enum SelvaFieldType { SELVA_FIELD_TYPE_WEAK_REFERENCE __attribute__((deprecated)) = 6, SELVA_FIELD_TYPE_WEAK_REFERENCES __attribute__((deprecated)) = 7, SELVA_FIELD_TYPE_ALIAS = 8, - SELVA_FIELD_TYPE_ALIASES = 9, + SELVA_FIELD_TYPE_ALIASES __attribute__((deprecated)) = 9, SELVA_FIELD_TYPE_COLVEC = 10, } __packed; diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index e6cb369613..46f361b96f 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 SAULX + * Copyright (c) 2024-2026 SAULX * SPDX-License-Identifier: MIT */ #include @@ -18,21 +18,14 @@ void selva_init_aliases(struct SelvaTypeEntry *type) for (size_t i = 0; i < nr_fields; i++) { const struct SelvaFieldSchema *fs = &fields_schema->field_schemas[i]; - struct SelvaAliases *field_aliases = &type->aliases[fs->alias_index]; - - switch (fs->type) { - case SELVA_FIELD_TYPE_ALIAS: - field_aliases->single = true; - __attribute__((__fallthrough__)); - case SELVA_FIELD_TYPE_ALIASES: -#if 0 + + if (fs->type == SELVA_FIELD_TYPE_ALIAS) { + struct SelvaAliases *field_aliases = &type->aliases[fs->alias_index]; + assert(fs->alias_index < type->ns.nr_aliases); -#endif field_aliases->field = fs->field; RB_INIT(&field_aliases->alias_by_name); RB_INIT(&field_aliases->alias_by_dest); - __attribute__((__fallthrough__)); - default: } } } @@ -40,6 +33,7 @@ void selva_init_aliases(struct SelvaTypeEntry *type) void selva_destroy_aliases(struct SelvaTypeEntry *type) { /* We assume that all the aliases in the aliases structs have been freed already. */ + assert(type->ns.nr_aliases == 0); selva_free(type->aliases); type->ns.nr_aliases = 0; type->aliases = nullptr; @@ -66,36 +60,13 @@ static void remove_alias_by_name(struct SelvaAliases *aliases, struct SelvaAlias { struct SelvaAlias *removed = RB_REMOVE(SelvaAliasesByName, &aliases->alias_by_name, alias); assert(removed); + aliases->nr_aliases--; } static void del_alias(struct SelvaAliases *aliases, struct SelvaAlias *alias) { remove_alias_by_name(aliases, alias); - - if (alias->prev) { - /* - * `alias` is in the middle or the last in the chain for this dest. - */ - alias->prev->next = alias->next; - } else { - /* - * `alias` must be the first in alias_by_dest with this destination. - * We must make the `next` the first. - */ - remove_alias_by_dest(aliases, alias); - if (alias->next) { - (void)RB_INSERT(SelvaAliasesByDest, &aliases->alias_by_dest, alias->next); - } - } - if (alias->next) { - /* - * This either sets a new `prev` or nulls it if `alias` was the first. - */ - alias->next->prev = alias->prev; - } - selva_free(alias); - aliases->nr_aliases--; } size_t selva_alias_count(const struct SelvaAliases *aliases) @@ -108,9 +79,6 @@ node_id_t selva_set_alias_p(struct SelvaAliases *aliases, struct SelvaAlias *new struct SelvaAlias *old_alias; node_id_t old_dest = 0; - new_alias->prev = nullptr; - new_alias->next = nullptr; - retry: old_alias = insert_alias_by_name(aliases, new_alias); if (old_alias) { @@ -121,15 +89,7 @@ node_id_t selva_set_alias_p(struct SelvaAliases *aliases, struct SelvaAlias *new struct SelvaAlias *prev_by_dest = RB_INSERT(SelvaAliasesByDest, &aliases->alias_by_dest, new_alias); if (prev_by_dest) { - new_alias->prev = prev_by_dest; - new_alias->next = prev_by_dest->next; - prev_by_dest->next = new_alias; - if (aliases->single) { - /* - * Restrict this field to a single alias, i.e. this is SELVA_FIELD_TYPE_ALIAS. - */ - del_alias(aliases, prev_by_dest); - } + del_alias(aliases, prev_by_dest); } return old_dest; @@ -177,27 +137,11 @@ void selva_del_alias_by_dest(struct SelvaAliases *aliases, node_id_t dest) } remove_alias_by_dest(aliases, alias); - assert(!alias->prev); /* This must be the first one on the list of by_dest aliases. */ /* * Remove this alias from by_name. */ remove_alias_by_name(aliases, alias); - - /* - * Remove the rest of aliases by this dest from by_name. - */ - struct SelvaAlias *next = alias->next; - while (next) { - struct SelvaAlias *tmp = next->next; - - assert(next->dest == alias->dest); - remove_alias_by_name(aliases, next); - selva_free(next); - - next = tmp; - } - selva_free(alias); } @@ -236,11 +180,6 @@ const struct SelvaAlias *selva_get_alias_by_dest(struct SelvaAliases *aliases, n return RB_FIND(SelvaAliasesByDest, &aliases->alias_by_dest, &find); } -const struct SelvaAlias *selva_get_next_alias(const struct SelvaAlias *alias) -{ - return (alias) ? alias->next : nullptr; -} - const char *selva_get_alias_name(const struct SelvaAlias *alias, size_t *len) { *len = alias->name_len; diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 2c49c7c163..e6d12a94db 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -70,8 +70,8 @@ static int SelvaTypeEntry_cmp(const struct SelvaTypeEntry *a, const struct Selva RB_GENERATE(SelvaTypeEntryIndex, SelvaTypeEntry, _entry, SelvaTypeEntry_cmp) RB_GENERATE(SelvaNodeIndex, SelvaNode, _index_entry, SelvaNode_cmp) -RB_GENERATE(SelvaAliasesByName, SelvaAlias, _entry1, SelvaAlias_cmp_name) -RB_GENERATE(SelvaAliasesByDest, SelvaAlias, _entry2, SelvaAlias_cmp_dest) +RB_GENERATE(SelvaAliasesByName, SelvaAlias, _entryByName, SelvaAlias_cmp_name) +RB_GENERATE(SelvaAliasesByDest, SelvaAlias, _entryByDest, SelvaAlias_cmp_dest) static bool node_expire_cmp(struct SelvaExpireToken *tok, selva_expire_cmp_arg_t arg) { @@ -817,11 +817,7 @@ static void hash_aliases(selva_hash_state_t *hash_state, struct SelvaTypeEntry * }; alias = RB_FIND(SelvaAliasesByDest, &aliases->alias_by_dest, &find); - while (alias) { - - selva_hash_update(hash_state, alias->name, alias->name_len); - alias = alias->next; - } + selva_hash_update(hash_state, alias->name, alias->name_len); } } diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 490134a6d2..18504ff02d 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -41,7 +41,6 @@ static const size_t selva_field_data_size[] = { [SELVA_FIELD_TYPE_REFERENCES] = sizeof(struct SelvaNodeReferences), [SELVA_FIELD_TYPE_MICRO_BUFFER] = 0, /* check fs. */ [SELVA_FIELD_TYPE_ALIAS] = 0, /* Aliases are stored separately under the type struct. */ - [SELVA_FIELD_TYPE_ALIASES] = 0, [SELVA_FIELD_TYPE_COLVEC] = sizeof(void *), }; @@ -1823,7 +1822,7 @@ struct SelvaNode *selva_fields_ensure_ref_edge( { struct SelvaTypeEntry *edge_type = selva_get_type_by_index(db, efc->edge_node_type); struct SelvaNode *edge = nullptr; - + if (!edge_type) { return nullptr; } @@ -1976,7 +1975,6 @@ struct SelvaFieldsPointer selva_fields_get_raw(struct SelvaNode *node, const str .len = selva_fields_get_data_size(fs), }; case SELVA_FIELD_TYPE_ALIAS: - case SELVA_FIELD_TYPE_ALIASES: case SELVA_FIELD_TYPE_COLVEC: return (struct SelvaFieldsPointer){ .ptr = nullptr, @@ -2035,7 +2033,6 @@ static int fields_del(struct SelvaDb *db, struct SelvaNode *node, struct SelvaFi } break; case SELVA_FIELD_TYPE_ALIAS: - case SELVA_FIELD_TYPE_ALIASES: case SELVA_FIELD_TYPE_COLVEC: return SELVA_ENOTSUP; } @@ -2337,7 +2334,6 @@ void selva_fields_hash_update(selva_hash_state_t *hash_state, struct SelvaDb *, } while (0); break; case SELVA_FIELD_TYPE_ALIAS: - case SELVA_FIELD_TYPE_ALIASES: case SELVA_FIELD_TYPE_COLVEC: /* * NOP These are hashed in the node hash in db.c. diff --git a/clibs/lib/selva/include/db.h b/clibs/lib/selva/include/db.h index 87daf77f75..b166dd3d63 100644 --- a/clibs/lib/selva/include/db.h +++ b/clibs/lib/selva/include/db.h @@ -50,10 +50,8 @@ struct SelvaNode { static_assert(offsetof(struct SelvaNode, node_id) == 0); struct SelvaAlias { - RB_ENTRY(SelvaAlias) _entry1; - RB_ENTRY(SelvaAlias) _entry2; - struct SelvaAlias *prev; - struct SelvaAlias *next; /*!< Next alias for the same destination. */ + RB_ENTRY(SelvaAlias) _entryByName; + RB_ENTRY(SelvaAlias) _entryByDest; node_id_t dest; uint32_t name_len; char name[] __counted_by(name_len); @@ -90,7 +88,6 @@ struct SelvaTypeEntry { } *blocks; struct SelvaAliases { field_t field; /*!< Alias field. */ - bool single; /*!< Only allow a single alias per node + field. */ struct SelvaAliasesByName alias_by_name; struct SelvaAliasesByDest alias_by_dest; size_t nr_aliases; /*!< Number of aliases by name. */ @@ -168,8 +165,8 @@ struct SelvaDb { RB_PROTOTYPE(SelvaTypeEntryIndex, SelvaTypeEntry, _entry, SelvaTypeEntry_cmp) RB_PROTOTYPE(SelvaNodeIndex, SelvaNode, _index_entry, SelvaNode_cmp) -RB_PROTOTYPE(SelvaAliasesByName, SelvaAlias, _entry1, SelvaAlias_cmp_name) -RB_PROTOTYPE(SelvaAliasesByDest, SelvaAlias, _entry2, SelvaAlias_cmp_dest) +RB_PROTOTYPE(SelvaAliasesByName, SelvaAlias, _entryByName, SelvaAlias_cmp_name) +RB_PROTOTYPE(SelvaAliasesByDest, SelvaAlias, _entryByDest, SelvaAlias_cmp_dest) int SelvaNode_cmp(const struct SelvaNode *a, const struct SelvaNode *b); int SelvaAlias_cmp_name(const struct SelvaAlias *a, const struct SelvaAlias *b); int SelvaAlias_cmp_dest(const struct SelvaAlias *a, const struct SelvaAlias *b); diff --git a/clibs/lib/selva/include/io.h b/clibs/lib/selva/include/io.h index 19804d1695..39b19f401b 100644 --- a/clibs/lib/selva/include/io.h +++ b/clibs/lib/selva/include/io.h @@ -39,6 +39,7 @@ * - Save block hash at the end of each block dump * - Save block writelog in common.sdb * - Remove support for earlier SDB versions + * - Moved aliases saving from each node to saving all aliases at once */ #define SELVA_SDB_VERSION 8 diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 18cde7f51e..28f421b6fb 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -244,7 +244,6 @@ static void save_node_fields(struct selva_io *io, const struct SelvaFieldsSchema io->sdb_write(selva_fields_nfo2p(fields, nfo), sizeof(uint8_t), fs->smb.len, io); break; case SELVA_FIELD_TYPE_ALIAS: - case SELVA_FIELD_TYPE_ALIASES: case SELVA_FIELD_TYPE_COLVEC: /* NOP */ break; @@ -264,36 +263,25 @@ static void save_node(struct selva_io *io, struct SelvaDb *db, struct SelvaNode save_node_fields(io, schema, node); } -static void save_aliases_node(struct selva_io *io, struct SelvaTypeEntry *te, node_id_t node_id) +static void save_aliases(struct selva_io *io, struct SelvaTypeEntry *te) { const sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; write_dump_magic(io, DUMP_MAGIC_ALIASES); - io->sdb_write(&nr_aliases, sizeof(nr_aliases), 1, io); for (size_t i = 0; i < nr_aliases; i++) { struct SelvaAliases *aliases = &te->aliases[i]; - const struct SelvaAlias *alias_first; - const struct SelvaAlias *alias; - sdb_nr_aliases_t nr_aliases_by_dest = 0; - - alias_first = alias = selva_get_alias_by_dest(aliases, node_id); - while (alias) { - nr_aliases_by_dest++; - alias = alias->next; - } - - io->sdb_write(&nr_aliases_by_dest, sizeof(nr_aliases_by_dest), 1, io); + sdb_nr_aliases_t nr_aliases_by_name = aliases->nr_aliases; + struct SelvaAlias *alias; - alias = alias_first; - while (alias) { + io->sdb_write(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); + RB_FOREACH(alias, SelvaAliasesByName, &aliases->alias_by_name) { const char *name_str = alias->name; const sdb_arr_len_t name_len = alias->name_len; io->sdb_write(&name_len, sizeof(name_len), 1, io); io->sdb_write(name_str, sizeof(*name_str), name_len, io); - - alias = alias->next; + io->sdb_write(&alias->dest, sizeof(alias->dest), 1, io); } } } @@ -496,7 +484,6 @@ int selva_dump_save_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i selva_hash128_t node_hash = selva_node_hash_update(db, te, node, tmp_hash_state); selva_hash_update(hash_state, &node_hash, sizeof(node_hash)); save_node(&io, db, node); - save_aliases_node(&io, te, node->node_id); } /* @@ -504,6 +491,7 @@ int selva_dump_save_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i * note: colvec is hashed in node_hash. */ selva_dump_save_colvec(&io, db, te, start); + save_aliases(&io, te); selva_hash128_t block_hash = selva_hash_digest(hash_state); selva_hash_free_state(hash_state); @@ -784,7 +772,6 @@ static int load_node_fields(struct selva_io *io, struct SelvaDb *db, struct Selv err = load_field_micro_buffer(io, node, fs); break; case SELVA_FIELD_TYPE_ALIAS: - case SELVA_FIELD_TYPE_ALIASES: /* NOP */ break; case SELVA_FIELD_TYPE_COLVEC: @@ -836,29 +823,28 @@ static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct Selva } __attribute__((warn_unused_result)) -static int load_aliases_node(struct selva_io *io, struct SelvaTypeEntry *te, node_id_t node_id) +static int load_aliases(struct selva_io *io, struct SelvaTypeEntry *te) { - sdb_nr_aliases_t nr_aliases; + sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; if (!read_dump_magic(io, DUMP_MAGIC_ALIASES)) { selva_io_errlog(io, "Invalid aliases magic for type %d", te->type); return SELVA_EINVAL; } - io->sdb_read(&nr_aliases, sizeof(nr_aliases), 1, io); for (sdb_nr_aliases_t i = 0; i < nr_aliases; i++) { - sdb_nr_aliases_t nr_aliases_by_dest; + sdb_nr_aliases_t nr_aliases_by_name; - io->sdb_read(&nr_aliases_by_dest, sizeof(nr_aliases_by_dest), 1, io); - for (size_t j = 0; j < nr_aliases_by_dest; j++) { + io->sdb_read(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); + for (size_t j = 0; j < nr_aliases_by_name; j++) { sdb_arr_len_t name_len; struct SelvaAlias *alias; io->sdb_read(&name_len, sizeof(name_len), 1, io); alias = selva_malloc(sizeof_wflex(struct SelvaAlias, name, name_len)); - alias->name_len = name_len; io->sdb_read(alias->name, sizeof(char), name_len, io); - alias->dest = node_id; + alias->name_len = name_len; + io->sdb_read(&alias->dest, sizeof(alias->dest), 1, io); selva_set_alias_p(&te->aliases[i], alias); } @@ -870,7 +856,6 @@ static int load_aliases_node(struct selva_io *io, struct SelvaTypeEntry *te, nod __attribute__((warn_unused_result)) static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te) { - int err; sdb_nr_nodes_t nr_nodes; io->sdb_read(&nr_nodes, sizeof(nr_nodes), 1, io); @@ -881,11 +866,6 @@ static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeE if (unlikely(node_id == 0)) { return SELVA_EINVAL; } - - err = load_aliases_node(io, te, node_id); - if (err) { - return err; - } } return 0; @@ -943,6 +923,7 @@ static int load_type(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEn err = load_nodes(io, db, te); err = (err) ?: load_colvec(io, te); + err = (err) ?: load_aliases(io, te); return err; } diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index e490239d65..c638d67930 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -237,19 +237,6 @@ static int type2fs_alias(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSch return 1; } -static int type2fs_aliases(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) -{ - struct SelvaFieldSchema *fs = &schema->field_schemas[field]; - - *fs = (struct SelvaFieldSchema){ - .field = field, - .type = SELVA_FIELD_TYPE_ALIASES, - .alias_index = ctx->alias_index++, - }; - - return 1; -} - static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) { struct SelvaFieldSchema *fs = &schema->field_schemas[field]; @@ -312,10 +299,6 @@ static struct schemabuf_parser { .type = SELVA_FIELD_TYPE_ALIAS, .type2fs = type2fs_alias, }, - [SELVA_FIELD_TYPE_ALIASES] = { - .type = SELVA_FIELD_TYPE_ALIASES, - .type2fs = type2fs_aliases, - }, [SELVA_FIELD_TYPE_COLVEC] = { .type = SELVA_FIELD_TYPE_COLVEC, .type2fs = type2fs_colvec, diff --git a/clibs/lib/selva/types.c b/clibs/lib/selva/types.c index 100876caba..c5fcaf85dc 100644 --- a/clibs/lib/selva/types.c +++ b/clibs/lib/selva/types.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 SAULX + * Copyright (c) 2024-2026 SAULX * SPDX-License-Identifier: MIT */ @@ -15,7 +15,6 @@ bool selva_is_valid_field_type(enum SelvaFieldType ftype) case SELVA_FIELD_TYPE_REFERENCES: case SELVA_FIELD_TYPE_MICRO_BUFFER: case SELVA_FIELD_TYPE_ALIAS: - case SELVA_FIELD_TYPE_ALIASES: case SELVA_FIELD_TYPE_COLVEC: return true; } @@ -40,8 +39,6 @@ const char *selva_str_field_type(enum SelvaFieldType ftype) return (const char *)"micro buffer"; case SELVA_FIELD_TYPE_ALIAS: return (const char *)"alias"; - case SELVA_FIELD_TYPE_ALIASES: - return (const char *)"aliases"; case SELVA_FIELD_TYPE_COLVEC: return (const char *)"columnar vector"; } From 1fb63fea17c095a366af389251ba6a9a80d282d3 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 30 Jan 2026 23:06:31 +0100 Subject: [PATCH 046/449] cleanup --- clibs/lib/selva/alias.c | 8 ++------ clibs/lib/selva/db.c | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index 46f361b96f..47ffaebe06 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -137,10 +137,6 @@ void selva_del_alias_by_dest(struct SelvaAliases *aliases, node_id_t dest) } remove_alias_by_dest(aliases, alias); - - /* - * Remove this alias from by_name. - */ remove_alias_by_name(aliases, alias); selva_free(alias); } @@ -188,7 +184,7 @@ const char *selva_get_alias_name(const struct SelvaAlias *alias, size_t *len) struct SelvaAliases *selva_get_aliases(struct SelvaTypeEntry *type, field_t field) { - size_t nr_aliases = type->ns.nr_aliases; + const size_t nr_aliases = type->ns.nr_aliases; for (size_t i = 0; i < nr_aliases; i++) { if (type->aliases[i].field == field) { @@ -201,7 +197,7 @@ struct SelvaAliases *selva_get_aliases(struct SelvaTypeEntry *type, field_t fiel void selva_remove_all_aliases(struct SelvaTypeEntry *type, node_id_t node_id) { - size_t nr_aliases = type->ns.nr_aliases; + const size_t nr_aliases = type->ns.nr_aliases; for (size_t i = 0; i < nr_aliases; i++) { selva_del_alias_by_dest(&type->aliases[i], node_id); diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index e6d12a94db..93d8fd48a1 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -548,7 +548,6 @@ static void selva_unl_node(struct SelvaDb *db, struct SelvaTypeEntry *type, stru void selva_flush_node(struct SelvaDb *db, struct SelvaTypeEntry *type, struct SelvaNode *node) { selva_mark_dirty(type, node->node_id); - selva_remove_all_aliases(type, node->node_id); selva_fields_flush(db, node); } From 2bc667d9a06122756448d50e01b3452e56fa770d Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 31 Jan 2026 07:54:14 +0100 Subject: [PATCH 047/449] use new typeDefs in selvaSchema --- native/modify/modify.zig | 4 +- native/query/include/include.zig | 2 +- native/types.zig | 12 ++ src/db-server/schema.ts | 139 ++++++++++------ src/db-server/schemaSelvaBuffer.ts | 245 ---------------------------- src/schema/defs/index.ts | 1 + src/schema/defs/props/base.ts | 16 +- src/schema/defs/props/references.ts | 42 ++++- src/schema/defs/props/separate.ts | 76 ++++++++- src/zigTsExports.ts | 37 +++++ test/query-ast/include.ts | 4 +- 11 files changed, 261 insertions(+), 317 deletions(-) delete mode 100644 src/db-server/schemaSelvaBuffer.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 5550f2530b..be62869c7b 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -57,7 +57,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); const size = main.type.size(); const value = data[j .. j + size]; - std.debug.print("main: size {any} value {any} current {any}\n", .{ size, value, current }); + // std.debug.print("main: size {any} value {any} current {any}\n", .{ size, value, current }); if (main.increment != .none) { switch (main.type) { .number => applyInc(f64, current, value, main.start, main.increment), @@ -95,7 +95,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u if (meta.isTmp) refId = utils.read(u32, items, refId * resItemSize); if (Node.getNode(refTypeEntry, refId)) |dst| { _ = try References.writeReference(db, node, propSchema, dst); - std.debug.print("meta.size {any}\n", .{meta.size}); + // std.debug.print("meta.size {any}\n", .{meta.size}); if (meta.size != 0) { const edgeProps = value[k .. k + meta.size]; const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); diff --git a/native/query/include/include.zig b/native/query/include/include.zig index 1d00118621..3777b02ace 100644 --- a/native/query/include/include.zig +++ b/native/query/include/include.zig @@ -119,7 +119,7 @@ pub fn include( .default => { const header = utils.readNext(t.IncludeHeader, q, &i); const value = try get(typeEntry, node, &header); - std.debug.print("??? value {any} - {any}\n", .{ value, header }); + // std.debug.print("??? value {any} - {any}\n", .{ value, header }); switch (header.propType) { t.PropType.text, => { diff --git a/native/types.zig b/native/types.zig index f40f9a672d..1f7e123da3 100644 --- a/native/types.zig +++ b/native/types.zig @@ -292,6 +292,18 @@ pub const PropType = enum(u8) { } }; +pub const PropTypeSelva = enum(u8) { + null = 0, + microBuffer = 1, + string = 2, + text = 3, + reference = 4, + references = 5, + alias = 8, + aliases = 9, + colVec = 10, +}; + pub const RefOp = enum(u8) { clear = 0, del = 1, diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index 3f0ed7148a..f20c21f993 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -2,34 +2,29 @@ import { DbServer } from './index.js' import { join } from 'node:path' import { writeFile } from 'node:fs/promises' import native, { idGenerator } from '../native.js' -import { schemaToSelvaBuffer } from './schemaSelvaBuffer.js' import { readUint32, writeUint32 } from '../utils/index.js' -import { OpType, pushSelvaSchemaHeader } from '../zigTsExports.js' -import { serialize, updateTypeDefs, type SchemaOut } from '../schema/index.js' +import { + LangCode, + Modify, + OpType, + PropType, + PropTypeSelva, + pushSelvaSchemaHeader, + pushSelvaSchemaMicroBuffer, + type PropTypeEnum, +} from '../zigTsExports.js' +import { + BLOCK_CAPACITY_DEFAULT, + serialize, + updateTypeDefs, + type SchemaOut, +} from '../schema/index.js' import { SCHEMA_FILE } from '../index.js' import { getTypeDefs } from '../schema/defs/getTypeDefs.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' const schemaOpId = idGenerator() -async function getSchemaIds(db: DbServer): Promise { - const id = schemaOpId.next().value - const msg = new Uint8Array(5) - - writeUint32(msg, id, 0) - msg[4] = OpType.getSchemaIds - - return new Promise((resolve) => { - db.addOpOnceListener(OpType.getSchemaIds, id, (buf: Uint8Array) => { - const ids = new Uint32Array(buf.length / Uint32Array.BYTES_PER_ELEMENT) - const tmp = new Uint8Array(ids.buffer) - tmp.set(buf) - resolve(ids) - }) - native.query(msg, db.dbCtxExternal) - }) -} - function setSchemaIds(db: DbServer, ids: Uint32Array): Promise { const id = schemaOpId.next().value @@ -95,6 +90,13 @@ export async function createSelvaType( }) } +const supportedDefaults = new Set([ + PropType.binary, + PropType.string, + PropType.text, + PropType.vector, + PropType.json, // same as binary (Uint8Array) +]) /** * Set schema used in native code. * This function should be only called when a new schema is set to an empty DB @@ -102,45 +104,76 @@ export async function createSelvaType( * necessary because `common.sdb` already contains the required schema. */ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { - const types = Object.keys(server.schemaTypesParsed) - const s = schemaToSelvaBuffer(server.schemaTypesParsed) - let maxTid = 0 - // console.log('-----', schemaToSelvaBufferNew(server.schema!)) - // process.exit() + const typeDefs = getTypeDefs(schema) + let maxTypeId = 0 await Promise.all( - s.map(async (buf, i) => { - const type = server.schemaTypesParsed[types[i]] - maxTid = Math.max(maxTid, type.id) - try { - await createSelvaType(server, type.id, buf) - } catch (err) { - throw new Error( - `Cannot update schema on selva (native) ${type.type} ${err.message}`, - ) + typeDefs.values().map((typeDef) => { + const buf = new AutoSizedUint8Array(4, 65536) + maxTypeId = Math.max(maxTypeId, typeDef.id) + let nrFixedFields = 1 + let nrVirtualFields = 0 + + for (const prop of typeDef.separate) { + if ( + 'default' in prop.schema && + prop.schema.default && + supportedDefaults.has(prop.type) + ) { + // TODO what is fixedFields exactly + // could we make a return type in the prop.pushSelvaSchema for this? + nrFixedFields++ + } else if ( + prop.type === PropType.reference || + prop.type === PropType.references + ) { + nrFixedFields++ + } else if ( + prop.type === PropType.alias || + prop.type === PropType.aliases || + prop.type === PropType.colVec + ) { + // We assume that these are always the last props! + nrVirtualFields++ + } } + + pushSelvaSchemaHeader(buf, { + blockCapacity: typeDef.schema.blockCapacity || BLOCK_CAPACITY_DEFAULT, + nrFields: 1 + typeDef.separate.length, + nrFixedFields, + nrVirtualFields, + sdbVersion: 8, + }) + + // handle main + const mainLen = typeDef.main.reduce((len, { size }) => len + size, 0) + pushSelvaSchemaMicroBuffer(buf, { + type: PropTypeSelva.microBuffer, + len: mainLen, + hasDefault: 1, + }) + + for (const prop of typeDef.main) { + if ('default' in prop.schema && prop.schema.default) { + prop.pushValue(buf, prop.schema.default, Modify.create, LangCode.none) + } else { + buf.fill(0, buf.length, buf.length + prop.size) + } + } + + // handle separate + for (const prop of typeDef.separate) { + // TODO put defaults! + prop.pushSelvaSchema(buf) + } + + return createSelvaType(server, typeDef.id, buf.view) }), ) - await setSchemaIds(server, new Uint32Array(maxTid)) + await setSchemaIds(server, new Uint32Array(maxTypeId)) if (server.fileSystemPath) { server.save({ skipDirtyCheck: true }).catch(console.error) } } - -// function schemaToSelvaBufferNew(schema: SchemaOut): Uint8Array[] { -// const typeDefs = getTypeDefs(schema) -// const buf = new AutoSizedUint8Array(4, 65536) -// for (const [, typeDef] of typeDefs) { -// for (const propDef of typeDef.main) { -// } -// } - -// pushSelvaSchemaHeader(buf, { -// blockCapacity: t.blockCapacity, -// nrFields: 1 + typeDef.separate.length, -// nrFixedFields, -// nrVirtualFields: virtualFields, -// sdbVersion: 8, -// }) -// } diff --git a/src/db-server/schemaSelvaBuffer.ts b/src/db-server/schemaSelvaBuffer.ts deleted file mode 100644 index 910ebe9734..0000000000 --- a/src/db-server/schemaSelvaBuffer.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { writeUint32 } from '../utils/index.js' -import { - createSelvaSchemaColvec, - createSelvaSchemaRef, - createSelvaSchemaString, - createSelvaSchemaText, - LangCode, - PropType, - PropTypeEnum, - pushSelvaSchemaColvec, - pushSelvaSchemaHeader, - pushSelvaSchemaMicroBuffer, - pushSelvaSchemaRef, - pushSelvaSchemaString, - pushSelvaSchemaText, - writeSelvaSchemaMicroBuffer, -} from '../zigTsExports.js' -import { - EMPTY_MICRO_BUFFER, - VECTOR_BASE_TYPE_SIZE_MAP, - type PropDef, - type PropDefEdge, - type SchemaTypeDef, -} from '../schema/index.js' -// import { write as writeString } from '../db-client/string.js' -import { fillEmptyMain } from '../schema/def/fillEmptyMain.js' -import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' -// import { Ctx } from '../db-client/_modify/Ctx.js' - -const selvaFieldType: Readonly> = { - NULL: 0, - MICRO_BUFFER: 1, - STRING: 2, - TEXT: 3, - REFERENCE: 4, - REFERENCES: 5, - ALIAS: 8, - ALIASES: 9, - COLVEC: 10, -} - -const selvaTypeMap = new Uint8Array(32) // 1.2x faster than JS array -selvaTypeMap[PropType.microBuffer] = selvaFieldType.MICRO_BUFFER -selvaTypeMap[PropType.vector] = selvaFieldType.MICRO_BUFFER -selvaTypeMap[PropType.binary] = selvaFieldType.STRING -selvaTypeMap[PropType.cardinality] = selvaFieldType.STRING -selvaTypeMap[PropType.json] = selvaFieldType.STRING -selvaTypeMap[PropType.string] = selvaFieldType.STRING -selvaTypeMap[PropType.text] = selvaFieldType.TEXT -selvaTypeMap[PropType.reference] = selvaFieldType.REFERENCE -selvaTypeMap[PropType.references] = selvaFieldType.REFERENCES -selvaTypeMap[PropType.alias] = selvaFieldType.ALIAS -selvaTypeMap[PropType.aliases] = selvaFieldType.ALIASES -selvaTypeMap[PropType.colVec] = selvaFieldType.COLVEC - -const EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT = 0x01 - -const supportedDefaults = new Set([ - PropType.binary, - PropType.string, - PropType.text, - PropType.vector, - PropType.json, // same as binary (Uint8Array) -]) - -function blockCapacity(blockCapacity: number): Uint8Array { - const buf = new Uint8Array(Uint32Array.BYTES_PER_ELEMENT) - writeUint32(buf, blockCapacity, 0) - return buf -} - -function sepPropCount(props: Array): number { - return props.filter((prop) => prop.separate).length -} - -function makeEdgeConstraintFlags(prop: PropDef): number { - let flags = 0 - - flags |= prop.dependent ? EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT : 0x00 - - return flags -} - -const propDefBuffer = ( - schemaBuf: AutoSizedUint8Array, - schema: { [key: string]: SchemaTypeDef }, - prop: PropDef, -): void => { - const type = prop.typeIndex - const selvaType = selvaTypeMap[type] - - if (prop.len && (type === PropType.microBuffer || type === PropType.vector)) { - pushSelvaSchemaMicroBuffer(schemaBuf, { - type: selvaType, - len: prop.len, - hasDefault: ~~!!prop.default, - }) - - if (prop.default) { - schemaBuf.set(prop.default, schemaBuf.length) - } - } else if (prop.len && type === PropType.colVec) { - const baseSize = VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] - pushSelvaSchemaColvec(schemaBuf, { - type: selvaType, - vecLen: prop.len / baseSize, - compSize: baseSize, - hasDefault: 0, - }) - // TODO Add support for default - } else if (type === PropType.reference || type === PropType.references) { - const dstType: SchemaTypeDef = schema[prop.inverseTypeName!] - pushSelvaSchemaRef(schemaBuf, { - type: selvaType, - flags: makeEdgeConstraintFlags(prop), - dstNodeType: dstType.id, - inverseField: prop.inversePropNumber!, - edgeNodeType: prop.edgeNodeTypeId ?? 0, - capped: prop.referencesCapped ?? 0, - }) - } else if ( - type === PropType.string || - type === PropType.binary || - type === PropType.cardinality || - type === PropType.json - ) { - if (prop.default && supportedDefaults.has(type)) { - console.warn('TODO default!!') - // const defaultValue = - // typeof prop.default === 'string' - // ? prop.default.normalize('NFKD') - // : type === PropType.json - // ? JSON.stringify(prop.default) - // : prop.default - // const defaultLen = - // defaultValue instanceof Uint8Array - // ? defaultValue.byteLength - // : 2 * native.stringByteLength(defaultValue) + STRING_EXTRA_MAX - // let buf = new Uint8Array(6 + defaultLen) - - // buf[0] = selvaType - // buf[1] = prop.len < 50 ? prop.len : 0 - // const l = - // defaultValue instanceof Uint8Array - // ? (buf.set(defaultValue, 6), defaultLen) - // : writeString({ buf } as Ctx, defaultValue, 6, LangCode.none, false) - // if (l != buf.length) { - // buf = buf.subarray(0, 6 + l) - // } - // writeUint32(buf, l, 2) // default len - - // return [...buf] - } else { - pushSelvaSchemaString(schemaBuf, { - type: selvaType, - fixedLen: prop.len < 50 ? prop.len : 0, - defaultLen: 0, - }) - } - } else if (type === PropType.text) { - // TODO Defaults - pushSelvaSchemaText(schemaBuf, { - type: selvaType, - nrDefaults: Object.keys(prop.default).length, - }) - - //for (const langName in prop.default) { - // console.warn('TODO default!!') - // const lang = LangCode[langName] - // const value = prop.default[langName].normalize('NFKD') - // const tmpLen = 4 + 2 * native.stringByteLength(value) + STRING_EXTRA_MAX - // let buf = new Uint8Array(tmpLen) - - // const l = writeString({ buf } as Ctx, value, 4, lang, false) - // if (l != buf.length) { - // buf = buf.subarray(0, 4 + l) - // } - // writeUint32(buf, l, 0) // length of the default - // fs.push(...buf) - //} - } else { - schemaBuf.pushUint8(selvaType) - } -} - -export function schemaToSelvaBuffer(schema: { - [key: string]: SchemaTypeDef -}): Uint8Array[] { - return Object.values(schema).map((t) => { - const props: PropDef[] = Object.values(t.props) - const rest: PropDef[] = [] - const nrFields = 1 + sepPropCount(props) - let nrFixedFields = 1 - let virtualFields = 0 - - if (nrFields >= 250) { - throw new Error('Too many fields') - } - - const mainLen = t.mainLen === 0 ? 1 : t.mainLen - const main = { - ...EMPTY_MICRO_BUFFER, - default: fillEmptyMain(props, mainLen), - len: mainLen, - } - - const buf = new AutoSizedUint8Array(4, 65536) - - for (const f of props) { - if (!f.separate) { - continue - } - - if (f.default && supportedDefaults.has(f.typeIndex)) { - nrFixedFields++ - } else if ( - f.typeIndex === PropType.reference || - f.typeIndex === PropType.references - ) { - nrFixedFields++ - } else if ( - f.typeIndex === PropType.alias || - f.typeIndex === PropType.aliases || - f.typeIndex === PropType.colVec - ) { - // We assume that these are always the last props! - virtualFields++ - } - rest.push(f) - } - - rest.sort((a, b) => a.prop - b.prop) - - pushSelvaSchemaHeader(buf, { - blockCapacity: t.blockCapacity, - nrFields, - nrFixedFields, - nrVirtualFields: virtualFields, - sdbVersion: 8, - }) - propDefBuffer(buf, schema, main) - rest.forEach((f) => propDefBuffer(buf, schema, f)) - return buf.slice() // Doing a slice might reduce the mem pressure? - }) -} diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 8e76b0a17d..4410036e8f 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -38,6 +38,7 @@ export type PropDef = { op: ModifyEnum, lang: LangCodeEnum, ): void + pushSelvaSchema(buf: AutoSizedUint8Array): void } export const isPropDef = (p: any): p is PropDef => { diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 5e3971f80a..23d404d404 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -1,8 +1,11 @@ import type { SchemaProp } from '../../../schema.js' -import type { - LangCodeEnum, - ModifyEnum, - PropTypeEnum, +import { + PropType, + PropTypeSelva, + type LangCodeEnum, + type ModifyEnum, + type PropTypeEnum, + type PropTypeSelvaEnum, } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { PropDef, TypeDef } from '../index.js' @@ -16,7 +19,7 @@ export class BasePropDef implements PropDef { id = 0 start = 0 size = 0 - type: PropTypeEnum = 0 as PropTypeEnum + type: PropTypeEnum = PropType.null schema: SchemaProp path: string[] isEdge: boolean = false @@ -29,4 +32,7 @@ export class BasePropDef implements PropDef { ): void { // To be implemented by subclasses } + pushSelvaSchema(buf: AutoSizedUint8Array): void { + // To be implemented by subclasses + } } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index c1ff50a79c..79725ebec5 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -2,9 +2,11 @@ import { Modify, ModifyReferences, PropType, + PropTypeSelva, pushModifyReferenceMetaHeader, pushModifyReferencesHeader, pushModifyReferencesMetaHeader, + pushSelvaSchemaRef, writeModifyReferenceMetaHeaderProps, writeModifyReferencesHeaderProps, writeModifyReferencesMetaHeaderProps, @@ -13,7 +15,11 @@ import { type PropTypeEnum, } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' -import type { SchemaProp } from '../../../schema.js' +import type { + SchemaProp, + SchemaReference, + SchemaReferences, +} from '../../../schema.js' import { BasePropDef } from './base.js' import type { PropDef, TypeDef } from '../index.js' import { @@ -199,6 +205,10 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { export const references = class References extends BasePropDef { override type: PropTypeEnum = PropType.references + declare schema: SchemaReferences + declare ref: TypeDef + declare refProp: PropDef + declare edges?: TypeDef override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -231,10 +241,24 @@ export const references = class References extends BasePropDef { deleteReferences(buf, val.delete) } } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaRef(buf, { + type: PropTypeSelva.references, + flags: makeEdgeConstraintFlags(this.schema.items), + dstNodeType: this.ref.id, + inverseField: this.refProp.id, + edgeNodeType: this.edges?.id ?? 0, + capped: this.schema.capped ?? 0, + }) + } } export const reference = class Reference extends BasePropDef { override type: PropTypeEnum = PropType.reference + declare schema: SchemaReference + declare ref: TypeDef + declare refProp: PropDef + declare edges?: TypeDef override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -295,4 +319,20 @@ export const reference = class Reference extends BasePropDef { } } } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaRef(buf, { + type: PropTypeSelva.reference, + flags: makeEdgeConstraintFlags(this.schema), + dstNodeType: this.ref.id, + inverseField: this.refProp.id, + edgeNodeType: this.edges?.id ?? 0, + capped: 0, + }) + } +} + +function makeEdgeConstraintFlags(schema: SchemaReference): number { + let flags = 0 + flags |= schema.dependent ? 0x01 : 0x00 + return flags } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 617071b5f4..00e7a5b53b 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -11,6 +11,12 @@ import { PropType, type LangCodeEnum, type PropTypeEnum, + PropTypeSelva, + type PropTypeSelvaEnum, + pushSelvaSchemaColvec, + pushSelvaSchemaMicroBuffer, + pushSelvaSchemaString, + pushSelvaSchemaText, } from '../../../zigTsExports.js' import { xxHash64 } from '../../../db-client/xxHash64.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' @@ -30,6 +36,7 @@ export const string = class String extends BasePropDef { this.pushValue = this.pushFixedValue as any } } + override type: PropTypeEnum = PropType.string override pushValue( buf: AutoSizedUint8Array, @@ -74,11 +81,24 @@ export const string = class String extends BasePropDef { buf.pushUint32(crc) } pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + } } // TODO do it nice export const text = class Text extends string { override type = PropType.text + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaText(buf, { + type: PropTypeSelva.text, + nrDefaults: 0, + }) + } } export const json = class Json extends string { @@ -93,6 +113,13 @@ export const json = class Json extends string { } super.pushValue(buf, JSON.stringify(value), lang) } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + } } export const binary = class Binary extends BasePropDef { @@ -114,6 +141,13 @@ export const binary = class Binary extends BasePropDef { } buf.set(value, buf.length) } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + } } export const alias = class Alias extends BasePropDef { @@ -124,6 +158,9 @@ export const alias = class Alias extends BasePropDef { ): asserts value is any { throw new Error('Serialize alias not implemented') } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + buf.pushUint8(PropTypeSelva.alias) + } } export const cardinality = class Cardinality extends BasePropDef { @@ -155,7 +192,6 @@ export const cardinality = class Cardinality extends BasePropDef { pushModifyCardinalityHeader(buf, this) for (const item of items) { - // validate(item, def) if (typeof item === 'string') { buf.reserveUint64() xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) @@ -166,12 +202,19 @@ export const cardinality = class Cardinality extends BasePropDef { } } } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + } } export const vector = class Vector extends BasePropDef { - constructor(prop: SchemaVector, path: string[], typeDef: TypeDef) { - super(prop, path, typeDef) - this.vectorSize = prop.size * 4 + constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + this.vectorSize = schema.size * 4 } vectorSize: number override type: PropTypeEnum = PropType.vector @@ -181,14 +224,23 @@ export const vector = class Vector extends BasePropDef { ): asserts value is any { throw new Error('Serialize vector not implemented') } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaMicroBuffer(buf, { + type: PropTypeSelva.colVec, + len: this.vectorSize, + hasDefault: 0, // TODO default + }) + } } export const colvec = class ColVec extends BasePropDef { - constructor(prop: SchemaVector, path: string[], typeDef: TypeDef) { - super(prop, path, typeDef) - this.colvecSize = prop.size * getByteSize(prop.baseType) + constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + this.compSize = getByteSize(schema.baseType) + this.vecLen = schema.size * this.compSize } - colvecSize: number + compSize: number + vecLen: number override type = PropType.colVec override pushValue( buf: AutoSizedUint8Array, @@ -196,6 +248,14 @@ export const colvec = class ColVec extends BasePropDef { ): asserts value is any { throw new Error('Serialize colvec not implemented') } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaColvec(buf, { + type: PropTypeSelva.colVec, + vecLen: this.vecLen, + compSize: this.compSize, + hasDefault: 0, + }) + } } function getByteSize(str?: string) { diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 70992572e9..885a10b445 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1225,6 +1225,43 @@ export const PropTypeInverse = { */ export type PropTypeEnum = (typeof PropType)[keyof typeof PropType] +export const PropTypeSelva = { + null: 0, + microBuffer: 1, + string: 2, + text: 3, + reference: 4, + references: 5, + alias: 8, + aliases: 9, + colVec: 10, +} as const + +export const PropTypeSelvaInverse = { + 0: 'null', + 1: 'microBuffer', + 2: 'string', + 3: 'text', + 4: 'reference', + 5: 'references', + 8: 'alias', + 9: 'aliases', + 10: 'colVec', +} as const + +/** + null, + microBuffer, + string, + text, + reference, + references, + alias, + aliases, + colVec + */ +export type PropTypeSelvaEnum = (typeof PropTypeSelva)[keyof typeof PropTypeSelva] + export const RefOp = { clear: 0, del: 1, diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 2c8ffd5565..393212fc4d 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -44,7 +44,7 @@ await test('include', async (t) => { }, }) - const a = await client.create('user', { + const a = client.create('user', { name: 'AAAAAAAAAA', y: 67, x: true, @@ -54,7 +54,7 @@ await test('include', async (t) => { }, }) - const b = await client.create('user', { + const b = client.create('user', { name: 'BBBBBBBBB', y: 67, x: true, From e9b939e2f9cf668b1e569cc865256a2fe32eefe7 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sat, 31 Jan 2026 07:59:19 +0100 Subject: [PATCH 048/449] filters --- native/types.zig | 16 --- src/db-query/ast/ast.ts | 35 ++++-- src/db-query/ast/filter/condition.ts | 182 +++++++++++++++++++++++++++ src/db-query/ast/filter/filter.ts | 79 ++++++++++++ src/db-query/ast/include.ts | 42 ++++--- src/db-query/ast/iteratorType.ts | 17 +-- src/db-query/ast/multiple.ts | 36 ++++-- src/db-query/ast/single.ts | 1 + src/db-query/ast/toCtx.ts | 6 +- src/zigTsExports.ts | 24 ---- test/query-ast/include.ts | 42 +++---- 11 files changed, 371 insertions(+), 109 deletions(-) create mode 100644 src/db-query/ast/filter/condition.ts create mode 100644 src/db-query/ast/filter/filter.ts diff --git a/native/types.zig b/native/types.zig index 94a6218f78..8841692564 100644 --- a/native/types.zig +++ b/native/types.zig @@ -838,25 +838,9 @@ pub const FilterOpCompare = enum(u8) { // ----------- gt = 14, lt = 15, - // gtBatch = 16, - // ltBatch = 17, - // gtBatchSmall = 18, - // ltBatchSmall = 19, - // ----------- ge = 20, le = 21, - // geBatch = 22, - // leBatch = 23, - // geBatchSmall = 24, - // leBatchSmall = 25, - // ----------- - // eq = 12, - // will become quite a lot :L > , < <=, >= - // maybe format a bit easier - - // var is a lot less - // selectLargeRef = 202, selectLargeRefs = 203, selectRef = 204, selectSmallRefs = 205, diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 67a91b9974..5385796827 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -2,9 +2,36 @@ import { ReaderLocales, ReaderSchema } from '../../protocol/index.js' import { PropDef, PropTree } from '../../schema/defs/index.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +export type FilterOpts = { + lowerCase?: boolean + fn?: + | 'dotProduct' + | 'manhattanDistance' + | 'cosineSimilarity' + | 'euclideanDistance' + score?: number +} + +export type Operator = + | '=' + | '<' + | '>' + | '!=' + | '>=' + | '<=' + | '..' + | '!..' + | 'exists' + | '!exists' + | 'like' + | '!like' + | 'includes' + | '!includes' + export type FilterOp = { - op: '=' | '<' | '>' | '..' | 'includes' | 'exists' | 'exist' + op: Operator val?: any + opts?: FilterOpts } export type FilterAst = { @@ -27,11 +54,6 @@ export type Include = { raw?: boolean } -export type IncludeCtx = { - tree: PropTree - main: { prop: PropDef; include: Include }[] -} - export type QueryAst = { include?: Include select?: { start: number; end: number } @@ -47,6 +69,5 @@ export type QueryAst = { export type Ctx = { query: AutoSizedUint8Array readSchema: ReaderSchema - sub: AutoSizedUint8Array locales: ReaderLocales } diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts new file mode 100644 index 0000000000..effe4c85e7 --- /dev/null +++ b/src/db-query/ast/filter/condition.ts @@ -0,0 +1,182 @@ +import { getPropWriter } from '../../../schema/def/utils.js' +import { PropDef } from '../../../schema/defs/index.js' +import { + FilterConditionByteSize, + FilterConditionAlignOf, + FilterOp, + FilterOpCompare, + writeFilterCondition, +} from '../../../zigTsExports.js' +import { FilterOpts, Operator } from '../ast.js' + +// this will be PUSH +export const conditionBuffer = ( + propDef: { start: number; id: number; size: number }, + size: number, + op: FilterOp, +) => { + const condition = new Uint8Array( + size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propDef.size, + ) + console.log('COND BUFFER', size, condition) + condition[0] = 255 // Means condition header is not aligned + const offset = + writeFilterCondition( + condition, + { + op, + start: propDef.start || 0, + prop: propDef.id, + fieldSchema: 0, + len: propDef.size, + offset: 255, // Means value is not aligned + size: size + propDef.size, + }, + FilterConditionAlignOf + 1, + ) + propDef.size + return { condition, offset } +} + +const opMap: Partial> = { + '=': 'eq', + '!=': 'neq', + '>': 'gt', + '<': 'lt', + '>=': 'ge', + '<=': 'le', +} + +const getFilterOp = ( + propDef: PropDef, + write: ReturnType, + operator: Operator, + size: number, +): { + size: number + op: FilterOp + write: ReturnType +} => { + const opName = opMap[operator] + + if (!opName) { + throw new Error(`un supported op ${operator}`) + } + + if ((opName === 'eq' || opName === 'neq') && size > 1) { + const vectorLen = 16 / propDef.size + if (size > vectorLen) { + return { + op: { + compare: FilterOpCompare[`${opName}Batch`], + prop: propDef.type, + }, + size: propDef.size, + write, + } + } else { + return { + op: { + compare: FilterOpCompare[`${opName}BatchSmall`], + prop: propDef.type, + }, + size: propDef.size, + write, + } + } + } else if (operator === '..' || operator === '!..') { + return { + op: { + compare: FilterOpCompare.range, + prop: propDef.type, + }, + size: propDef.size * 2, + write: (condition: Uint8Array, v: any, offset: number) => { + // x >= 3 && x <= 11 + // (x -% 3) <= (11 - 3) + write(condition, v[0], offset) + write(condition, v[1] - v[0], offset + propDef.size) + return condition + }, + } + } else { + return { + op: { + compare: FilterOpCompare[opName], + prop: propDef.type, + }, + size: propDef.size, + write, + } + } +} + +export const createCondition = ( + propDef: PropDef, + operator: Operator, + value?: any, + opts?: FilterOpts, +) => { + if (value !== undefined && !(value instanceof Array)) { + value = [value] + } + + const writer = getPropWriter(propDef.type) + const { op, size, write } = getFilterOp( + propDef, + writer, + operator, + value.length, + ) + + const vectorLen = 16 / size + + if (value.length == 1 || operator === '..' || operator == '!..') { + const { condition, offset } = conditionBuffer( + { size: propDef.size, start: propDef.start, id: propDef.id }, + size, + op, + ) + if (operator === '..' || operator == '!..') { + write(condition, value, offset) + } else { + write(condition, value[0], offset) + } + + console.log('derp', condition) + return condition + } else if (value.length > vectorLen) { + const { condition, offset } = conditionBuffer( + { ...propDef, start: propDef.start || 0 }, + value.length * size, + op, + ) + let i = offset + // Actual values + for (const v of value) { + write(condition, v, i) + i += propDef.size + } + // Empty padding for SIMD (16 bytes) + for (let j = 0; j < vectorLen; j++) { + write(condition, value[0], i) + i += size + } + return condition + } else if (value.length > 1) { + // Small batch + const { condition, offset } = conditionBuffer( + { ...propDef, start: propDef.start || 0 }, + value.length * size, + op, + ) + let i = offset + for (let j = 0; j < vectorLen; j++) { + // Allways use a full ARM neon simd vector (16 bytes) + write(condition, j >= value.length ? value[0] : value[j], i) + i += size + } + return condition + } + + throw new Error('Cannot create filter cond') +} diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts new file mode 100644 index 0000000000..aa09134765 --- /dev/null +++ b/src/db-query/ast/filter/filter.ts @@ -0,0 +1,79 @@ +import { + isPropDef, + PropDef, + PropTree, + TypeDef, +} from '../../../schema/defs/index.js' +import { PropType } from '../../../zigTsExports.js' +import { Ctx, FilterAst, FilterOp } from '../ast.js' +import { createCondition } from './condition.js' + +type WalkCtx = { + tree: PropTree + main: { prop: PropDef; ops: FilterOp[] }[] +} + +const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { + const { tree, main } = walkCtx + + for (const field in ast.props) { + const prop = tree.get(field) + const astProp = ast.props[field] + const ops = astProp.ops + + // AND & OR + + if (isPropDef(prop)) { + if (prop.type === PropType.references) { + // references(astProp, ctx, prop) + } else if (prop.type === PropType.reference) { + // reference(astProp, ctx, prop) + } else if (ops) { + console.log({ ops }) + if (prop.id === 0) { + main.push({ prop, ops }) + } else { + for (const op of ops) { + const cond = createCondition(prop, op.op, op.val, op.opts) + ctx.query.set(cond, ctx.query.length) + } + // includeProp(ctx, prop, include) + } + } + } else { + if (prop) { + walk(astProp, ctx, typeDef, { + main, + tree: prop, + }) + } else { + // if EN, if NL + throw new Error(`Prop does not exist ${field}`) + } + } + } + return walkCtx +} + +export const filter = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef): number => { + const startIndex = ctx.query.length + // doink + + const { main } = walk(ast, ctx, typeDef, { + main: [], + tree: typeDef.tree, + }) + + console.log('collect dat main', main) + + for (const { prop, ops } of main) { + for (const op of ops) { + const cond = createCondition(prop, op.op, op.val, op.opts) + console.log(cond) + ctx.query.set(cond, ctx.query.length) + } + } + // includeMainProps(ctx, main, typeDef) + + return ctx.query.length - startIndex +} diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index 1821fe32df..59e1635ac3 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -1,4 +1,9 @@ -import { PropDef, TypeDef, isPropDef } from '../../schema/defs/index.js' +import { + PropDef, + PropTree, + TypeDef, + isPropDef, +} from '../../schema/defs/index.js' import { IncludeOp, MAIN_PROP, @@ -7,11 +12,16 @@ import { pushIncludePartialHeader, pushIncludePartialProp, } from '../../zigTsExports.js' -import { Ctx, Include, IncludeCtx, QueryAst } from './ast.js' +import { Ctx, Include, QueryAst } from './ast.js' import { references } from './multiple.js' import { readPropDef } from './readSchema.js' import { reference } from './single.js' +type WalkCtx = { + tree: PropTree + main: { prop: PropDef; include: Include }[] +} + const includeProp = (ctx: Ctx, prop: PropDef, include: Include) => { pushIncludeHeader(ctx.query, { op: IncludeOp.default, @@ -29,15 +39,12 @@ const includeMainProps = ( props.sort((a, b) => a.prop.start < b.prop.start ? -1 : a.prop.start === b.prop.start ? 0 : 1, ) + let i = 0 for (const { include, prop } of props) { - i += prop.size - ctx.readSchema.main.props[prop.start ?? 0] = readPropDef( - prop, - ctx.locales, - include, - ) + ctx.readSchema.main.props[i] = readPropDef(prop, ctx.locales, include) ctx.readSchema.main.len += prop.size + i += prop.size } if (props.length === typeDef.main.length) { pushIncludeHeader(ctx.query, { @@ -61,13 +68,8 @@ const includeMainProps = ( } } -export const collect = ( - ast: QueryAst, - ctx: Ctx, - typeDef: TypeDef, - includeCtx: IncludeCtx, -) => { - const { main, tree } = includeCtx +const walk = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { + const { main, tree } = walkCtx // if ast.include.glob === '*' include all from schema // same for ast.include.glob === '**' for (const field in ast.props) { @@ -88,7 +90,7 @@ export const collect = ( } } else { if (prop) { - collect(astProp, ctx, typeDef, { + walk(astProp, ctx, typeDef, { main, tree: prop, }) @@ -98,15 +100,15 @@ export const collect = ( } } } - return includeCtx + return walkCtx } export const include = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef): number => { - const includeStart = ctx.query.length - const { main } = collect(ast, ctx, typeDef, { + const startIndex = ctx.query.length + const { main } = walk(ast, ctx, typeDef, { main: [], tree: typeDef.tree, }) includeMainProps(ctx, main, typeDef) - return ctx.query.length - includeStart + return ctx.query.length - startIndex } diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index b9f6f2069f..6f0179342a 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -7,19 +7,20 @@ import { QUERY_ITERATOR_SEARCH_VEC, QueryIteratorTypeEnum, Order, + QueryHeader, } from '../../zigTsExports.js' import { QueryDef, QueryDefType } from '../../db-client/query/types.js' -export const getIteratorType = ( - edge: boolean, - edgeInclude: boolean, -): QueryIteratorTypeEnum => { - const hasFilter: boolean = false - const hasSearch = false //def.search?.size && def.search.size > 0 - const isVector = false // hasSearch && def.search!.isVector +export const getIteratorType = (header: QueryHeader): QueryIteratorTypeEnum => { + const hasFilter: boolean = header.filterSize != 0 + const edge: boolean = header.edgeTypeId != 0 + const edgeInclude: boolean = header.edgeSize != 0 + // const hasFilter = def.filter.size > 0 - const isDesc = false // def.order === Order.desc const hasSort = false // + const isDesc = false // def.order === Order.desc + const hasSearch = false //def.search?.size && def.search.size > 0 + const isVector = false // hasSearch && def.search!.isVector // def.sort && // (def.sort.prop !== ID_PROP || def.type === QueryDefType.References) // def.type === QueryDefType.References && diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 38967d14af..f786fa0837 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -5,9 +5,11 @@ import { QueryType, ID_PROP, writeQueryHeaderProps as props, - QueryHeaderByteSize, + QueryIteratorType, + readQueryHeader, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' +import { filter } from './filter/filter.js' import { include } from './include.js' import { getIteratorType } from './iteratorType.js' import { readPropDef, readSchema } from './readSchema.js' @@ -23,14 +25,29 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { limit: (ast.range?.end || 1000) + rangeStart, sort: false, filterSize: 0, + + // Lets remove all this from the header and make specific ones searchSize: 0, - iteratorType: getIteratorType(false, false), + iteratorType: QueryIteratorType.default, edgeTypeId: 0, edgeSize: 0, edgeFilterSize: 0, size: 0, }) + + if (ast.filter) { + const filterSize = filter(ast.filter, ctx, typeDef) + console.log({ filterSize }) + props.filterSize(ctx.query.data, filterSize, headerIndex) + } + props.includeSize(ctx.query.data, include(ast, ctx, typeDef), headerIndex) + + props.iteratorType( + ctx.query.data, + getIteratorType(readQueryHeader(ctx.query.data, headerIndex)), + headerIndex, + ) } export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { @@ -45,7 +62,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { sort: false, filterSize: 0, searchSize: 0, - iteratorType: getIteratorType(false, false), + iteratorType: QueryIteratorType.default, edgeTypeId: 0, edgeSize: 0, edgeFilterSize: 0, @@ -66,6 +83,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { }, prop.typeDef, ) + props.includeSize(ctx.query.data, size, headerIndex) if (ast.edges) { @@ -83,11 +101,13 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { }, edges, ) - props.iteratorType( - ctx.query.data, - getIteratorType(true, size > 0), - headerIndex, - ) + props.edgeSize(ctx.query.data, size, headerIndex) } + + props.iteratorType( + ctx.query.data, + getIteratorType(readQueryHeader(ctx.query.data, headerIndex)), + headerIndex, + ) } diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index 2ace77cb6c..7f333ad5df 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -7,6 +7,7 @@ import { } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { include } from './include.js' +import { getIteratorType } from './iteratorType.js' import { readPropDef, readSchema } from './readSchema.js' export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { diff --git a/src/db-query/ast/toCtx.ts b/src/db-query/ast/toCtx.ts index 5b1661e474..0ad6ea82ad 100644 --- a/src/db-query/ast/toCtx.ts +++ b/src/db-query/ast/toCtx.ts @@ -13,11 +13,11 @@ export const astToQueryCtx = ( schema: SchemaOut, ast: QueryAst, query: AutoSizedUint8Array, - sub: AutoSizedUint8Array, + // sub: AutoSizedUint8Array, maybe we can just check the query for subs + // PREPARE ): { query: Uint8Array readSchema: ReaderSchema - subscription: Uint8Array // make this optional ? } => { query.length = 0 @@ -36,7 +36,6 @@ export const astToQueryCtx = ( const ctx: Ctx = { query, - sub, readSchema: readSchema(), locales: getReaderLocales(schema), } @@ -52,6 +51,5 @@ export const astToQueryCtx = ( return { query: query.view.slice(), readSchema: ctx.readSchema, - subscription: new Uint8Array(0), } } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index f4c2ffc689..50d4df3012 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -3805,16 +3805,8 @@ export const FilterOpCompare = { nrange: 11, gt: 14, lt: 15, - gtBatch: 16, - ltBatch: 17, - gtBatchSmall: 18, - ltBatchSmall: 19, ge: 20, le: 21, - geBatch: 22, - leBatch: 23, - geBatchSmall: 24, - leBatchSmall: 25, selectLargeRefs: 203, selectRef: 204, selectSmallRefs: 205, @@ -3834,16 +3826,8 @@ export const FilterOpCompareInverse = { 11: 'nrange', 14: 'gt', 15: 'lt', - 16: 'gtBatch', - 17: 'ltBatch', - 18: 'gtBatchSmall', - 19: 'ltBatchSmall', 20: 'ge', 21: 'le', - 22: 'geBatch', - 23: 'leBatch', - 24: 'geBatchSmall', - 25: 'leBatchSmall', 203: 'selectLargeRefs', 204: 'selectRef', 205: 'selectSmallRefs', @@ -3863,16 +3847,8 @@ export const FilterOpCompareInverse = { nrange, gt, lt, - gtBatch, - ltBatch, - gtBatchSmall, - ltBatchSmall, ge, le, - geBatch, - leBatch, - geBatchSmall, - leBatchSmall, selectLargeRefs, selectRef, selectSmallRefs, diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index cff939d126..6d536436c2 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,6 +1,7 @@ import { deSerializeSchema } from '../../dist/protocol/db-read/schema/deserialize.js' import { convertToReaderSchema } from '../../src/db-client/query/queryDefToReadSchema.js' import { registerQuery } from '../../src/db-client/query/registerQuery.js' +import { QueryAst } from '../../src/db-query/ast/ast.js' import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { resultToObject, @@ -79,20 +80,23 @@ await test('include', async (t) => { // let d = Date.now() - const ast = { + const ast: QueryAst = { type: 'user', + filter: { + props: { + y: { ops: [{ op: '=', val: 67 }] }, + }, + }, props: { - // name: { include: {} }, - // y: { include: {} }, + name: { include: {} }, + y: { include: {} }, // x: { include: {} }, // cook: { // props: { // cookie: { include: {} }, // }, // }, - // // NOW ADD MR FRIEND! - // mrFriend: { // props: { // name: { include: {} }, @@ -103,26 +107,20 @@ await test('include', async (t) => { // }, // }, // }, - - friends: { - props: { - name: { include: {} }, - }, - edges: { - props: { - $level: { include: {} }, - }, - }, - }, + // friends: { + // props: { + // name: { include: {} }, + // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, + // }, }, } - const ctx = astToQueryCtx( - client.schema!, - ast, - new AutoSizedUint8Array(1000), - new AutoSizedUint8Array(1000), - ) + const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) // TODO // references From 82491da863775b83214f2c981dcb2332d05d21b2 Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 31 Jan 2026 08:15:24 +0100 Subject: [PATCH 049/449] update --- src/db-server/schema.ts | 24 ++++-------------------- src/schema/defs/getTypeDefs.ts | 2 +- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index f20c21f993..fe80708ea0 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -20,7 +20,7 @@ import { type SchemaOut, } from '../schema/index.js' import { SCHEMA_FILE } from '../index.js' -import { getTypeDefs } from '../schema/defs/getTypeDefs.js' +import { getTypeDefs, propIndexOffset } from '../schema/defs/getTypeDefs.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' const schemaOpId = idGenerator() @@ -114,25 +114,10 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { let nrVirtualFields = 0 for (const prop of typeDef.separate) { - if ( - 'default' in prop.schema && - prop.schema.default && - supportedDefaults.has(prop.type) - ) { - // TODO what is fixedFields exactly - // could we make a return type in the prop.pushSelvaSchema for this? + const offset = propIndexOffset(prop) + if (offset < 0) { nrFixedFields++ - } else if ( - prop.type === PropType.reference || - prop.type === PropType.references - ) { - nrFixedFields++ - } else if ( - prop.type === PropType.alias || - prop.type === PropType.aliases || - prop.type === PropType.colVec - ) { - // We assume that these are always the last props! + } else if (offset > 0) { nrVirtualFields++ } } @@ -163,7 +148,6 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { // handle separate for (const prop of typeDef.separate) { - // TODO put defaults! prop.pushSelvaSchema(buf) } diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index c2eafd4781..1b9352eff3 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -14,7 +14,7 @@ const mainSorter = (a, b) => { return 1 } -const propIndexOffset = (prop: PropDef) => { +export const propIndexOffset = (prop: PropDef): number => { switch (prop.type) { // We pack default on the beginning, for smallest possible mem case PropType.microBuffer: From fab444a611bf4f92ceb0a1a7ad0fac17884e864a Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sat, 31 Jan 2026 08:33:18 +0100 Subject: [PATCH 050/449] added write methods to prop classes --- src/db-query/ast/filter/condition.ts | 6 +- src/schema/defs/props/fixed.ts | 407 +++++++++++++++------------ src/schema/defs/props/references.ts | 338 ++++++++++++++++++++++ src/schema/defs/props/separate.ts | 97 ++++++- test/schema/props/validate.ts | 66 +++++ test/schema/props/write.ts | 144 ++++++++++ 6 files changed, 878 insertions(+), 180 deletions(-) create mode 100644 test/schema/props/validate.ts create mode 100644 test/schema/props/write.ts diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts index effe4c85e7..51b5ce7d89 100644 --- a/src/db-query/ast/filter/condition.ts +++ b/src/db-query/ast/filter/condition.ts @@ -18,7 +18,6 @@ export const conditionBuffer = ( const condition = new Uint8Array( size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propDef.size, ) - console.log('COND BUFFER', size, condition) condition[0] = 255 // Means condition header is not aligned const offset = writeFilterCondition( @@ -121,6 +120,8 @@ export const createCondition = ( } const writer = getPropWriter(propDef.type) + // this needs to use propDef.write + const { op, size, write } = getFilterOp( propDef, writer, @@ -128,6 +129,8 @@ export const createCondition = ( value.length, ) + // this is fixed make fixed and variable in a file + const vectorLen = 16 / size if (value.length == 1 || operator === '..' || operator == '!..') { @@ -145,6 +148,7 @@ export const createCondition = ( console.log('derp', condition) return condition } else if (value.length > vectorLen) { + // only relevant for eq and neq const { condition, offset } = conditionBuffer( { ...propDef, start: propDef.start || 0 }, value.length * size, diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index abc587a63f..5b3687c1bc 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -1,10 +1,96 @@ import type { EnumItem, SchemaEnum } from '../../../schema.js' import { convertToTimestamp } from '../../../utils/timestamp.js' +import { + writeDoubleLE, + writeInt64, + writeUint16, + writeUint32, +} from '../../../utils/index.js' import { PropType, type PropTypeEnum } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' +const validateNumber = (value: unknown, prop: any, path: string[]) => { + if (typeof value !== 'number') { + throw new Error('Invalid type for number ' + path.join('.')) + } + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${path.join('.')}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${path.join('.')}`, + ) + } + return value +} + +const validateTimestamp = (value: unknown, prop: any, path: string[]) => { + const ts = convertToTimestamp(value as any) + if (prop.min !== undefined && ts < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${path.join('.')}`, + ) + } + if (prop.max !== undefined && ts > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${path.join('.')}`, + ) + } + return ts +} + +const validateInteger = ( + value: unknown, + prop: any, + path: string[], + type: string, + min: number, + max: number, +) => { + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error(`Invalid type for ${type} ` + path.join('.')) + } + if (value < min || value > max) { + throw new Error(`Value out of range for ${type} ` + path.join('.')) + } + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${path.join('.')}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${path.join('.')}`, + ) + } + return value +} + +const validateEnum = ( + value: unknown, + vals: Map, + path: string[], +) => { + if (typeof value !== 'string' && typeof value !== 'number') { + throw new Error('Invalid type for enum ' + path.join('.')) + } + if (!vals.has(value)) { + throw new Error(`Invalid enum value ${value} for ${path.join('.')}`) + } + return vals.get(value) ?? 0 +} + +const validateBoolean = (value: unknown, path: string[]) => { + if (typeof value !== 'boolean') { + throw new Error('Invalid type for boolean ' + path.join('.')) + } + return value ? 1 : 0 +} + export const number = class Number extends BasePropDef { override type: PropTypeEnum = PropType.number override size = 8 @@ -12,25 +98,12 @@ export const number = class Number extends BasePropDef { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number') { - throw new Error('Invalid type for number ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushDoubleLE(value) + const val = validateNumber(value, this.schema, this.path) + buf.pushDoubleLE(val) + } + write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateNumber(value, this.schema, this.path) + writeDoubleLE(buf, val, offset) } } @@ -40,24 +113,13 @@ export const timestamp = class Timestamp extends number { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number | string { - const ts = convertToTimestamp(value as any) - const prop = this.schema as any - if (prop.min !== undefined && ts < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && ts > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } + const ts = validateTimestamp(value, this.schema, this.path) buf.pushInt64(ts) } + override write(buf: Uint8Array, value: unknown, offset: number) { + const ts = validateTimestamp(value, this.schema, this.path) + writeInt64(buf, ts, offset) + } } export const uint8 = class Uint8 extends BasePropDef { @@ -67,28 +129,26 @@ export const uint8 = class Uint8 extends BasePropDef { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for uint8 ' + this.path.join('.')) - } - if (value < 0 || value > 255) { - throw new Error('Value out of range for uint8 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint8(value) + const val = validateInteger( + value, + this.schema, + this.path, + 'uint8', + 0, + 255, + ) as number + buf.pushUint8(val) + } + write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateInteger( + value, + this.schema, + this.path, + 'uint8', + 0, + 255, + ) as number + buf[offset] = val } } @@ -98,28 +158,26 @@ export const int8 = class Int8 extends uint8 { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for int8 ' + this.path.join('.')) - } - if (value < -128 || value > 127) { - throw new Error('Value out of range for int8 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint8(value) + const val = validateInteger( + value, + this.schema, + this.path, + 'int8', + -128, + 127, + ) as number + buf.pushUint8(val) + } + override write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateInteger( + value, + this.schema, + this.path, + 'int8', + -128, + 127, + ) as number + buf[offset] = val } } @@ -130,28 +188,26 @@ export const uint16 = class Uint16 extends BasePropDef { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for uint16 ' + this.path.join('.')) - } - if (value < 0 || value > 65535) { - throw new Error('Value out of range for uint16 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint16(value) + const val = validateInteger( + value, + this.schema, + this.path, + 'uint16', + 0, + 65535, + ) as number + buf.pushUint16(val) + } + write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateInteger( + value, + this.schema, + this.path, + 'uint16', + 0, + 65535, + ) as number + writeUint16(buf, val, offset) } } @@ -161,28 +217,26 @@ export const int16 = class Int16 extends uint16 { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for int16 ' + this.path.join('.')) - } - if (value < -32768 || value > 32767) { - throw new Error('Value out of range for int16 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint16(value) + const val = validateInteger( + value, + this.schema, + this.path, + 'int16', + -32768, + 32767, + ) as number + buf.pushUint16(val) + } + override write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateInteger( + value, + this.schema, + this.path, + 'int16', + -32768, + 32767, + ) as number + writeUint16(buf, val, offset) } } @@ -193,28 +247,26 @@ export const uint32 = class Uint32 extends BasePropDef { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for uint32 ' + this.path.join('.')) - } - if (value < 0 || value > 4294967295) { - throw new Error('Value out of range for uint32 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint32(value) + const val = validateInteger( + value, + this.schema, + this.path, + 'uint32', + 0, + 4294967295, + ) as number + buf.pushUint32(val) + } + write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateInteger( + value, + this.schema, + this.path, + 'uint32', + 0, + 4294967295, + ) as number + writeUint32(buf, val, offset) } } @@ -224,28 +276,26 @@ export const int32 = class Int32 extends uint32 { buf: AutoSizedUint8Array, value: unknown, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for int32 ' + this.path.join('.')) - } - if (value < -2147483648 || value > 2147483647) { - throw new Error('Value out of range for int32 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint32(value) + const val = validateInteger( + value, + this.schema, + this.path, + 'int32', + -2147483648, + 2147483647, + ) as number + buf.pushUint32(val) + } + override write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateInteger( + value, + this.schema, + this.path, + 'int32', + -2147483648, + 2147483647, + ) as number + writeUint32(buf, val, offset) } } @@ -266,13 +316,12 @@ export const enum_ = class Enum extends uint8 { buf: AutoSizedUint8Array, value: unknown, ): asserts value is EnumItem { - if (typeof value !== 'string' && typeof value !== 'number') { - throw new Error('Invalid type for enum ' + this.path.join('.')) - } - if (!this.vals.has(value)) { - throw new Error(`Invalid enum value ${value} for ${this.path.join('.')}`) - } - buf.pushUint8(this.vals.get(value) ?? 0) + const val = validateEnum(value, this.vals, this.path) + buf.pushUint8(val) + } + override write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateEnum(value as any, this.vals, this.path) + buf[offset] = val } } @@ -283,9 +332,11 @@ export const boolean = class Boolean extends BasePropDef { buf: AutoSizedUint8Array, value: unknown, ): asserts value is boolean { - if (typeof value !== 'boolean') { - throw new Error('Invalid type for boolean ' + this.path.join('.')) - } - buf.pushUint8(value ? 1 : 0) + const val = validateBoolean(value, this.path) + buf.pushUint8(val) + } + write(buf: Uint8Array, value: unknown, offset: number) { + const val = validateBoolean(value, this.path) + buf[offset] = val } } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 79725ebec5..0756f3ede6 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -7,13 +7,17 @@ import { pushModifyReferencesHeader, pushModifyReferencesMetaHeader, pushSelvaSchemaRef, + writeModifyReferenceMetaHeader, writeModifyReferenceMetaHeaderProps, + writeModifyReferencesHeader, writeModifyReferencesHeaderProps, + writeModifyReferencesMetaHeader, writeModifyReferencesMetaHeaderProps, type LangCodeEnum, type ModifyEnum, type PropTypeEnum, } from '../../../zigTsExports.js' +import { writeUint32 } from '../../../utils/index.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { SchemaProp, @@ -124,6 +128,103 @@ const serializeIdsAndMeta = ( return i } +const writeIds = ( + buf: Uint8Array, + ids: number[], + valueIndex: number, + bufOffset: number, +): { index: number; written: number } => { + let i = valueIndex + let written = 0 + writeUint32(buf, 0, bufOffset) + bufOffset += 4 + written += 4 + for (; i < ids.length; i++) { + const id = getRealId(ids[i]) + if (!id) break + writeUint32(buf, id, bufOffset) + bufOffset += 4 + written += 4 + } + return { index: i, written } +} + +const writeTmpIds = ( + buf: Uint8Array, + items: BasedModify[], + valueIndex: number, + bufOffset: number, +): { index: number; written: number } => { + let i = valueIndex + let written = 0 + writeUint32(buf, 0, bufOffset) + bufOffset += 4 + written += 4 + for (; i < items.length; i++) { + const tmpId = getTmpId(items[i]) + if (tmpId === undefined) break + writeUint32(buf, tmpId, bufOffset) + bufOffset += 4 + written += 4 + } + return { index: i, written } +} + +const writeIdsAndMeta = ( + buf: Uint8Array, + items: any[], + op: ModifyEnum, + valueIndex: number, + lang: LangCodeEnum, + bufOffset: number, + edgesType?: TypeDef, +): { index: number; written: number } => { + let i = valueIndex + let written = 0 + const start = bufOffset + bufOffset += 4 + written += 4 + + for (; i < items.length; i++) { + const item = items[i] + if (!isValidRefObj(item)) { + break + } + const realId = getRealId(item.id) + const id = realId || getTmpId(item.id) + if (id === undefined) { + break + } + const headerSize = writeModifyReferencesMetaHeader( + buf, + { + id: id, + isTmp: !realId, + withIndex: '$index' in item, + index: item.$index, + size: 0, + }, + bufOffset, + ) + const sizeAfterHeader = headerSize - bufOffset + const index = bufOffset + bufOffset += sizeAfterHeader + written += sizeAfterHeader + + if (edgesType) { + const edges = getEdges(item) + if (edges) { + // TODO: Edges writing not supported in raw write + } + } + } + + // store the amount of refs aka size + writeUint32(buf, i - valueIndex, start) + + return { index: i, written } +} + const isValidRefObj = (item: any) => { if (typeof item === 'object' && item !== null) { return getRealId(item.id) || getTmpId(item.id) !== undefined @@ -175,6 +276,82 @@ const setReferences = ( } } +const setReferencesWrite = ( + buf: Uint8Array, + value: any[], + prop: PropDef, + op: ModifyEnum, + lang: LangCodeEnum, + bufOffset: number, +): number => { + let offset = 0 + const len = value.length + while (offset < len) { + const item = value[offset] + if (getRealId(item)) { + const index = bufOffset + bufOffset = writeModifyReferencesHeader( + buf, + { + op: ModifyReferences.ids, + size: 0, + }, + bufOffset, + ) + const start = bufOffset + const res = writeIds(buf, value, offset, bufOffset) + offset = res.index + bufOffset += res.written + writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) + } else if (getTmpId(item) !== undefined) { + const index = bufOffset + bufOffset = writeModifyReferencesHeader( + buf, + { + op: ModifyReferences.tmpIds, + size: 0, + }, + bufOffset, + ) + const start = bufOffset + const res = writeTmpIds(buf, value, offset, bufOffset) + offset = res.index + bufOffset += res.written + writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) + } else if (isValidRefObj(item)) { + const index = bufOffset + bufOffset = writeModifyReferencesHeader( + buf, + { + op: ModifyReferences.idsWithMeta, + size: 0, + }, + bufOffset, + ) + const start = bufOffset + const res = writeIdsAndMeta( + buf, + value, + op, + offset, + lang, + bufOffset, + prop.edges, + ) + offset = res.index + bufOffset += res.written + writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) + } else if (item instanceof BasedModify) { + throw item + } else if (typeof item === 'object' && item?.id instanceof BasedModify) { + throw item.id + } else { + throw 'bad ref!' + } + } + return bufOffset +} + const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { let offset = 0 while (offset < value.length) { @@ -203,6 +380,53 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { } } +const deleteReferencesWrite = ( + buf: Uint8Array, + value: any[], + bufOffset: number, +): number => { + let offset = 0 + while (offset < value.length) { + const item = value[offset] + if (getRealId(item)) { + const index = bufOffset + bufOffset = writeModifyReferencesHeader( + buf, + { + op: ModifyReferences.delIds, + size: 0, + }, + bufOffset, + ) + const start = bufOffset + const res = writeIds(buf, value, offset, bufOffset) + offset = res.index + bufOffset += res.written + writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) + } else if (getTmpId(item) !== undefined) { + const index = bufOffset + bufOffset = writeModifyReferencesHeader( + buf, + { + op: ModifyReferences.delTmpIds, + size: 0, + }, + bufOffset, + ) + const start = bufOffset + const res = writeTmpIds(buf, value, offset, bufOffset) + offset = res.index + bufOffset += res.written + writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) + } else if (item instanceof BasedModify) { + throw item + } else { + throw 'bad ref' + } + } + return bufOffset +} + export const references = class References extends BasePropDef { override type: PropTypeEnum = PropType.references declare schema: SchemaReferences @@ -241,6 +465,44 @@ export const references = class References extends BasePropDef { deleteReferences(buf, val.delete) } } + write( + buf: Uint8Array, + value: unknown, + offset: number, + op: ModifyEnum, + lang: LangCodeEnum, + ) { + if (typeof value !== 'object' || value === null) { + throw new Error('References value must be an object and not null') + } + + const val = value as { + add?: any[] + update?: any[] + delete?: any[] + } + + if (Array.isArray(value)) { + // In write() we do NOT clear? + // pushValue clears if Modify.update. + // We should replicate logic. + if (op === Modify.update) { + // buf.push(ModifyReferences.clear) + buf[offset] = ModifyReferences.clear + offset += 1 + } + offset = setReferencesWrite(buf, value, this, op, lang, offset) + } + if (val.add) { + offset = setReferencesWrite(buf, val.add, this, op, lang, offset) + } + if (val.update) { + offset = setReferencesWrite(buf, val.update, this, op, lang, offset) + } + if (val.delete) { + offset = deleteReferencesWrite(buf, val.delete, offset) + } + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaRef(buf, { type: PropTypeSelva.references, @@ -319,6 +581,82 @@ export const reference = class Reference extends BasePropDef { } } } + write( + buf: Uint8Array, + value: unknown, + offset: number, + lang: LangCodeEnum, + op: ModifyEnum, + ) { + const id = getRealId(value) + if (id) { + writeModifyReferenceMetaHeader( + buf, + { + id, + isTmp: false, + size: 0, + }, + offset, + ) + return + } + const tmpId = getTmpId(value) + if (tmpId !== undefined) { + writeModifyReferenceMetaHeader( + buf, + { + id: tmpId, + isTmp: true, + size: 0, + }, + offset, + ) + return + } + + if (value instanceof BasedModify) { + throw value + } + + if (typeof value === 'object' && value !== null) { + const val = value as { id: any } + const realId = getRealId(val.id) + const id = realId || getTmpId(val.id) + if (id !== undefined) { + const index = offset + offset = writeModifyReferenceMetaHeader( + buf, + { + id, + isTmp: !realId, + size: 0, + }, + offset, + ) + const start = offset + + const prop: PropDef = this + if (prop.edges) { + const edges = getEdges(val) + if (edges) { + // TODO: Edges writing + // serializeProps(prop.edges.tree, edges, buf, op, lang) + // writeModifyReferenceMetaHeaderProps.size( + // buf, + // offset - start, + // index, + // ) + } + } + return + } + + if (val.id instanceof BasedModify) { + throw val.id + } + } + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaRef(buf, { type: PropTypeSelva.reference, diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 00e7a5b53b..970180dfcd 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -12,12 +12,13 @@ import { type LangCodeEnum, type PropTypeEnum, PropTypeSelva, - type PropTypeSelvaEnum, pushSelvaSchemaColvec, pushSelvaSchemaMicroBuffer, pushSelvaSchemaString, pushSelvaSchemaText, + writeModifyCardinalityHeader, } from '../../../zigTsExports.js' +import { writeUint32 } from '../../../utils/index.js' import { xxHash64 } from '../../../db-client/xxHash64.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' @@ -80,6 +81,42 @@ export const string = class String extends BasePropDef { const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } + write(buf: Uint8Array, val: unknown, offset: number, lang: LangCodeEnum) { + if (typeof val !== 'string') { + throw new Error('Invalid type for string ' + this.path.join('.')) + } + const prop = this.schema as SchemaString + if (prop.min !== undefined && val.length < prop.min) { + throw new Error( + `Length ${val.length} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && val.length > prop.max) { + throw new Error( + `Length ${val.length} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + const normalized = val.normalize('NFKD') + buf[offset] = lang + buf[offset + 1] = NOT_COMPRESSED + const { written } = ENCODER.encodeInto(normalized, buf.subarray(offset + 2)) + + if (prop.maxBytes !== undefined) { + if (written > prop.maxBytes) { + throw new Error( + `Byte length ${written} is larger than maxBytes ${ + prop.maxBytes + } for ${this.path.join('.')}`, + ) + } + } + const crc = native.crc32(buf.subarray(offset + 2, offset + 2 + written)) + writeUint32(buf, crc, offset + 2 + written) + } pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { @@ -113,6 +150,17 @@ export const json = class Json extends string { } super.pushValue(buf, JSON.stringify(value), lang) } + override write( + buf: Uint8Array, + value: unknown, + offset: number, + lang: LangCodeEnum, + ) { + if (value === undefined) { + throw new Error('Invalid undefined value for json ' + this.path.join('.')) + } + super.write(buf, JSON.stringify(value), offset, lang) + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -141,6 +189,20 @@ export const binary = class Binary extends BasePropDef { } buf.set(value, buf.length) } + write(buf: Uint8Array, value: unknown, offset: number) { + if (!(value instanceof Uint8Array)) { + throw new Error('Invalid type for binary ' + this.path.join('.')) + } + const prop = this.schema as SchemaString + if (prop.maxBytes !== undefined && value.byteLength > prop.maxBytes) { + throw new Error( + `Byte length ${value.byteLength} is larger than maxBytes ${ + prop.maxBytes + } for ${this.path.join('.')}`, + ) + } + buf.set(value, offset) + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -202,6 +264,33 @@ export const cardinality = class Cardinality extends BasePropDef { } } } + write(buf: Uint8Array, value: unknown, offset: number) { + if (value instanceof Uint8Array && value.byteLength !== 8) { + throw new Error('unhandled error cardi') + } + + if (!Array.isArray(value)) { + value = [value] + } + + const items = value as any[] + + if (items.length === 0) return + + offset = writeModifyCardinalityHeader(buf, this, offset) + + for (const item of items) { + if (typeof item === 'string') { + xxHash64(ENCODER.encode(item), buf, offset) + offset += 8 + } else if (item instanceof Uint8Array && item.byteLength === 8) { + buf.set(item, offset) + offset += 8 + } else { + throw new Error('Invalid value for cardinality ' + this.path.join('.')) + } + } + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -224,6 +313,9 @@ export const vector = class Vector extends BasePropDef { ): asserts value is any { throw new Error('Serialize vector not implemented') } + write(buf: Uint8Array, value: unknown, offset: number) { + throw new Error('Serialize vector not implemented') + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaMicroBuffer(buf, { type: PropTypeSelva.colVec, @@ -248,6 +340,9 @@ export const colvec = class ColVec extends BasePropDef { ): asserts value is any { throw new Error('Serialize colvec not implemented') } + write(buf: Uint8Array, value: unknown, offset: number) { + throw new Error('Serialize colvec not implemented') + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaColvec(buf, { type: PropTypeSelva.colVec, diff --git a/test/schema/props/validate.ts b/test/schema/props/validate.ts new file mode 100644 index 0000000000..8878d123b2 --- /dev/null +++ b/test/schema/props/validate.ts @@ -0,0 +1,66 @@ +import test from '../../shared/test.js' +import { + number, + uint8, + int8, + enum_ as enumProp, + boolean, +} from '../../../src/schema/defs/props/fixed.js' +import { AutoSizedUint8Array } from '../../../src/utils/AutoSizedUint8Array.js' + +const assertThrows = (fn: () => void, re: RegExp) => { + try { + fn() + } catch (e) { + if (re.test(e.message)) return + throw new Error(`Expected error matching ${re}, got "${e.message}"`) + } + throw new Error(`Expected error matching ${re}, but function did not throw`) +} + +await test('Fixed props validation: throws on invalid input', async (t) => { + const autoBuf = new AutoSizedUint8Array() + const writeBuf = new Uint8Array(10) + + // Number validation + // @ts-ignore + const numProp = new number({ type: 'number', min: 10, max: 20 }, ['test'], {}) + + assertThrows(() => numProp.pushValue(autoBuf, 5), /smaller than min/) + assertThrows(() => numProp.pushValue(autoBuf, 25), /larger than max/) + assertThrows(() => numProp.pushValue(autoBuf, 'string'), /Invalid type/) + + assertThrows(() => numProp.write(writeBuf, 5, 0), /smaller than min/) + assertThrows(() => numProp.write(writeBuf, 25, 0), /larger than max/) + assertThrows(() => numProp.write(writeBuf, 'string', 0), /Invalid type/) + + // Uint8 validation + // @ts-ignore + const u8Prop = new uint8({ type: 'uint8' }, ['test'], {}) + assertThrows(() => u8Prop.pushValue(autoBuf, 256), /Value out of range/) + assertThrows(() => u8Prop.pushValue(autoBuf, -1), /Value out of range/) + + assertThrows(() => u8Prop.write(writeBuf, 256, 0), /Value out of range/) + assertThrows(() => u8Prop.write(writeBuf, -1, 0), /Value out of range/) + + // Int8 validation + // @ts-ignore + const i8Prop = new int8({ type: 'int8' }, ['test'], {}) + assertThrows(() => i8Prop.pushValue(autoBuf, 128), /Value out of range/) + assertThrows(() => i8Prop.pushValue(autoBuf, -129), /Value out of range/) + + assertThrows(() => i8Prop.write(writeBuf, 128, 0), /Value out of range/) + assertThrows(() => i8Prop.write(writeBuf, -129, 0), /Value out of range/) + + // Enum validation + // @ts-ignore + const enProp = new enumProp({ type: 'enum', enum: ['a', 'b'] }, ['test'], {}) + assertThrows(() => enProp.pushValue(autoBuf, 'c'), /Invalid enum value/) + assertThrows(() => enProp.write(writeBuf, 'c', 0), /Invalid enum value/) + + // Boolean validation + // @ts-ignore + const boolProp = new boolean({ type: 'boolean' }, ['test'], {}) + assertThrows(() => boolProp.pushValue(autoBuf, 123), /Invalid type/) + assertThrows(() => boolProp.write(writeBuf, 123, 0), /Invalid type/) +}) diff --git a/test/schema/props/write.ts b/test/schema/props/write.ts new file mode 100644 index 0000000000..3cc99fdff7 --- /dev/null +++ b/test/schema/props/write.ts @@ -0,0 +1,144 @@ +import test from '../../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { + number, + timestamp, + uint8, + int8, + uint16, + int16, + uint32, + int32, + enum_ as enumProp, + boolean, +} from '../../../src/schema/defs/props/fixed.js' +import { + string, + json, + binary, + cardinality, +} from '../../../src/schema/defs/props/separate.js' +import { + references, + reference, +} from '../../../src/schema/defs/props/references.js' +import { AutoSizedUint8Array } from '../../../src/utils/AutoSizedUint8Array.js' +import { LangCode, Modify } from '../../../src/zigTsExports.js' + +await test('Fixed props: write matches pushValue', async (t) => { + const cases = [ + { Ctor: number, schema: { type: 'number' }, value: 123.456 }, + { Ctor: timestamp, schema: { type: 'timestamp' }, value: Date.now() }, + { Ctor: uint8, schema: { type: 'uint8' }, value: 123 }, + { Ctor: int8, schema: { type: 'int8' }, value: -12 }, + { Ctor: uint16, schema: { type: 'uint16' }, value: 12345 }, + { Ctor: int16, schema: { type: 'int16' }, value: -12345 }, + { Ctor: uint32, schema: { type: 'uint32' }, value: 12345678 }, + { Ctor: int32, schema: { type: 'int32' }, value: -12345678 }, + { Ctor: enumProp, schema: { type: 'enum', enum: ['a', 'b'] }, value: 0 }, // enum index + { Ctor: boolean, schema: { type: 'boolean' }, value: true }, + ] + + for (const { Ctor, schema, value } of cases) { + // @ts-ignore + const prop = new Ctor(schema, ['test'], {}) + const autoBuf = new AutoSizedUint8Array() + try { + prop.pushValue(autoBuf, value) + } catch (e) { + if (Ctor === enumProp) { + prop.pushValue(autoBuf, 'a') + } else { + throw e + } + } + + const pushResult = new Uint8Array(autoBuf.data.subarray(0, autoBuf.length)) + + const writeBuf = new Uint8Array(pushResult.length + 10) + const offset = 2 + // @ts-ignore + if (Ctor === enumProp) { + prop.write(writeBuf, 'a', offset) + } else { + prop.write(writeBuf, value, offset) + } + + const writeResult = writeBuf.subarray(offset, offset + pushResult.length) + + deepEqual(writeResult, pushResult, `Mismatch for ${schema.type}`) + } +}) + +await test('Separate props: write matches pushValue', async (t) => { + const cases = [ + { + Ctor: string, + schema: { type: 'string' }, + value: 'hello world', + lang: LangCode.en, + }, + { + Ctor: json, + schema: { type: 'json' }, + value: { foo: 'bar' }, + lang: LangCode.en, + }, + { + Ctor: binary, + schema: { type: 'binary' }, + value: new Uint8Array([1, 2, 3]), + lang: LangCode.en, + }, + // Cardinality + { + Ctor: cardinality, + schema: { type: 'cardinality' }, + value: ['a', 'b'], + lang: LangCode.en, + }, + ] + + for (const { Ctor, schema, value, lang } of cases) { + // @ts-ignore + const prop = new Ctor(schema, ['test'], {}) + const autoBuf = new AutoSizedUint8Array() + prop.pushValue(autoBuf, value, lang) + + const pushResult = new Uint8Array(autoBuf.data.subarray(0, autoBuf.length)) + + const writeBuf = new Uint8Array(pushResult.length + 10) + const offset = 5 + // @ts-ignore + prop.write(writeBuf, value, offset, lang) + + const writeResult = writeBuf.subarray(offset, offset + pushResult.length) + + deepEqual(writeResult, pushResult, `Mismatch for ${schema.type}`) + } +}) + +await test('References props: write matches pushValue', async (t) => { + const refsValue = [1000, 2000] + + // @ts-ignore + const prop = new references({ type: 'references' }, ['test'], {}) + const autoBuf = new AutoSizedUint8Array() + + // Modify.update + const op = Modify.update + + // @ts-ignore + prop.pushValue(autoBuf, refsValue, op, LangCode.en) + + const pushResult = new Uint8Array(autoBuf.data.subarray(0, autoBuf.length)) + + const writeBuf = new Uint8Array(pushResult.length + 100) + const offset = 10 + + prop.write(writeBuf, refsValue, offset, op, LangCode.en) + + const writeResult = writeBuf.subarray(offset, offset + pushResult.length) + + deepEqual(writeResult, pushResult, 'Mismatch for references') +}) From 4f2eadc3669c8a2e490534f2f8cd8645850bf0b7 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sat, 31 Jan 2026 09:03:24 +0100 Subject: [PATCH 051/449] propDef write --- src/db-query/ast/filter/condition.ts | 24 +++--- src/schema/defs/index.ts | 11 ++- src/schema/defs/props/base.ts | 11 ++- src/schema/defs/props/fixed.ts | 107 ++++++++++++++++++++++++--- src/schema/defs/props/references.ts | 18 ++--- src/schema/defs/props/separate.ts | 66 ++++++++++++++--- test/schema/props/write.ts | 6 +- 7 files changed, 194 insertions(+), 49 deletions(-) diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts index 51b5ce7d89..2d973bbaf5 100644 --- a/src/db-query/ast/filter/condition.ts +++ b/src/db-query/ast/filter/condition.ts @@ -1,4 +1,3 @@ -import { getPropWriter } from '../../../schema/def/utils.js' import { PropDef } from '../../../schema/defs/index.js' import { FilterConditionByteSize, @@ -6,6 +5,8 @@ import { FilterOp, FilterOpCompare, writeFilterCondition, + ModifyEnum, + LangCodeEnum, } from '../../../zigTsExports.js' import { FilterOpts, Operator } from '../ast.js' @@ -47,13 +48,12 @@ const opMap: Partial> = { const getFilterOp = ( propDef: PropDef, - write: ReturnType, operator: Operator, size: number, ): { size: number op: FilterOp - write: ReturnType + write: (buf: Uint8Array, val: any, offset: number) => void } => { const opName = opMap[operator] @@ -61,6 +61,10 @@ const getFilterOp = ( throw new Error(`un supported op ${operator}`) } + const write = (buf: Uint8Array, val: any, offset: number) => { + propDef.write(buf, val, offset) + } + if ((opName === 'eq' || opName === 'neq') && size > 1) { const vectorLen = 16 / propDef.size if (size > vectorLen) { @@ -92,8 +96,8 @@ const getFilterOp = ( write: (condition: Uint8Array, v: any, offset: number) => { // x >= 3 && x <= 11 // (x -% 3) <= (11 - 3) - write(condition, v[0], offset) - write(condition, v[1] - v[0], offset + propDef.size) + propDef.write(condition, v[0], offset) + propDef.write(condition, v[1] - v[0], offset + propDef.size) return condition }, } @@ -119,15 +123,7 @@ export const createCondition = ( value = [value] } - const writer = getPropWriter(propDef.type) - // this needs to use propDef.write - - const { op, size, write } = getFilterOp( - propDef, - writer, - operator, - value.length, - ) + const { op, size, write } = getFilterOp(propDef, operator, value.length) // this is fixed make fixed and variable in a file diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 4410036e8f..0fd2962d9b 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -36,8 +36,17 @@ export type PropDef = { buf: AutoSizedUint8Array, value: unknown, op: ModifyEnum, - lang: LangCodeEnum, + lang?: LangCodeEnum, ): void + + write( + buf: Uint8Array, + val: any, + offset: number, + op?: ModifyEnum, + lang?: LangCodeEnum, + ): void + pushSelvaSchema(buf: AutoSizedUint8Array): void } diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 23d404d404..34200a2885 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -27,8 +27,17 @@ export class BasePropDef implements PropDef { pushValue( buf: AutoSizedUint8Array, value: unknown, - lang: LangCodeEnum, op: ModifyEnum, + lang: LangCodeEnum, + ): void { + // To be implemented by subclasses + } + write( + buf: Uint8Array, + value: unknown, + offset: number, + op?: ModifyEnum, + lang?: LangCodeEnum, ): void { // To be implemented by subclasses } diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 5b3687c1bc..aae5155f53 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -6,7 +6,12 @@ import { writeUint16, writeUint32, } from '../../../utils/index.js' -import { PropType, type PropTypeEnum } from '../../../zigTsExports.js' +import { + PropType, + type PropTypeEnum, + type ModifyEnum, + type LangCodeEnum, +} from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' @@ -97,11 +102,19 @@ export const number = class Number extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateNumber(value, this.schema, this.path) buf.pushDoubleLE(val) } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateNumber(value, this.schema, this.path) writeDoubleLE(buf, val, offset) } @@ -112,11 +125,19 @@ export const timestamp = class Timestamp extends number { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number | string { const ts = validateTimestamp(value, this.schema, this.path) buf.pushInt64(ts) } - override write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const ts = validateTimestamp(value, this.schema, this.path) writeInt64(buf, ts, offset) } @@ -128,6 +149,8 @@ export const uint8 = class Uint8 extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateInteger( value, @@ -139,7 +162,13 @@ export const uint8 = class Uint8 extends BasePropDef { ) as number buf.pushUint8(val) } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateInteger( value, this.schema, @@ -157,6 +186,8 @@ export const int8 = class Int8 extends uint8 { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateInteger( value, @@ -168,7 +199,13 @@ export const int8 = class Int8 extends uint8 { ) as number buf.pushUint8(val) } - override write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateInteger( value, this.schema, @@ -187,6 +224,8 @@ export const uint16 = class Uint16 extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateInteger( value, @@ -198,7 +237,13 @@ export const uint16 = class Uint16 extends BasePropDef { ) as number buf.pushUint16(val) } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateInteger( value, this.schema, @@ -216,6 +261,8 @@ export const int16 = class Int16 extends uint16 { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateInteger( value, @@ -227,7 +274,13 @@ export const int16 = class Int16 extends uint16 { ) as number buf.pushUint16(val) } - override write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateInteger( value, this.schema, @@ -246,6 +299,8 @@ export const uint32 = class Uint32 extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateInteger( value, @@ -257,7 +312,13 @@ export const uint32 = class Uint32 extends BasePropDef { ) as number buf.pushUint32(val) } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateInteger( value, this.schema, @@ -275,6 +336,8 @@ export const int32 = class Int32 extends uint32 { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is number { const val = validateInteger( value, @@ -286,7 +349,13 @@ export const int32 = class Int32 extends uint32 { ) as number buf.pushUint32(val) } - override write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateInteger( value, this.schema, @@ -315,11 +384,19 @@ export const enum_ = class Enum extends uint8 { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op: ModifyEnum, + _lang: LangCodeEnum, ): asserts value is EnumItem { const val = validateEnum(value, this.vals, this.path) buf.pushUint8(val) } - override write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateEnum(value as any, this.vals, this.path) buf[offset] = val } @@ -331,11 +408,19 @@ export const boolean = class Boolean extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op: ModifyEnum, + _lang: LangCodeEnum, ): asserts value is boolean { const val = validateBoolean(value, this.path) buf.pushUint8(val) } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { const val = validateBoolean(value, this.path) buf[offset] = val } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 0756f3ede6..7865647d27 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -234,7 +234,7 @@ const isValidRefObj = (item: any) => { const setReferences = ( buf: AutoSizedUint8Array, value: any[], - prop: PropDef, + prop: BasePropDef & { edges?: TypeDef }, op: ModifyEnum, lang: LangCodeEnum, ) => { @@ -279,7 +279,7 @@ const setReferences = ( const setReferencesWrite = ( buf: Uint8Array, value: any[], - prop: PropDef, + prop: BasePropDef & { edges?: TypeDef }, op: ModifyEnum, lang: LangCodeEnum, bufOffset: number, @@ -465,7 +465,7 @@ export const references = class References extends BasePropDef { deleteReferences(buf, val.delete) } } - write( + override write( buf: Uint8Array, value: unknown, offset: number, @@ -524,8 +524,8 @@ export const reference = class Reference extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, - lang: LangCodeEnum, op: ModifyEnum, + lang: LangCodeEnum, ): asserts value is any { const id = getRealId(value) if (id) { @@ -560,7 +560,7 @@ export const reference = class Reference extends BasePropDef { isTmp: !realId, size: 0, }) - const prop: PropDef = this + const prop = this if (prop.edges) { const edges = getEdges(val) if (edges) { @@ -581,12 +581,12 @@ export const reference = class Reference extends BasePropDef { } } } - write( + override write( buf: Uint8Array, value: unknown, offset: number, - lang: LangCodeEnum, op: ModifyEnum, + lang: LangCodeEnum, ) { const id = getRealId(value) if (id) { @@ -635,9 +635,7 @@ export const reference = class Reference extends BasePropDef { offset, ) const start = offset - - const prop: PropDef = this - if (prop.edges) { + if (this.edges) { const edges = getEdges(val) if (edges) { // TODO: Edges writing diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 970180dfcd..63c632a37f 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -11,12 +11,14 @@ import { PropType, type LangCodeEnum, type PropTypeEnum, + type ModifyEnum, PropTypeSelva, pushSelvaSchemaColvec, pushSelvaSchemaMicroBuffer, pushSelvaSchemaString, pushSelvaSchemaText, writeModifyCardinalityHeader, + LangCode, } from '../../../zigTsExports.js' import { writeUint32 } from '../../../utils/index.js' import { xxHash64 } from '../../../db-client/xxHash64.js' @@ -42,7 +44,8 @@ export const string = class String extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, val: unknown, - lang: LangCodeEnum, + _op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, ): asserts val is string { if (typeof val !== 'string') { throw new Error('Invalid type for string ' + this.path.join('.')) @@ -81,7 +84,13 @@ export const string = class String extends BasePropDef { const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } - write(buf: Uint8Array, val: unknown, offset: number, lang: LangCodeEnum) { + override write( + buf: Uint8Array, + val: unknown, + offset: number, + _op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, + ): asserts val is string { if (typeof val !== 'string') { throw new Error('Invalid type for string ' + this.path.join('.')) } @@ -118,6 +127,7 @@ export const string = class String extends BasePropDef { writeUint32(buf, crc, offset + 2 + written) } pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} + override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -143,23 +153,25 @@ export const json = class Json extends string { override pushValue( buf: AutoSizedUint8Array, value: unknown, - lang: LangCodeEnum, + op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, ) { if (value === undefined) { throw new Error('Invalid undefined value for json ' + this.path.join('.')) } - super.pushValue(buf, JSON.stringify(value), lang) + super.pushValue(buf, JSON.stringify(value), op, lang) } override write( buf: Uint8Array, value: unknown, offset: number, - lang: LangCodeEnum, + op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, ) { if (value === undefined) { throw new Error('Invalid undefined value for json ' + this.path.join('.')) } - super.write(buf, JSON.stringify(value), offset, lang) + super.write(buf, JSON.stringify(value), offset, op, lang) } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { @@ -175,6 +187,8 @@ export const binary = class Binary extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is Uint8Array { if (!(value instanceof Uint8Array)) { throw new Error('Invalid type for binary ' + this.path.join('.')) @@ -189,7 +203,13 @@ export const binary = class Binary extends BasePropDef { } buf.set(value, buf.length) } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { if (!(value instanceof Uint8Array)) { throw new Error('Invalid type for binary ' + this.path.join('.')) } @@ -217,6 +237,8 @@ export const alias = class Alias extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is any { throw new Error('Serialize alias not implemented') } @@ -237,6 +259,8 @@ export const cardinality = class Cardinality extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is any { if (value instanceof Uint8Array && value.byteLength !== 8) { // buf.set(value, buf.length) @@ -264,7 +288,13 @@ export const cardinality = class Cardinality extends BasePropDef { } } } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { if (value instanceof Uint8Array && value.byteLength !== 8) { throw new Error('unhandled error cardi') } @@ -310,10 +340,18 @@ export const vector = class Vector extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is any { throw new Error('Serialize vector not implemented') } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { throw new Error('Serialize vector not implemented') } override pushSelvaSchema(buf: AutoSizedUint8Array) { @@ -337,10 +375,18 @@ export const colvec = class ColVec extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, + _op: ModifyEnum, + _lang: LangCodeEnum, ): asserts value is any { throw new Error('Serialize colvec not implemented') } - write(buf: Uint8Array, value: unknown, offset: number) { + override write( + buf: Uint8Array, + value: unknown, + offset: number, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ) { throw new Error('Serialize colvec not implemented') } override pushSelvaSchema(buf: AutoSizedUint8Array) { diff --git a/test/schema/props/write.ts b/test/schema/props/write.ts index 3cc99fdff7..d79c81f0b5 100644 --- a/test/schema/props/write.ts +++ b/test/schema/props/write.ts @@ -103,14 +103,16 @@ await test('Separate props: write matches pushValue', async (t) => { // @ts-ignore const prop = new Ctor(schema, ['test'], {}) const autoBuf = new AutoSizedUint8Array() - prop.pushValue(autoBuf, value, lang) + // @ts-ignore + prop.pushValue(autoBuf, value, undefined, lang) const pushResult = new Uint8Array(autoBuf.data.subarray(0, autoBuf.length)) const writeBuf = new Uint8Array(pushResult.length + 10) const offset = 5 // @ts-ignore - prop.write(writeBuf, value, offset, lang) + // @ts-ignore + prop.write(writeBuf, value, offset, undefined, lang) const writeResult = writeBuf.subarray(offset, offset + pushResult.length) From 699fcd14b03df70787aa6baf644a5d15a5b17814 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sat, 31 Jan 2026 09:08:28 +0100 Subject: [PATCH 052/449] fix --- src/schema/defs/props/separate.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 63c632a37f..0a1466d4ca 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -363,6 +363,10 @@ export const vector = class Vector extends BasePropDef { } } +// This will become similair to Main BUFFER +// and it can use it if there is an option used like "appendOnly: true" on the type +// then we can switch to colvec for all main buffer props +// if there are no var props we can iterate straight trough the colvec list using another iterator export const colvec = class ColVec extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) From 0d81874f7b6f94410693b6d4cb2a4c77e6388801 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sun, 1 Feb 2026 09:17:15 +0100 Subject: [PATCH 053/449] remove logs add more --- src/db-client/query/filter/toByteCode.ts | 16 +--------------- src/db-query/ast/filter/filter.ts | 14 ++++++-------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/db-client/query/filter/toByteCode.ts b/src/db-client/query/filter/toByteCode.ts index 9d0328e2fc..c6e6b088ac 100644 --- a/src/db-client/query/filter/toByteCode.ts +++ b/src/db-client/query/filter/toByteCode.ts @@ -1,4 +1,3 @@ -import { debugBuffer } from '../../../sdk.js' import { writeUint64 } from '../../../utils/uint8.js' import { FilterConditionByteSize, @@ -9,7 +8,6 @@ import { PropType, writeFilterSelect, } from '../../../zigTsExports.js' -import { combineIntermediateResults } from '../query.js' import { byteSize } from '../toByteCode/utils.js' import { IntermediateByteCode, QueryDefFilter } from '../types.js' import { conditionBuffer } from './condition.js' @@ -17,7 +15,7 @@ import { conditionBuffer } from './condition.js' const addConditions = ( result: IntermediateByteCode[], def: QueryDefFilter, - fromLastProp: number, // bit wrong for id... + fromLastProp: number, ) => { let lastProp = -1 const prevProp = def.conditions.get(fromLastProp) @@ -38,7 +36,6 @@ const getSelectOp = ( edgeTypeId: number, isMulti: boolean, ): FilterOpCompareEnum => { - // very different if (edgeTypeId != 0) { return isMulti ? FilterOpCompare.selectLargeRefs : FilterOpCompare.selectRef } @@ -134,21 +131,10 @@ export const filterToBuffer = ( ) const nextOrIndex = resultSize + condition.byteLength + fromIndex - // console.log('DERP', nextOrIndex) - writeUint64(condition, nextOrIndex, offset) result.unshift(condition) result.push(filterToBuffer(def.or, lastProp, nextOrIndex, false)) } - // if (top && result.length > 0) { - // console.dir(logger(def), { depth: 10 }) - // const totalByteLength = byteSize(result) - // const res = new Uint8Array(totalByteLength) - // const nResult = combineIntermediateResults(res, 0, result) - // console.log('FILTER!', totalByteLength) - // debugBuffer(res) - // } - return result } diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index aa09134765..4dc73048ff 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -22,22 +22,21 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { const ops = astProp.ops // AND & OR - if (isPropDef(prop)) { if (prop.type === PropType.references) { // references(astProp, ctx, prop) } else if (prop.type === PropType.reference) { + // this can be added here // reference(astProp, ctx, prop) } else if (ops) { - console.log({ ops }) if (prop.id === 0) { main.push({ prop, ops }) } else { for (const op of ops) { + // can prob just push this directly const cond = createCondition(prop, op.op, op.val, op.opts) ctx.query.set(cond, ctx.query.length) } - // includeProp(ctx, prop, include) } } } else { @@ -57,23 +56,22 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { export const filter = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef): number => { const startIndex = ctx.query.length - // doink + + // or cond needs to be here const { main } = walk(ast, ctx, typeDef, { main: [], tree: typeDef.tree, }) - console.log('collect dat main', main) - for (const { prop, ops } of main) { for (const op of ops) { const cond = createCondition(prop, op.op, op.val, op.opts) - console.log(cond) ctx.query.set(cond, ctx.query.length) } } - // includeMainProps(ctx, main, typeDef) + + // or filter needs to be here return ctx.query.length - startIndex } From 995596e93c45a65d125e35cb136c6ce50a28cb82 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sun, 1 Feb 2026 12:20:05 +0100 Subject: [PATCH 054/449] fix --- native/query/filter/filter.zig | 21 ++++---- native/utils.zig | 17 +++---- src/db-query/ast/ast.ts | 6 +-- src/db-query/ast/filter/condition.ts | 69 +++++++++++++------------- src/db-query/ast/filter/filter.ts | 72 ++++++++++++++++++++++++---- src/utils/AutoSizedUint8Array.ts | 10 ++++ test/query-ast/include.ts | 5 ++ 7 files changed, 134 insertions(+), 66 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 9d86adcdb6..a5d6ee6b87 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -18,7 +18,7 @@ pub fn prepare( var i: usize = 0; while (i < q.len) { const headerSize = COND_ALIGN_BYTES + 1 + utils.sizeOf(t.FilterCondition); - var condition: *t.FilterCondition = undefined; + var c: *t.FilterCondition = undefined; // 255 means its unprepared - the condition new index will be set when aligned if (q[i] == 255) { @@ -26,17 +26,18 @@ pub fn prepare( const totalSize = headerSize + condSize; q[i] = COND_ALIGN_BYTES - utils.alignLeft(t.FilterCondition, q[i + 1 .. i + totalSize]) + 1; - condition = utils.readPtr(t.FilterCondition, q, q[i] + i); + c = utils.readPtr(t.FilterCondition, q, q[i] + i); - if (condition.op.compare != t.FilterOpCompare.nextOrIndex) { - condition.fieldSchema = try Schema.getFieldSchema(typeEntry, condition.prop); + if (c.op.compare != t.FilterOpCompare.nextOrIndex) { + c.fieldSchema = try Schema.getFieldSchema(typeEntry, c.prop); } const nextI = q[i] + i + utils.sizeOf(t.FilterCondition); - condition.offset = utils.alignLeftLen(condition.len, q[nextI .. totalSize + i]); + + c.offset = utils.alignLeftLen(c.len, q[nextI .. totalSize + i]); const end = totalSize + i; - switch (condition.op.compare) { + switch (c.op.compare) { .selectLargeRefEdge => { // const select = utils.readPtr(t.FilterSelect, q, i + q[i] + utils.sizeOf(t.FilterCondition) + @alignOf(t.FilterSelect) - condition.offset); // const edgeSelect = utils.readPtr(t.FilterSelect, q, i + q[i] + utils.sizeOf(t.FilterCondition) + @alignOf(t.FilterSelect) - condition.offset); @@ -46,7 +47,7 @@ pub fn prepare( i = end; }, .selectRef => { - const select = utils.readPtr(t.FilterSelect, q, nextI + @alignOf(t.FilterSelect) - condition.offset); + const select = utils.readPtr(t.FilterSelect, q, nextI + @alignOf(t.FilterSelect) - c.offset); select.typeEntry = try Node.getType(ctx.db, select.typeId); try prepare(q[end .. end + select.size], ctx, select.typeEntry); i = end + select.size; @@ -56,8 +57,8 @@ pub fn prepare( }, } } else { - condition = utils.readPtr(t.FilterCondition, q, q[i] + i + 1); - const totalSize = headerSize + condition.size; + c = utils.readPtr(t.FilterCondition, q, q[i] + i + 1); + const totalSize = headerSize + c.size; const end = totalSize + i; i = end; } @@ -109,10 +110,12 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { const c = utils.readPtr(t.FilterCondition, q, i + q[i]); const index = i + q[i] + utils.sizeOf(t.FilterCondition); var nextIndex = COND_ALIGN_BYTES + 1 + utils.sizeOf(t.FilterCondition) + c.size + i; + if (prop != c.prop) { prop = c.prop; v = Fields.getRaw(node, c.fieldSchema); } + pass = switch (c.op.compare) { .nextOrIndex => blk: { nextOrIndex = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; diff --git a/native/utils.zig b/native/utils.zig index bd82d11bea..43147f9c6d 100644 --- a/native/utils.zig +++ b/native/utils.zig @@ -257,14 +257,15 @@ pub inline fn alignLeft(comptime T: type, data: []u8) u8 { if (offset != 0) move(aligned, unAligned); return offset; } - -pub inline fn alignLeftLen(alignment: u8, data: []u8) u8 { - // (i + 7) & ~@as(usize, 7); more efficient - const unAligned = data[alignment..data.len]; - const address = @intFromPtr(unAligned.ptr); - const offset: u8 = @truncate(address % alignment); - const aligned = data[alignment - offset .. data.len - offset]; - if (offset != 0) move(aligned, unAligned); +pub fn alignLeftLen(alignment: u8, data: []u8) u8 { + const addr = @intFromPtr(data.ptr); + const offset = @as(u8, @truncate(addr & (alignment - 1))); + if (offset == 0) return 0; + const start = alignment - offset; + const len = data.len - alignment; + const src = data[alignment .. alignment + len]; + const dst = data[start .. start + len]; + move(dst, src); return offset; } diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 5385796827..52a986935d 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -1,5 +1,4 @@ import { ReaderLocales, ReaderSchema } from '../../protocol/index.js' -import { PropDef, PropTree } from '../../schema/defs/index.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' export type FilterOpts = { @@ -41,8 +40,8 @@ export type FilterAst = { select?: { start: number; end: number } } } - or?: FilterAst[] - and?: FilterAst[] + or?: FilterAst + and?: FilterAst edges?: FilterAst } @@ -66,6 +65,7 @@ export type QueryAst = { props?: Record edges?: QueryAst } + export type Ctx = { query: AutoSizedUint8Array readSchema: ReaderSchema diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts index 2d973bbaf5..69caadef7e 100644 --- a/src/db-query/ast/filter/condition.ts +++ b/src/db-query/ast/filter/condition.ts @@ -10,30 +10,35 @@ import { } from '../../../zigTsExports.js' import { FilterOpts, Operator } from '../ast.js' +export const conditionByteSize = (propSize: number, size: number) => { + return size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propSize +} + // this will be PUSH export const conditionBuffer = ( - propDef: { start: number; id: number; size: number }, + prop: { start: number; id: number; size: number }, size: number, op: FilterOp, ) => { - const condition = new Uint8Array( - size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propDef.size, - ) + const condition = new Uint8Array(conditionByteSize(prop.size, size)) condition[0] = 255 // Means condition header is not aligned const offset = writeFilterCondition( condition, { op, - start: propDef.start || 0, - prop: propDef.id, + start: prop.start || 0, + prop: prop.id, fieldSchema: 0, - len: propDef.size, + len: prop.size, offset: 255, // Means value is not aligned - size: size + propDef.size, + size: size + prop.size, }, FilterConditionAlignOf + 1, - ) + propDef.size + ) + prop.size + + console.log('----', prop.size) + return { condition, offset } } @@ -47,7 +52,7 @@ const opMap: Partial> = { } const getFilterOp = ( - propDef: PropDef, + prop: PropDef, operator: Operator, size: number, ): { @@ -62,27 +67,27 @@ const getFilterOp = ( } const write = (buf: Uint8Array, val: any, offset: number) => { - propDef.write(buf, val, offset) + prop.write(buf, val, offset) } if ((opName === 'eq' || opName === 'neq') && size > 1) { - const vectorLen = 16 / propDef.size + const vectorLen = 16 / prop.size if (size > vectorLen) { return { op: { compare: FilterOpCompare[`${opName}Batch`], - prop: propDef.type, + prop: prop.type, }, - size: propDef.size, + size: prop.size, write, } } else { return { op: { compare: FilterOpCompare[`${opName}BatchSmall`], - prop: propDef.type, + prop: prop.type, }, - size: propDef.size, + size: prop.size, write, } } @@ -90,14 +95,14 @@ const getFilterOp = ( return { op: { compare: FilterOpCompare.range, - prop: propDef.type, + prop: prop.type, }, - size: propDef.size * 2, + size: prop.size * 2, write: (condition: Uint8Array, v: any, offset: number) => { // x >= 3 && x <= 11 // (x -% 3) <= (11 - 3) - propDef.write(condition, v[0], offset) - propDef.write(condition, v[1] - v[0], offset + propDef.size) + prop.write(condition, v[0], offset) + prop.write(condition, v[1] - v[0], offset + prop.size) return condition }, } @@ -105,16 +110,16 @@ const getFilterOp = ( return { op: { compare: FilterOpCompare[opName], - prop: propDef.type, + prop: prop.type, }, - size: propDef.size, + size: prop.size, write, } } } export const createCondition = ( - propDef: PropDef, + prop: PropDef, operator: Operator, value?: any, opts?: FilterOpts, @@ -123,7 +128,7 @@ export const createCondition = ( value = [value] } - const { op, size, write } = getFilterOp(propDef, operator, value.length) + const { op, size, write } = getFilterOp(prop, operator, value.length) // this is fixed make fixed and variable in a file @@ -131,7 +136,7 @@ export const createCondition = ( if (value.length == 1 || operator === '..' || operator == '!..') { const { condition, offset } = conditionBuffer( - { size: propDef.size, start: propDef.start, id: propDef.id }, + { size: prop.size, start: prop.start, id: prop.id }, size, op, ) @@ -145,16 +150,12 @@ export const createCondition = ( return condition } else if (value.length > vectorLen) { // only relevant for eq and neq - const { condition, offset } = conditionBuffer( - { ...propDef, start: propDef.start || 0 }, - value.length * size, - op, - ) + const { condition, offset } = conditionBuffer(prop, value.length * size, op) let i = offset // Actual values for (const v of value) { write(condition, v, i) - i += propDef.size + i += prop.size } // Empty padding for SIMD (16 bytes) for (let j = 0; j < vectorLen; j++) { @@ -164,11 +165,7 @@ export const createCondition = ( return condition } else if (value.length > 1) { // Small batch - const { condition, offset } = conditionBuffer( - { ...propDef, start: propDef.start || 0 }, - value.length * size, - op, - ) + const { condition, offset } = conditionBuffer(prop, value.length * size, op) let i = offset for (let j = 0; j < vectorLen; j++) { // Allways use a full ARM neon simd vector (16 bytes) diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index 4dc73048ff..c33becf97a 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -4,12 +4,19 @@ import { PropTree, TypeDef, } from '../../../schema/defs/index.js' -import { PropType } from '../../../zigTsExports.js' +import { debugBuffer } from '../../../sdk.js' +import { writeUint64 } from '../../../utils/uint8.js' +import { FilterOpCompare, ID_PROP, PropType } from '../../../zigTsExports.js' import { Ctx, FilterAst, FilterOp } from '../ast.js' -import { createCondition } from './condition.js' +import { + conditionBuffer, + conditionByteSize, + createCondition, +} from './condition.js' type WalkCtx = { tree: PropTree + prop: number main: { prop: PropDef; ops: FilterOp[] }[] } @@ -32,10 +39,11 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { if (prop.id === 0) { main.push({ prop, ops }) } else { + walkCtx.prop = prop.id for (const op of ops) { // can prob just push this directly - const cond = createCondition(prop, op.op, op.val, op.opts) - ctx.query.set(cond, ctx.query.length) + const condition = createCondition(prop, op.op, op.val, op.opts) + ctx.query.set(condition, ctx.query.length) } } } @@ -44,6 +52,7 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { walk(astProp, ctx, typeDef, { main, tree: prop, + prop: walkCtx.prop, }) } else { // if EN, if NL @@ -54,24 +63,67 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { return walkCtx } -export const filter = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef): number => { +export const filter = ( + ast: FilterAst, + ctx: Ctx, + typeDef: TypeDef, + filterIndex: number = 0, + lastProp: number = ID_PROP, +): number => { const startIndex = ctx.query.length + // need to pass the prop + // or cond needs to be here - const { main } = walk(ast, ctx, typeDef, { + const walkCtx = { main: [], tree: typeDef.tree, - }) + prop: lastProp, + } + + // if (ast.or) { + // ctx.query.reserve(conditionByteSize(8, 8)) + // } + + const { main } = walk(ast, ctx, typeDef, walkCtx) for (const { prop, ops } of main) { + // better to do main first scince they are usualy lighter filters... + walkCtx.prop = prop.id for (const op of ops) { - const cond = createCondition(prop, op.op, op.val, op.opts) - ctx.query.set(cond, ctx.query.length) + const condition = createCondition(prop, op.op, op.val, op.opts) + ctx.query.set(condition, ctx.query.length) } } - // or filter needs to be here + // if (ast.or) { + // const resultSize = ctx.query.length - startIndex + // const { offset, condition } = conditionBuffer( + // { id: lastProp, size: 8, start: 0 }, + // 8, + // { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, + // ) + + // const nextOrIndex = resultSize + filterIndex + + // console.info('NEXT OR INDEX', nextOrIndex) + // writeUint64(condition, nextOrIndex, offset) + // ctx.query.set(condition, startIndex) + // // then add the actual OR cond + + // filter( + // ast.or, + // ctx, + // typeDef, + // ctx.query.length - startIndex + filterIndex, + // walkCtx.prop, + // ) + // } + + console.log('-------------------------DERP FILTER...') + debugBuffer(ctx.query.data, startIndex, ctx.query.length) + console.log('-------------------------DERP FILTER... DONE') return ctx.query.length - startIndex } diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 3372d70701..1b40b2e1c6 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -235,6 +235,16 @@ export class AutoSizedUint8Array { return index } + reserve(amount: number): number { + const index = this.length + const end = index + amount + if (end > this.data.length) { + this.ensure(end) + } + this.length = end + return index + } + // Core array methods restored for type safety and performance push(byte: number): void { return this.pushUint8(byte) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 1634ea9a1c..336f6607b8 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -86,6 +86,11 @@ await test('include', async (t) => { props: { y: { ops: [{ op: '=', val: 67 }] }, }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 99 }] }, + // }, + // }, }, props: { name: { include: {} }, From 9c6e27dd8c7a750db635236376633a09213e5c99 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sun, 1 Feb 2026 12:31:02 +0100 Subject: [PATCH 055/449] fixed main buffer sorting --- src/schema/defs/getTypeDefs.ts | 1 + test/query-ast/include.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 1b9352eff3..684865ba73 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -10,6 +10,7 @@ import { defs, type PropDef, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 if (a.size === 4 && b.size !== 8) return -1 + if (a.size === 2 && a.size !== 4 && b.size !== 8) return -1 if (a.size === b.size) return 0 return 1 } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 336f6607b8..dfe09936ff 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,4 +1,5 @@ import { deSerializeSchema } from '../../dist/protocol/db-read/schema/deserialize.js' +import { getTypeDefs } from '../../dist/schema/defs/getTypeDefs.js' import { convertToReaderSchema } from '../../src/db-client/query/queryDefToReadSchema.js' import { registerQuery } from '../../src/db-client/query/registerQuery.js' import { QueryAst } from '../../src/db-query/ast/ast.js' @@ -45,6 +46,8 @@ await test('include', async (t) => { }, }) + console.log(getTypeDefs(client.schema!).get('user')?.main) + const a = client.create('user', { name: 'AAAAAAAAAA', y: 67, From fd4e5f5ef7c634f4a264e9bcf3c57ebf00533fd8 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 08:03:44 +0100 Subject: [PATCH 056/449] fix --- src/schema/defs/props/fixed.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index aae5155f53..77b839fe24 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -384,8 +384,8 @@ export const enum_ = class Enum extends uint8 { override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op: ModifyEnum, - _lang: LangCodeEnum, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is EnumItem { const val = validateEnum(value, this.vals, this.path) buf.pushUint8(val) @@ -408,8 +408,8 @@ export const boolean = class Boolean extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op: ModifyEnum, - _lang: LangCodeEnum, + _op?: ModifyEnum, + _lang?: LangCodeEnum, ): asserts value is boolean { const val = validateBoolean(value, this.path) buf.pushUint8(val) From 2467946c482a6ab7bb952755132249832430d969 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 08:07:51 +0100 Subject: [PATCH 057/449] add or --- src/db-query/ast/filter/filter.ts | 46 +++++++++++++++---------------- test/query-ast/include.ts | 18 ++++++------ 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index c33becf97a..d2798eaf7f 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -97,29 +97,29 @@ export const filter = ( } } - // if (ast.or) { - // const resultSize = ctx.query.length - startIndex - // const { offset, condition } = conditionBuffer( - // { id: lastProp, size: 8, start: 0 }, - // 8, - // { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, - // ) - - // const nextOrIndex = resultSize + filterIndex - - // console.info('NEXT OR INDEX', nextOrIndex) - // writeUint64(condition, nextOrIndex, offset) - // ctx.query.set(condition, startIndex) - // // then add the actual OR cond - - // filter( - // ast.or, - // ctx, - // typeDef, - // ctx.query.length - startIndex + filterIndex, - // walkCtx.prop, - // ) - // } + if (ast.or) { + const resultSize = ctx.query.length - startIndex + const { offset, condition } = conditionBuffer( + { id: lastProp, size: 8, start: 0 }, + 8, + { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, + ) + + const nextOrIndex = resultSize + filterIndex + + console.info('NEXT OR INDEX', nextOrIndex) + writeUint64(condition, nextOrIndex, offset) + ctx.query.set(condition, startIndex) + // then add the actual OR cond + + filter( + ast.or, + ctx, + typeDef, + ctx.query.length - startIndex + filterIndex, + walkCtx.prop, + ) + } console.log('-------------------------DERP FILTER...') debugBuffer(ctx.query.data, startIndex, ctx.query.length) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index dfe09936ff..63574d34ab 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,7 +1,4 @@ -import { deSerializeSchema } from '../../dist/protocol/db-read/schema/deserialize.js' import { getTypeDefs } from '../../dist/schema/defs/getTypeDefs.js' -import { convertToReaderSchema } from '../../src/db-client/query/queryDefToReadSchema.js' -import { registerQuery } from '../../src/db-client/query/registerQuery.js' import { QueryAst } from '../../src/db-query/ast/ast.js' import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { @@ -68,11 +65,12 @@ await test('include', async (t) => { }, }) - client.create('user', { + await client.create('user', { name: 'CCCCCCCCC', cook: { cookie: 1234, }, + y: 0, // mrFriend: { id: a, $level: 67 }, friends: [{ id: a, $level: 250 }, b], }) @@ -87,13 +85,13 @@ await test('include', async (t) => { type: 'user', filter: { props: { - y: { ops: [{ op: '=', val: 67 }] }, + y: { ops: [{ op: '=', val: 0 }] }, + }, + or: { + props: { + y: { ops: [{ op: '=', val: 67 }] }, + }, }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 99 }] }, - // }, - // }, }, props: { name: { include: {} }, From 7a8b0f15f203155cc4de1d1944514d427686e28b Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 08:38:58 +0100 Subject: [PATCH 058/449] fix --- src/db-query/ast/iteratorType.ts | 1 - src/db-query/ast/single.ts | 1 + test/query-ast/include.ts | 65 ++++++++++++++++---------------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index 6f0179342a..99bf786964 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -6,7 +6,6 @@ import { QUERY_ITERATOR_SEARCH, QUERY_ITERATOR_SEARCH_VEC, QueryIteratorTypeEnum, - Order, QueryHeader, } from '../../zigTsExports.js' import { QueryDef, QueryDefType } from '../../db-client/query/types.js' diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index 7f333ad5df..c727a3e04b 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -51,6 +51,7 @@ export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { }, edges, ) + props.edgeSize(ctx.query.data, size, headerIndex) } } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 63574d34ab..0762c8db7c 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -43,7 +43,7 @@ await test('include', async (t) => { }, }) - console.log(getTypeDefs(client.schema!).get('user')?.main) + // console.log(getTypeDefs(client.schema!).get('user')?.main) const a = client.create('user', { name: 'AAAAAAAAAA', @@ -71,7 +71,7 @@ await test('include', async (t) => { cookie: 1234, }, y: 0, - // mrFriend: { id: a, $level: 67 }, + mrFriend: { id: a, $level: 67 }, friends: [{ id: a, $level: 250 }, b], }) @@ -83,46 +83,47 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - filter: { - props: { - y: { ops: [{ op: '=', val: 0 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 67 }] }, - }, - }, - }, + // filter: { + // props: { + // y: { ops: [{ op: '=', val: 0 }] }, + // // add reference + // // add references + // // add edges + // }, + // // add AND + // or: { + // props: { + // y: { ops: [{ op: '=', val: 67 }] }, + // }, + // }, + // }, props: { - name: { include: {} }, + // name: { include: {} }, y: { include: {} }, - // x: { include: {} }, - // cook: { - // props: { - // cookie: { include: {} }, - // }, - // }, - // // NOW ADD MR FRIEND! // mrFriend: { // props: { - // name: { include: {} }, - // }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, - // }, - // friends: { - // props: { - // name: { include: {} }, + // y: { include: {} }, // }, + + // // EDGE + // edges: { // props: { // $level: { include: {} }, // }, // }, // }, + // fix friends + friends: { + props: { + name: { include: {} }, + }, + edges: { + props: { + $level: { include: {} }, + }, + }, + }, }, } @@ -134,7 +135,7 @@ await test('include', async (t) => { // TEXT - make this localized true // OPTS - console.dir(ctx, { depth: 10 }) + // console.dir(ctx, { depth: 10 }) const result = await db.server.getQueryBuf(ctx.query) debugBuffer(result) From 2b5ad16cafcc29076e7315e6a00c1152d5be6872 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 09:04:39 +0100 Subject: [PATCH 059/449] correct or --- native/query/filter/filter.zig | 1 + src/db-query/ast/filter/filter.ts | 11 ++++--- test/query-ast/include.ts | 55 +++++++++++++++++-------------- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index a5d6ee6b87..9ff8e5ee88 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -119,6 +119,7 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { pass = switch (c.op.compare) { .nextOrIndex => blk: { nextOrIndex = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; + std.debug.print("hello OR {any} \n", .{nextOrIndex}); break :blk true; }, .selectRef => blk: { diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index d2798eaf7f..fd2910adb9 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -82,9 +82,9 @@ export const filter = ( prop: lastProp, } - // if (ast.or) { - // ctx.query.reserve(conditionByteSize(8, 8)) - // } + if (ast.or) { + ctx.query.reserve(conditionByteSize(8, 8)) + } const { main } = walk(ast, ctx, typeDef, walkCtx) @@ -99,15 +99,16 @@ export const filter = ( if (ast.or) { const resultSize = ctx.query.length - startIndex + const nextOrIndex = resultSize + filterIndex + const { offset, condition } = conditionBuffer( { id: lastProp, size: 8, start: 0 }, 8, { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, ) - const nextOrIndex = resultSize + filterIndex - console.info('NEXT OR INDEX', nextOrIndex) + console.dir(ast.or, { depth: 10 }) writeUint64(condition, nextOrIndex, offset) ctx.query.set(condition, startIndex) // then add the actual OR cond diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 0762c8db7c..3df2580a73 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -83,20 +83,20 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - // filter: { - // props: { - // y: { ops: [{ op: '=', val: 0 }] }, - // // add reference - // // add references - // // add edges - // }, - // // add AND - // or: { - // props: { - // y: { ops: [{ op: '=', val: 67 }] }, - // }, - // }, - // }, + filter: { + props: { + y: { ops: [{ op: '=', val: 0 }] }, + // add reference + // add references + // add edges + }, + // add AND + or: { + props: { + y: { ops: [{ op: '=', val: 67 }] }, + }, + }, + }, props: { // name: { include: {} }, y: { include: {} }, @@ -114,19 +114,26 @@ await test('include', async (t) => { // }, // }, // fix friends - friends: { - props: { - name: { include: {} }, - }, - edges: { - props: { - $level: { include: {} }, - }, - }, - }, + // friends: { + // props: { + // name: { include: {} }, + // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, + // }, }, } + // single ref edge filter + // sort + // variable filters + + // in js + // meta include, text include (localized: true) + const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) // TODO From fb3148eb1394fd5f3d6b8c37c9f6d6e63ef2fb60 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 11:11:01 +0100 Subject: [PATCH 060/449] clean up .write() --- src/schema/def/utils.ts | 25 ++- src/schema/defs/props/base.ts | 20 +- src/schema/defs/props/fixed.ts | 148 ------------ src/schema/defs/props/references.ts | 337 +--------------------------- src/schema/defs/props/separate.ts | 127 ----------- src/utils/AutoSizedUint8Array.ts | 32 +-- 6 files changed, 48 insertions(+), 641 deletions(-) diff --git a/src/schema/def/utils.ts b/src/schema/def/utils.ts index eced26bf36..5ea71e2dbb 100644 --- a/src/schema/def/utils.ts +++ b/src/schema/def/utils.ts @@ -110,16 +110,23 @@ export const sortMainProps = ( ) => { const sizeA = REVERSE_SIZE_MAP[a.typeIndex] const sizeB = REVERSE_SIZE_MAP[b.typeIndex] - if (sizeA === 8) { - return -1 - } - if (sizeA === 4 && sizeB !== 8) { - return -1 - } - if (sizeA === sizeB) { - return 0 - } + if (sizeA === 8) return -1 + if (sizeA === 4 && sizeB !== 8) return -1 + if (sizeA === 2 && sizeB !== 8) return -1 + if (sizeA === sizeB) return 0 return 1 + + // if (sizeA === 8) { + // return -1 + // } + // if (sizeA === 4 && sizeB !== 8) { + // return -1 + // } + + // if (sizeA === sizeB) { + // return 0 + // } + // return 1 } export const propIndexOffset = (prop: PropDef) => { diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 34200a2885..3a8bcbedd7 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -1,15 +1,16 @@ import type { SchemaProp } from '../../../schema.js' import { + LangCode, + Modify, PropType, - PropTypeSelva, type LangCodeEnum, type ModifyEnum, type PropTypeEnum, - type PropTypeSelvaEnum, } from '../../../zigTsExports.js' -import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { PropDef, TypeDef } from '../index.js' +let sharedBuf: AutoSizedUint8Array export class BasePropDef implements PropDef { constructor(schema: SchemaProp, path: string[], typeDef: TypeDef) { this.schema = schema @@ -36,10 +37,17 @@ export class BasePropDef implements PropDef { buf: Uint8Array, value: unknown, offset: number, - op?: ModifyEnum, - lang?: LangCodeEnum, + op: ModifyEnum = Modify.create, + lang: LangCodeEnum = LangCode.none, ): void { - // To be implemented by subclasses + if (sharedBuf) { + sharedBuf.data = buf + sharedBuf.length = offset + sharedBuf.maxLength = buf.length + } else { + sharedBuf = new AutoSizedUint8Array(0, buf.length, buf) + } + this.pushValue(sharedBuf, value, op, lang) } pushSelvaSchema(buf: AutoSizedUint8Array): void { // To be implemented by subclasses diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 77b839fe24..6a23483055 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -1,11 +1,5 @@ import type { EnumItem, SchemaEnum } from '../../../schema.js' import { convertToTimestamp } from '../../../utils/timestamp.js' -import { - writeDoubleLE, - writeInt64, - writeUint16, - writeUint32, -} from '../../../utils/index.js' import { PropType, type PropTypeEnum, @@ -108,16 +102,6 @@ export const number = class Number extends BasePropDef { const val = validateNumber(value, this.schema, this.path) buf.pushDoubleLE(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateNumber(value, this.schema, this.path) - writeDoubleLE(buf, val, offset) - } } export const timestamp = class Timestamp extends number { @@ -131,16 +115,6 @@ export const timestamp = class Timestamp extends number { const ts = validateTimestamp(value, this.schema, this.path) buf.pushInt64(ts) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const ts = validateTimestamp(value, this.schema, this.path) - writeInt64(buf, ts, offset) - } } export const uint8 = class Uint8 extends BasePropDef { @@ -162,23 +136,6 @@ export const uint8 = class Uint8 extends BasePropDef { ) as number buf.pushUint8(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateInteger( - value, - this.schema, - this.path, - 'uint8', - 0, - 255, - ) as number - buf[offset] = val - } } export const int8 = class Int8 extends uint8 { @@ -199,23 +156,6 @@ export const int8 = class Int8 extends uint8 { ) as number buf.pushUint8(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateInteger( - value, - this.schema, - this.path, - 'int8', - -128, - 127, - ) as number - buf[offset] = val - } } export const uint16 = class Uint16 extends BasePropDef { @@ -237,23 +177,6 @@ export const uint16 = class Uint16 extends BasePropDef { ) as number buf.pushUint16(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateInteger( - value, - this.schema, - this.path, - 'uint16', - 0, - 65535, - ) as number - writeUint16(buf, val, offset) - } } export const int16 = class Int16 extends uint16 { @@ -274,23 +197,6 @@ export const int16 = class Int16 extends uint16 { ) as number buf.pushUint16(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateInteger( - value, - this.schema, - this.path, - 'int16', - -32768, - 32767, - ) as number - writeUint16(buf, val, offset) - } } export const uint32 = class Uint32 extends BasePropDef { @@ -312,23 +218,6 @@ export const uint32 = class Uint32 extends BasePropDef { ) as number buf.pushUint32(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateInteger( - value, - this.schema, - this.path, - 'uint32', - 0, - 4294967295, - ) as number - writeUint32(buf, val, offset) - } } export const int32 = class Int32 extends uint32 { @@ -349,23 +238,6 @@ export const int32 = class Int32 extends uint32 { ) as number buf.pushUint32(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateInteger( - value, - this.schema, - this.path, - 'int32', - -2147483648, - 2147483647, - ) as number - writeUint32(buf, val, offset) - } } export const enum_ = class Enum extends uint8 { @@ -390,16 +262,6 @@ export const enum_ = class Enum extends uint8 { const val = validateEnum(value, this.vals, this.path) buf.pushUint8(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateEnum(value as any, this.vals, this.path) - buf[offset] = val - } } export const boolean = class Boolean extends BasePropDef { @@ -414,14 +276,4 @@ export const boolean = class Boolean extends BasePropDef { const val = validateBoolean(value, this.path) buf.pushUint8(val) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - const val = validateBoolean(value, this.path) - buf[offset] = val - } } diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 7865647d27..7a75d6fbff 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -7,18 +7,15 @@ import { pushModifyReferencesHeader, pushModifyReferencesMetaHeader, pushSelvaSchemaRef, - writeModifyReferenceMetaHeader, writeModifyReferenceMetaHeaderProps, - writeModifyReferencesHeader, writeModifyReferencesHeaderProps, - writeModifyReferencesMetaHeader, writeModifyReferencesMetaHeaderProps, type LangCodeEnum, type ModifyEnum, type PropTypeEnum, } from '../../../zigTsExports.js' import { writeUint32 } from '../../../utils/index.js' -import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { SchemaProp, SchemaReference, @@ -128,103 +125,6 @@ const serializeIdsAndMeta = ( return i } -const writeIds = ( - buf: Uint8Array, - ids: number[], - valueIndex: number, - bufOffset: number, -): { index: number; written: number } => { - let i = valueIndex - let written = 0 - writeUint32(buf, 0, bufOffset) - bufOffset += 4 - written += 4 - for (; i < ids.length; i++) { - const id = getRealId(ids[i]) - if (!id) break - writeUint32(buf, id, bufOffset) - bufOffset += 4 - written += 4 - } - return { index: i, written } -} - -const writeTmpIds = ( - buf: Uint8Array, - items: BasedModify[], - valueIndex: number, - bufOffset: number, -): { index: number; written: number } => { - let i = valueIndex - let written = 0 - writeUint32(buf, 0, bufOffset) - bufOffset += 4 - written += 4 - for (; i < items.length; i++) { - const tmpId = getTmpId(items[i]) - if (tmpId === undefined) break - writeUint32(buf, tmpId, bufOffset) - bufOffset += 4 - written += 4 - } - return { index: i, written } -} - -const writeIdsAndMeta = ( - buf: Uint8Array, - items: any[], - op: ModifyEnum, - valueIndex: number, - lang: LangCodeEnum, - bufOffset: number, - edgesType?: TypeDef, -): { index: number; written: number } => { - let i = valueIndex - let written = 0 - const start = bufOffset - bufOffset += 4 - written += 4 - - for (; i < items.length; i++) { - const item = items[i] - if (!isValidRefObj(item)) { - break - } - const realId = getRealId(item.id) - const id = realId || getTmpId(item.id) - if (id === undefined) { - break - } - const headerSize = writeModifyReferencesMetaHeader( - buf, - { - id: id, - isTmp: !realId, - withIndex: '$index' in item, - index: item.$index, - size: 0, - }, - bufOffset, - ) - const sizeAfterHeader = headerSize - bufOffset - const index = bufOffset - bufOffset += sizeAfterHeader - written += sizeAfterHeader - - if (edgesType) { - const edges = getEdges(item) - if (edges) { - // TODO: Edges writing not supported in raw write - } - } - } - - // store the amount of refs aka size - writeUint32(buf, i - valueIndex, start) - - return { index: i, written } -} - const isValidRefObj = (item: any) => { if (typeof item === 'object' && item !== null) { return getRealId(item.id) || getTmpId(item.id) !== undefined @@ -276,82 +176,6 @@ const setReferences = ( } } -const setReferencesWrite = ( - buf: Uint8Array, - value: any[], - prop: BasePropDef & { edges?: TypeDef }, - op: ModifyEnum, - lang: LangCodeEnum, - bufOffset: number, -): number => { - let offset = 0 - const len = value.length - while (offset < len) { - const item = value[offset] - if (getRealId(item)) { - const index = bufOffset - bufOffset = writeModifyReferencesHeader( - buf, - { - op: ModifyReferences.ids, - size: 0, - }, - bufOffset, - ) - const start = bufOffset - const res = writeIds(buf, value, offset, bufOffset) - offset = res.index - bufOffset += res.written - writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) - } else if (getTmpId(item) !== undefined) { - const index = bufOffset - bufOffset = writeModifyReferencesHeader( - buf, - { - op: ModifyReferences.tmpIds, - size: 0, - }, - bufOffset, - ) - const start = bufOffset - const res = writeTmpIds(buf, value, offset, bufOffset) - offset = res.index - bufOffset += res.written - writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) - } else if (isValidRefObj(item)) { - const index = bufOffset - bufOffset = writeModifyReferencesHeader( - buf, - { - op: ModifyReferences.idsWithMeta, - size: 0, - }, - bufOffset, - ) - const start = bufOffset - const res = writeIdsAndMeta( - buf, - value, - op, - offset, - lang, - bufOffset, - prop.edges, - ) - offset = res.index - bufOffset += res.written - writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) - } else if (item instanceof BasedModify) { - throw item - } else if (typeof item === 'object' && item?.id instanceof BasedModify) { - throw item.id - } else { - throw 'bad ref!' - } - } - return bufOffset -} - const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { let offset = 0 while (offset < value.length) { @@ -380,53 +204,6 @@ const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { } } -const deleteReferencesWrite = ( - buf: Uint8Array, - value: any[], - bufOffset: number, -): number => { - let offset = 0 - while (offset < value.length) { - const item = value[offset] - if (getRealId(item)) { - const index = bufOffset - bufOffset = writeModifyReferencesHeader( - buf, - { - op: ModifyReferences.delIds, - size: 0, - }, - bufOffset, - ) - const start = bufOffset - const res = writeIds(buf, value, offset, bufOffset) - offset = res.index - bufOffset += res.written - writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) - } else if (getTmpId(item) !== undefined) { - const index = bufOffset - bufOffset = writeModifyReferencesHeader( - buf, - { - op: ModifyReferences.delTmpIds, - size: 0, - }, - bufOffset, - ) - const start = bufOffset - const res = writeTmpIds(buf, value, offset, bufOffset) - offset = res.index - bufOffset += res.written - writeModifyReferencesHeaderProps.size(buf, bufOffset - start, index) - } else if (item instanceof BasedModify) { - throw item - } else { - throw 'bad ref' - } - } - return bufOffset -} - export const references = class References extends BasePropDef { override type: PropTypeEnum = PropType.references declare schema: SchemaReferences @@ -465,44 +242,6 @@ export const references = class References extends BasePropDef { deleteReferences(buf, val.delete) } } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - op: ModifyEnum, - lang: LangCodeEnum, - ) { - if (typeof value !== 'object' || value === null) { - throw new Error('References value must be an object and not null') - } - - const val = value as { - add?: any[] - update?: any[] - delete?: any[] - } - - if (Array.isArray(value)) { - // In write() we do NOT clear? - // pushValue clears if Modify.update. - // We should replicate logic. - if (op === Modify.update) { - // buf.push(ModifyReferences.clear) - buf[offset] = ModifyReferences.clear - offset += 1 - } - offset = setReferencesWrite(buf, value, this, op, lang, offset) - } - if (val.add) { - offset = setReferencesWrite(buf, val.add, this, op, lang, offset) - } - if (val.update) { - offset = setReferencesWrite(buf, val.update, this, op, lang, offset) - } - if (val.delete) { - offset = deleteReferencesWrite(buf, val.delete, offset) - } - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaRef(buf, { type: PropTypeSelva.references, @@ -581,80 +320,6 @@ export const reference = class Reference extends BasePropDef { } } } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - op: ModifyEnum, - lang: LangCodeEnum, - ) { - const id = getRealId(value) - if (id) { - writeModifyReferenceMetaHeader( - buf, - { - id, - isTmp: false, - size: 0, - }, - offset, - ) - return - } - const tmpId = getTmpId(value) - if (tmpId !== undefined) { - writeModifyReferenceMetaHeader( - buf, - { - id: tmpId, - isTmp: true, - size: 0, - }, - offset, - ) - return - } - - if (value instanceof BasedModify) { - throw value - } - - if (typeof value === 'object' && value !== null) { - const val = value as { id: any } - const realId = getRealId(val.id) - const id = realId || getTmpId(val.id) - if (id !== undefined) { - const index = offset - offset = writeModifyReferenceMetaHeader( - buf, - { - id, - isTmp: !realId, - size: 0, - }, - offset, - ) - const start = offset - if (this.edges) { - const edges = getEdges(val) - if (edges) { - // TODO: Edges writing - // serializeProps(prop.edges.tree, edges, buf, op, lang) - // writeModifyReferenceMetaHeaderProps.size( - // buf, - // offset - start, - // index, - // ) - } - } - return - } - - if (val.id instanceof BasedModify) { - throw val.id - } - } - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaRef(buf, { type: PropTypeSelva.reference, diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 0a1466d4ca..1b5461e21f 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -17,10 +17,8 @@ import { pushSelvaSchemaMicroBuffer, pushSelvaSchemaString, pushSelvaSchemaText, - writeModifyCardinalityHeader, LangCode, } from '../../../zigTsExports.js' -import { writeUint32 } from '../../../utils/index.js' import { xxHash64 } from '../../../db-client/xxHash64.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' @@ -84,48 +82,6 @@ export const string = class String extends BasePropDef { const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } - override write( - buf: Uint8Array, - val: unknown, - offset: number, - _op?: ModifyEnum, - lang: LangCodeEnum = LangCode.none, - ): asserts val is string { - if (typeof val !== 'string') { - throw new Error('Invalid type for string ' + this.path.join('.')) - } - const prop = this.schema as SchemaString - if (prop.min !== undefined && val.length < prop.min) { - throw new Error( - `Length ${val.length} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && val.length > prop.max) { - throw new Error( - `Length ${val.length} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - const normalized = val.normalize('NFKD') - buf[offset] = lang - buf[offset + 1] = NOT_COMPRESSED - const { written } = ENCODER.encodeInto(normalized, buf.subarray(offset + 2)) - - if (prop.maxBytes !== undefined) { - if (written > prop.maxBytes) { - throw new Error( - `Byte length ${written} is larger than maxBytes ${ - prop.maxBytes - } for ${this.path.join('.')}`, - ) - } - } - const crc = native.crc32(buf.subarray(offset + 2, offset + 2 + written)) - writeUint32(buf, crc, offset + 2 + written) - } pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} override pushSelvaSchema(buf: AutoSizedUint8Array) { @@ -161,18 +117,6 @@ export const json = class Json extends string { } super.pushValue(buf, JSON.stringify(value), op, lang) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - op?: ModifyEnum, - lang: LangCodeEnum = LangCode.none, - ) { - if (value === undefined) { - throw new Error('Invalid undefined value for json ' + this.path.join('.')) - } - super.write(buf, JSON.stringify(value), offset, op, lang) - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -203,26 +147,6 @@ export const binary = class Binary extends BasePropDef { } buf.set(value, buf.length) } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - if (!(value instanceof Uint8Array)) { - throw new Error('Invalid type for binary ' + this.path.join('.')) - } - const prop = this.schema as SchemaString - if (prop.maxBytes !== undefined && value.byteLength > prop.maxBytes) { - throw new Error( - `Byte length ${value.byteLength} is larger than maxBytes ${ - prop.maxBytes - } for ${this.path.join('.')}`, - ) - } - buf.set(value, offset) - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -288,39 +212,6 @@ export const cardinality = class Cardinality extends BasePropDef { } } } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - if (value instanceof Uint8Array && value.byteLength !== 8) { - throw new Error('unhandled error cardi') - } - - if (!Array.isArray(value)) { - value = [value] - } - - const items = value as any[] - - if (items.length === 0) return - - offset = writeModifyCardinalityHeader(buf, this, offset) - - for (const item of items) { - if (typeof item === 'string') { - xxHash64(ENCODER.encode(item), buf, offset) - offset += 8 - } else if (item instanceof Uint8Array && item.byteLength === 8) { - buf.set(item, offset) - offset += 8 - } else { - throw new Error('Invalid value for cardinality ' + this.path.join('.')) - } - } - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, @@ -345,15 +236,6 @@ export const vector = class Vector extends BasePropDef { ): asserts value is any { throw new Error('Serialize vector not implemented') } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - throw new Error('Serialize vector not implemented') - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaMicroBuffer(buf, { type: PropTypeSelva.colVec, @@ -384,15 +266,6 @@ export const colvec = class ColVec extends BasePropDef { ): asserts value is any { throw new Error('Serialize colvec not implemented') } - override write( - buf: Uint8Array, - value: unknown, - offset: number, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ) { - throw new Error('Serialize colvec not implemented') - } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaColvec(buf, { type: PropTypeSelva.colVec, diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 1b40b2e1c6..480457b703 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -34,34 +34,36 @@ export class AutoSizedUint8Array { data: Uint8Array length: number - private readonly _maxCapacity: number + maxLength: number constructor( initialCapacity: number = 256, - maxCapacity: number = 1024 * 1024 * 1024, + maxLength: number = 1024 * 1024 * 1024, + data: Uint8Array = new Uint8Array( + new (ArrayBuffer as any)(initialCapacity, { + maxByteLength: maxLength, + }), + ), ) { - const buffer = new (ArrayBuffer as any)(initialCapacity, { - maxByteLength: maxCapacity, - }) - this.data = new Uint8Array(buffer) + this.data = data this.length = 0 - this._maxCapacity = maxCapacity + this.maxLength = maxLength } - private ensure(requiredCapacity: number): void { - const currentCapacity = this.data.byteLength - if (currentCapacity >= requiredCapacity) return - if (requiredCapacity > this._maxCapacity) { + private ensure(requiredLength: number): void { + const currentLength = this.data.byteLength + if (currentLength >= requiredLength) return + if (requiredLength > this.maxLength) { throw AutoSizedUint8Array.ERR_OVERFLOW } // Manual Max for speed - const doubleCapacity = currentCapacity * 2 + const doubleCapacity = currentLength * 2 const newCapacity = - requiredCapacity > doubleCapacity ? requiredCapacity : doubleCapacity - // Cap at maxCapacity + requiredLength > doubleCapacity ? requiredLength : doubleCapacity + // Cap at maxLength const finalCapacity = - newCapacity > this._maxCapacity ? this._maxCapacity : newCapacity + newCapacity > this.maxLength ? this.maxLength : newCapacity ;(this.data.buffer as any).resize(finalCapacity) } From 1766bdde934326db58ced6a8c0e4a6c2cf8bbbd0 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 11:12:10 +0100 Subject: [PATCH 061/449] clean --- src/schema/defs/props/references.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 7a75d6fbff..5705358315 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -14,7 +14,6 @@ import { type ModifyEnum, type PropTypeEnum, } from '../../../zigTsExports.js' -import { writeUint32 } from '../../../utils/index.js' import { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { SchemaProp, From 77ced98905cefbc30369016e04ea7f792e584a96 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 11:18:35 +0100 Subject: [PATCH 062/449] fix --- src/schema/defs/props/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 3a8bcbedd7..9777de8831 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -42,11 +42,11 @@ export class BasePropDef implements PropDef { ): void { if (sharedBuf) { sharedBuf.data = buf - sharedBuf.length = offset sharedBuf.maxLength = buf.length } else { sharedBuf = new AutoSizedUint8Array(0, buf.length, buf) } + sharedBuf.length = offset this.pushValue(sharedBuf, value, op, lang) } pushSelvaSchema(buf: AutoSizedUint8Array): void { From 471fd093a41871e65dea69bee8dbae07b3d91626 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 11:20:18 +0100 Subject: [PATCH 063/449] a little nicer --- src/schema/defs/props/base.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 9777de8831..31fa923cf9 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -40,13 +40,10 @@ export class BasePropDef implements PropDef { op: ModifyEnum = Modify.create, lang: LangCodeEnum = LangCode.none, ): void { - if (sharedBuf) { - sharedBuf.data = buf - sharedBuf.maxLength = buf.length - } else { - sharedBuf = new AutoSizedUint8Array(0, buf.length, buf) - } + sharedBuf ??= new AutoSizedUint8Array(0, 0, buf) + sharedBuf.data = buf sharedBuf.length = offset + sharedBuf.maxLength = buf.length this.pushValue(sharedBuf, value, op, lang) } pushSelvaSchema(buf: AutoSizedUint8Array): void { From 0fe5f9095e3bfa21e0e1f5a89d8f06e6fd1a6c39 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 11:42:28 +0100 Subject: [PATCH 064/449] update the main sorting --- src/schema/def/utils.ts | 14 +------------- src/schema/defs/getTypeDefs.ts | 2 +- test/modify/boolean.ts | 9 +++++++-- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/schema/def/utils.ts b/src/schema/def/utils.ts index 5ea71e2dbb..c2bc358471 100644 --- a/src/schema/def/utils.ts +++ b/src/schema/def/utils.ts @@ -112,21 +112,9 @@ export const sortMainProps = ( const sizeB = REVERSE_SIZE_MAP[b.typeIndex] if (sizeA === 8) return -1 if (sizeA === 4 && sizeB !== 8) return -1 - if (sizeA === 2 && sizeB !== 8) return -1 + if (sizeA === 2 && sizeB !== 4 && sizeB !== 8) return -1 if (sizeA === sizeB) return 0 return 1 - - // if (sizeA === 8) { - // return -1 - // } - // if (sizeA === 4 && sizeB !== 8) { - // return -1 - // } - - // if (sizeA === sizeB) { - // return 0 - // } - // return 1 } export const propIndexOffset = (prop: PropDef) => { diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 684865ba73..6f3a2a163f 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -10,7 +10,7 @@ import { defs, type PropDef, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 if (a.size === 4 && b.size !== 8) return -1 - if (a.size === 2 && a.size !== 4 && b.size !== 8) return -1 + if (a.size === 2 && b.size !== 4 && b.size !== 8) return -1 if (a.size === b.size) return 0 return 1 } diff --git a/test/modify/boolean.ts b/test/modify/boolean.ts index 2dd358effb..fc7e5789c0 100644 --- a/test/modify/boolean.ts +++ b/test/modify/boolean.ts @@ -59,18 +59,20 @@ await test('modify boolean on edge', async (t) => { }) const u1 = await db.create('user', { isNice: true }) - + console.log('???') const a = db.create('holder', { toUser: { id: u1, }, }) - const b = db.create('holder', { + console.log('???------') + const b = await db.create('holder', { toUser: { id: u1, $edgeBool: true, }, }) + console.log('=================') const c = db.create('holder', { toUser: { id: u1, @@ -85,6 +87,9 @@ await test('modify boolean on edge', async (t) => { .include('toUser.$edgeBool') .get() .toObject() + + console.dir(resA, { depth: null }) + deepEqual(resA.toUser?.$edgeBool, false) // Check b (true) From 1ba2d4e08e532b1ab6f2c6eb8579331c7e2e3b04 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 11:44:25 +0100 Subject: [PATCH 065/449] fix --- test/query-ast/include.ts | 64 +++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 3df2580a73..cad844a34c 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -85,6 +85,7 @@ await test('include', async (t) => { type: 'user', filter: { props: { + // 1 y: { ops: [{ op: '=', val: 0 }] }, // add reference // add references @@ -92,10 +93,24 @@ await test('include', async (t) => { }, // add AND or: { + // 4 props: { y: { ops: [{ op: '=', val: 67 }] }, }, }, + and: { + // 2 + props: { + y: { ops: [{ op: '=', val: 10 }] }, + }, + or: { + // 3 + props: { + y: { ops: [{ op: '=', val: 3 }] }, + }, + }, + }, + // ->4 1 ->3 2 ->4 3 }, props: { // name: { include: {} }, @@ -114,16 +129,16 @@ await test('include', async (t) => { // }, // }, // fix friends - // friends: { - // props: { - // name: { include: {} }, - // }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, - // }, + friends: { + props: { + name: { include: {} }, + }, + edges: { + props: { + $level: { include: {} }, + }, + }, + }, }, } @@ -137,10 +152,33 @@ await test('include', async (t) => { const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) // TODO - // references - // edges + + // filter + // AND + or (x.or.y).or(z) + // reference + edge + // refernces filters? + select + // now parsing in filters + // finish all operators (exist ,!nexist) how to handle for fixed? + // var filters + // like + // references includes + // includes + //. single ref or includes + //. eq STRING crc32 this is a seperate op /w branch check /w + + // include + // JS + // references select // TEXT - make this localized true - // OPTS + // meta + + // subscriptions MULTI + references + // subscriptions in modify + // subscription NOW reschedule + // now parsingio + + // Based-server / client + // ctx .get bug // console.dir(ctx, { depth: 10 }) From dc59cfe45197c8043f99f6d408e3d1fb03a96d7e Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 11:47:18 +0100 Subject: [PATCH 066/449] fix --- native/query/single.zig | 2 ++ test/query-ast/include.ts | 55 +++++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index eede2ca15f..3e5b6846b7 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -123,6 +123,8 @@ pub fn referenceEdge( const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); + std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); + if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index cad844a34c..7e1d340667 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -55,15 +55,15 @@ await test('include', async (t) => { }, }) - const b = client.create('user', { - name: 'BBBBBBBBB', - y: 67, - x: true, - flap: 9999, - cook: { - cookie: 1234, - }, - }) + // const b = client.create('user', { + // name: 'BBBBBBBBB', + // y: 67, + // x: true, + // flap: 9999, + // cook: { + // cookie: 1234, + // }, + // }) await client.create('user', { name: 'CCCCCCCCC', @@ -72,7 +72,7 @@ await test('include', async (t) => { }, y: 0, mrFriend: { id: a, $level: 67 }, - friends: [{ id: a, $level: 250 }, b], + // friends: [{ id: a, $level: 250 }, b], }) await db.drain() @@ -115,11 +115,16 @@ await test('include', async (t) => { props: { // name: { include: {} }, y: { include: {} }, - // mrFriend: { - // props: { - // y: { include: {} }, - // }, - + mrFriend: { + props: { + y: { include: {} }, + }, + edges: { + props: { + $level: { include: {} }, + }, + }, + }, // // EDGE // edges: { @@ -129,16 +134,16 @@ await test('include', async (t) => { // }, // }, // fix friends - friends: { - props: { - name: { include: {} }, - }, - edges: { - props: { - $level: { include: {} }, - }, - }, - }, + // friends: { + // props: { + // name: { include: {} }, + // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, + // }, }, } From c6df1e7ef598f9a57a2b5737fee64b066d90c4a9 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 11:54:47 +0100 Subject: [PATCH 067/449] edge ref --- native/modify/modify.zig | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index be62869c7b..7907a6ead7 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -94,13 +94,15 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u var refId = meta.id; if (meta.isTmp) refId = utils.read(u32, items, refId * resItemSize); if (Node.getNode(refTypeEntry, refId)) |dst| { - _ = try References.writeReference(db, node, propSchema, dst); - // std.debug.print("meta.size {any}\n", .{meta.size}); - if (meta.size != 0) { - const edgeProps = value[k .. k + meta.size]; - const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); - const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); - try modifyProps(db, edgeType, dst, edgeProps, items); + const ref = try References.writeReference(db, node, propSchema, dst); + if (ref) |r| { + if (meta.size != 0) { + const edgeProps = value[k .. k + meta.size]; + const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); + const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); + const edgeNode = try Node.ensureRefEdgeNode(db, node, edgeConstraint, r); + try modifyProps(db, edgeType, edgeNode, edgeProps, items); + } } } }, From 9fe2af6e950bcf96fcafdd84417d4fe6ed6e58e5 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 12:05:12 +0100 Subject: [PATCH 068/449] fix --- native/query/filter/filter.zig | 1 + native/query/single.zig | 3 +++ native/selva/references.zig | 1 + 3 files changed, 5 insertions(+) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 9ff8e5ee88..29d4b48ddc 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -149,6 +149,7 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { i = nextOrIndex; nextOrIndex = q.len; } else { + // i = nextIndex; } } diff --git a/native/query/single.zig b/native/query/single.zig index 3e5b6846b7..5e035fb476 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -120,6 +120,9 @@ pub fn referenceEdge( const nestedQuery = q[i.* .. i.* + header.includeSize]; try Include.include(node, ctx, nestedQuery, typeEntry); + // handle this case + // if ref.edge == 0 + const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); diff --git a/native/selva/references.zig b/native/selva/references.zig index 3d30115a3b..df9222de9b 100644 --- a/native/selva/references.zig +++ b/native/selva/references.zig @@ -90,6 +90,7 @@ pub fn ReferencesIteratorEdges(comptime desc: bool) type { if (edgeNode) |n2| { return ReferencesIteratorEdgesResult{ .node = n1, .edge = n2 }; } + // no edge but node exsits } } return null; From 3c1021cea8c2a4d324f0c7e31bca910c63df8dc3 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 13:30:30 +0100 Subject: [PATCH 069/449] add initial validate fn --- native/modify/modify.zig | 4 ++-- src/schema/defs/index.ts | 1 + src/schema/defs/props/base.ts | 18 ++++++++++++------ src/schema/defs/props/separate.ts | 3 --- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 7907a6ead7..27137216f6 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -95,8 +95,8 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u if (meta.isTmp) refId = utils.read(u32, items, refId * resItemSize); if (Node.getNode(refTypeEntry, refId)) |dst| { const ref = try References.writeReference(db, node, propSchema, dst); - if (ref) |r| { - if (meta.size != 0) { + if (meta.size != 0) { + if (ref) |r| { const edgeProps = value[k .. k + meta.size]; const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 0fd2962d9b..2482d7dbc9 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -48,6 +48,7 @@ export type PropDef = { ): void pushSelvaSchema(buf: AutoSizedUint8Array): void + validate(val: unknown, lang?: LangCodeEnum): void } export const isPropDef = (p: any): p is PropDef => { diff --git a/src/schema/defs/props/base.ts b/src/schema/defs/props/base.ts index 31fa923cf9..112b03034b 100644 --- a/src/schema/defs/props/base.ts +++ b/src/schema/defs/props/base.ts @@ -10,7 +10,8 @@ import { import { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import type { PropDef, TypeDef } from '../index.js' -let sharedBuf: AutoSizedUint8Array +let writeBuf: AutoSizedUint8Array +let validateBuf: AutoSizedUint8Array export class BasePropDef implements PropDef { constructor(schema: SchemaProp, path: string[], typeDef: TypeDef) { this.schema = schema @@ -40,11 +41,16 @@ export class BasePropDef implements PropDef { op: ModifyEnum = Modify.create, lang: LangCodeEnum = LangCode.none, ): void { - sharedBuf ??= new AutoSizedUint8Array(0, 0, buf) - sharedBuf.data = buf - sharedBuf.length = offset - sharedBuf.maxLength = buf.length - this.pushValue(sharedBuf, value, op, lang) + writeBuf ??= new AutoSizedUint8Array(0, 0, buf) + writeBuf.data = buf + writeBuf.length = offset + writeBuf.maxLength = buf.length + this.pushValue(writeBuf, value, op, lang) + } + validate(value: unknown, lang: LangCodeEnum = LangCode.none) { + validateBuf ??= new AutoSizedUint8Array() + validateBuf.length = 0 + this.pushValue(writeBuf, value, Modify.create, lang) } pushSelvaSchema(buf: AutoSizedUint8Array): void { // To be implemented by subclasses diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 1b5461e21f..2bb5d0ea63 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -196,11 +196,8 @@ export const cardinality = class Cardinality extends BasePropDef { } const items = value as any[] - if (items.length === 0) return - pushModifyCardinalityHeader(buf, this) - for (const item of items) { if (typeof item === 'string') { buf.reserveUint64() From 95fd3f2b243ba10c90ceb8c7ab49a540a6071838 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 13:57:59 +0100 Subject: [PATCH 070/449] remove log --- native/selva/node.zig | 2 -- src/db-client/modify/index.ts | 5 ++--- test/modify/boolean.ts | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/native/selva/node.zig b/native/selva/node.zig index 7d9edeba01..6e311410b6 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -156,9 +156,7 @@ pub inline fn getNodeFromReference(dstType: selva.Type, ref: anytype) ?Node { } pub inline fn ensureRefEdgeNode(db: *DbCtx, node: Node, efc: selva.EdgeFieldConstraint, ref: selva.ReferenceLarge) !Node { - std.debug.print("here we are?\n", .{}); const edgeNode = selva.c.selva_fields_ensure_ref_edge(db.selva, node, efc, ref, 0); - std.debug.print("here we are: {any}\n", .{edgeNode}); if (edgeNode) |n| { selva.markDirty(db, efc.edge_node_type, getNodeId(n)); return n; diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index f148966e6d..cd4cc62605 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -41,10 +41,9 @@ export const serializeProps = ( } } else { const prop = def as PropDef - if (prop.size) { + if (prop.id === 0) { // main - const increment = - val !== null && typeof val === 'object' && (val as any).increment + const increment = typeof val === 'object' && val?.increment pushModifyMainHeader(buf, { id: 0, start: prop.start, diff --git a/test/modify/boolean.ts b/test/modify/boolean.ts index fc7e5789c0..0fbd571d8a 100644 --- a/test/modify/boolean.ts +++ b/test/modify/boolean.ts @@ -59,20 +59,17 @@ await test('modify boolean on edge', async (t) => { }) const u1 = await db.create('user', { isNice: true }) - console.log('???') const a = db.create('holder', { toUser: { id: u1, }, }) - console.log('???------') const b = await db.create('holder', { toUser: { id: u1, $edgeBool: true, }, }) - console.log('=================') const c = db.create('holder', { toUser: { id: u1, From 3753135c0e350711096c721cdca9df98ddd42ab1 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 2 Feb 2026 14:05:43 +0100 Subject: [PATCH 071/449] validate perf --- package-lock.json | 62 +++++++++++++++++- package.json | 4 +- test/query-ast/include.ts | 12 ++++ test/query-ast/validate.perf.ts | 113 ++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 test/query-ast/validate.perf.ts diff --git a/package-lock.json b/package-lock.json index c4941b93e0..20b4d7c1f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@swc-node/register": "^1.11.1", "@types/node": "^22.5.3", "@types/react": "^18.3.1", + "arktype": "2.1.29", "async-sema": "3.1.1", "concurrently": "^9.2.1", "fs-extra": "^11.1.1", @@ -44,7 +45,8 @@ "ts-node": "^10.9.2", "tsdown": "^0.16.7", "tsx": "^4.20.6", - "typescript": "^5.6.3" + "typescript": "^5.6.3", + "valibot": "1.2.0" }, "engines": { "node": "24", @@ -56,6 +58,23 @@ "@esbuild/linux-x64": "^0.27.0" } }, + "node_modules/@ark/schema": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.56.0.tgz", + "integrity": "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/util": "0.56.0" + } + }, + "node_modules/@ark/util": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@ark/util/-/util-0.56.0.tgz", + "integrity": "sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/generator": { "version": "7.28.5", "dev": true, @@ -155,6 +174,10 @@ "@based/locale-x86-64-gnu": "*" } }, + "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { + "dev": true, + "optional": true + }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", @@ -1776,6 +1799,28 @@ "dev": true, "license": "MIT" }, + "node_modules/arkregex": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/arkregex/-/arkregex-0.0.5.tgz", + "integrity": "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/util": "0.56.0" + } + }, + "node_modules/arktype": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/arktype/-/arktype-2.1.29.tgz", + "integrity": "sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ark/schema": "0.56.0", + "@ark/util": "0.56.0", + "arkregex": "0.0.5" + } + }, "node_modules/ast-kit": { "version": "2.2.0", "dev": true, @@ -3131,6 +3176,21 @@ "dev": true, "license": "MIT" }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/validator": { "version": "13.15.23", "license": "MIT", diff --git a/package.json b/package.json index 3c05e6c19a..9c664c8f22 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,9 @@ "ts-node": "^10.9.2", "tsdown": "^0.16.7", "tsx": "^4.20.6", - "typescript": "^5.6.3" + "typescript": "^5.6.3", + "arktype": "2.1.29", + "valibot": "1.2.0" }, "exports": { ".": "./dist/sdk.js", diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 7e1d340667..3dc25d180c 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -158,6 +158,16 @@ await test('include', async (t) => { // TODO + // const bla = schema({ + // email: { type: 'string', format: 'email' }, + // subject: { type: 'string', max: 100 }, + // body: 'string' + // }) + + // const sendEmail = (x: bla) => { + // return sendgrid.sendEmail('beerdejim@gmail.com') + // } + // filter // AND + or (x.or.y).or(z) // reference + edge @@ -177,6 +187,8 @@ await test('include', async (t) => { // TEXT - make this localized true // meta + // SORT in js + // subscriptions MULTI + references // subscriptions in modify // subscription NOW reschedule diff --git a/test/query-ast/validate.perf.ts b/test/query-ast/validate.perf.ts new file mode 100644 index 0000000000..f41612fcad --- /dev/null +++ b/test/query-ast/validate.perf.ts @@ -0,0 +1,113 @@ +import * as v from 'valibot' +import { type } from 'arktype' + +// ========================================== +// 1. DATA GENERATION +// ========================================== +const validData = { + name: 'Performance Master', + id: 123456789, + isNice: true, + tags: [100, 200, 300, 400, 500], +} + +// ========================================== +// 2. RAW JS VALIDATOR +// ========================================== +function manualValidator(data: any): boolean { + if (!data || typeof data !== 'object') return false + + if (typeof data.name !== 'string' || data.name.length > 30) return false + + if (typeof data.id !== 'number' || data.id < 0 || data.id > 4000000000) + return false + + if (typeof data.isNice !== 'boolean') return false + + const tags = data.tags + if (!Array.isArray(tags) || tags.length > 10) return false + for (let i = 0; i < tags.length; i++) { + const t = tags[i] + if (typeof t !== 'number' || t < 0 || t > 4000) return false + } + + return true +} + +// ========================================== +// 3. VALIBOT SCHEMA +// ========================================== +const ValibotSchema = v.object({ + name: v.pipe(v.string(), v.maxLength(30)), + id: v.pipe(v.number(), v.minValue(0), v.maxValue(4000000000)), + isNice: v.boolean(), + tags: v.pipe( + v.array(v.pipe(v.number(), v.minValue(0), v.maxValue(4000))), + v.maxLength(10), + ), +}) + +// ========================================== +// 4. ARKTYPE SCHEMA +// ========================================== +const ArkTypeSchema = type({ + name: 'string<=30', + id: '0<=number<=4000000000', + isNice: 'boolean', + tags: '(0<=number<=4000)[] <= 10', +}) + +// ========================================== +// 5. SIMPLE BENCH RUNNER +// ========================================== + +function runBenchmark( + name: string, + fn: () => void, + iterations: number = 500_000, +) { + process.stdout.write(`Running ${name}... `) + + // 1. Warmup (Trigger JIT optimization) + for (let i = 0; i < 1000; i++) { + fn() + } + + // 2. Garbage Collection (Optional: helps stability if using standard node flags) + if (global.gc) global.gc() + + // 3. Measure + const start = Date.now() + for (let i = 0; i < iterations; i++) { + fn() + } + const end = Date.now() + + const totalTimeMs = end - start + const opsPerSec = (iterations / totalTimeMs) * 1000 + + // Format Output + const formatter = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }) + console.log(`${formatter.format(opsPerSec)} ops/sec (${totalTimeMs}ms)`) +} + +// ========================================== +// 6. EXECUTION +// ========================================== + +console.log('--- STARTING BENCHMARK ---\n') + +// Sanity Checks +if (!manualValidator(validData)) throw new Error('Manual Failed') +if (!v.safeParse(ValibotSchema, validData).success) + throw new Error('Valibot Failed') +if (ArkTypeSchema(validData) instanceof type.errors) + throw new Error('ArkType Failed') + +const ITERATIONS = 10_000_000 + +runBenchmark('Raw JS ', () => manualValidator(validData), ITERATIONS) +runBenchmark('ArkType ', () => ArkTypeSchema(validData), ITERATIONS) +runBenchmark('Valibot ', () => v.parse(ValibotSchema, validData), ITERATIONS) + +console.log('\n--- DONE ---') From 56b246e8b509059174922ec59067a64336f0be25 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 14:49:48 +0100 Subject: [PATCH 072/449] make default string work --- src/schema/defs/props/separate.ts | 15 +++++- test/modify/boolean.ts | 2 - test/modify/default.ts | 88 +++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 test/modify/default.ts diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 2bb5d0ea63..3c06a1427d 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -18,6 +18,7 @@ import { pushSelvaSchemaString, pushSelvaSchemaText, LangCode, + writeSelvaSchemaStringProps, } from '../../../zigTsExports.js' import { xxHash64 } from '../../../db-client/xxHash64.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' @@ -37,7 +38,7 @@ export const string = class String extends BasePropDef { this.pushValue = this.pushFixedValue as any } } - + declare schema: SchemaString override type: PropTypeEnum = PropType.string override pushValue( buf: AutoSizedUint8Array, @@ -82,14 +83,24 @@ export const string = class String extends BasePropDef { const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } + pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaString(buf, { + const index = pushSelvaSchemaString(buf, { type: PropTypeSelva.string, fixedLen: 0, defaultLen: 0, }) + if (this.schema.default) { + const start = buf.length + this.pushValue(buf, this.schema.default) + writeSelvaSchemaStringProps.defaultLen( + buf.data, + buf.length - start, + index, + ) + } } } diff --git a/test/modify/boolean.ts b/test/modify/boolean.ts index 0fbd571d8a..7bf8675bda 100644 --- a/test/modify/boolean.ts +++ b/test/modify/boolean.ts @@ -85,8 +85,6 @@ await test('modify boolean on edge', async (t) => { .get() .toObject() - console.dir(resA, { depth: null }) - deepEqual(resA.toUser?.$edgeBool, false) // Check b (true) diff --git a/test/modify/default.ts b/test/modify/default.ts new file mode 100644 index 0000000000..36cb804414 --- /dev/null +++ b/test/modify/default.ts @@ -0,0 +1,88 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('modify - default values basic', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: { type: 'string', default: 'Untitled' }, + score: { type: 'number', default: 100 }, + isActive: { type: 'boolean', default: true }, + }, + }, + }) + + // 1. Create with no values provided + const a = await db.create('thing', {}) + const resA = await db.query('thing', a).get() + deepEqual(resA, { id: a, name: 'Untitled', score: 100, isActive: true }) + + // 2. Create with specific values (override default) + const b = await db.create('thing', { + name: 'Specific', + score: 10, + isActive: false, + }) + const resB = await db.query('thing', b).get() + deepEqual(resB, { id: b, name: 'Specific', score: 10, isActive: false }) + + // 3. Create with mixed values + const c = await db.create('thing', { score: 50 }) + const resC = await db.query('thing', c).get() + deepEqual(resC, { id: c, name: 'Untitled', score: 50, isActive: true }) +}) + +await test('modify - default values on edge', async (t) => { + const db = await testDb(t, { + types: { + user: { + name: 'string', + }, + group: { + member: { + ref: 'user', + prop: 'groups', + $role: { type: 'string', default: 'member' }, + $level: { type: 'number', default: 1 }, + }, + }, + }, + }) + + const u1 = await db.create('user', { name: 'u1' }) + + // 1. Create edge without edge props + const g1 = await db.create('group', { + member: { id: u1 }, + }) + + const resG1 = await db + .query('group', g1) + .include('member.$role') + .include('member.$level') + .include('member.id') + .get() + + // @ts-ignore + deepEqual(resG1.member?.$role, 'member') + // @ts-ignore + deepEqual(resG1.member?.$level, 1) + + // 2. Create edge with edge props + const g2 = await db.create('group', { + member: { id: u1, $role: 'admin', $level: 99 }, + }) + + const resG2 = await db + .query('group', g2) + .include('member.$role') + .include('member.$level') + .include('member.id') + .get() + + // @ts-ignore + deepEqual(resG2.member?.$role, 'admin') + // @ts-ignore + deepEqual(resG2.member?.$level, 99) +}) From b6d4c4e059bb82b715d7db832747f0703d35d110 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 16:06:22 +0100 Subject: [PATCH 073/449] add inverse props for references on edges --- native/query/single.zig | 2 - src/protocol/db-read/read.ts | 1 - src/schema/defs/getTypeDefs.ts | 92 ++++++++++++++++++++------------- src/schema/schema/reference.ts | 26 ++++++---- src/schema/schema/references.ts | 4 +- test/modify/references.ts | 3 +- 6 files changed, 75 insertions(+), 53 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index 5e035fb476..97457836c0 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -126,8 +126,6 @@ pub fn referenceEdge( const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); - std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); - if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); diff --git a/src/protocol/db-read/read.ts b/src/protocol/db-read/read.ts index 18b0b07d3e..bca63289b9 100644 --- a/src/protocol/db-read/read.ts +++ b/src/protocol/db-read/read.ts @@ -109,7 +109,6 @@ const references: ReadInstruction = (q, result, i, item) => { } const edge: ReadInstruction = (q, result, i, item) => { - console.log('edge', i) return readInstruction(result[i], q.edges!, result, i + 1, item) } diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 6f3a2a163f..78ce84627f 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -5,7 +5,7 @@ import { type SchemaType, } from '../../schema.js' import { PropType } from '../../zigTsExports.js' -import { defs, type PropDef, type TypeDef } from './index.js' +import { defs, type PropDef, type PropTree, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 @@ -46,6 +46,25 @@ export const propIndexOffset = (prop: PropDef): number => { const separateSorter = (a, b) => propIndexOffset(a) - propIndexOffset(b) +const addPropDef = ( + prop: SchemaProp, + path: string[], + typeDef: TypeDef, +) => { + const Def = defs[prop.type] + if (!Def) { + throw 'unknown def' + } + + const def: PropDef = new Def(prop, path, typeDef) + if (def.size) { + typeDef.main.push(def) + } else { + typeDef.separate.push(def) + } + return def +} + const getTypeDef = (schema: SchemaType): TypeDef => { const { props } = schema const typeDef: TypeDef = { @@ -70,44 +89,16 @@ const getTypeDef = (schema: SchemaType): TypeDef => { const branch = new Map() walk(prop.props, path, branch) tree.set(key, branch) - continue - } - - const Def = defs[prop.type] - if (!Def) { - console.error('unknown def') - // TODO: handle missing type - continue - } - - const def = new Def(prop, path, typeDef) - if (def.size) { - typeDef.main.push(def) } else { - typeDef.separate.push(def) + const def = addPropDef(prop, path, typeDef) + typeDef.props.set(path.join('.'), def) + tree.set(key, def) } - typeDef.props.set(path.join('.'), def) - tree.set(key, def) } } walk(props, [], typeDef.tree) - // -------- sort and assign main -------- - typeDef.main.sort(mainSorter) - let start = 0 - for (const prop of typeDef.main) { - prop.start = start - start += prop.size - } - - // -------- sort and assign separate --------- - typeDef.separate.sort(separateSorter) - let propId = 1 - for (const prop of typeDef.separate) { - prop.id = propId++ - } - return typeDef } @@ -130,10 +121,24 @@ export const getTypeDefs = (schema: SchemaOut): Map => { def.schema.type === 'references' ? def.schema.items : def.schema if (prop.type !== 'reference') continue def.ref = typeDefs.get(prop.ref)! - if (!prop.prop) { - continue + if (prop.prop) { + def.refProp = def.ref.props.get(prop.prop)! + } else { + def.refProp = addPropDef( + { + type: 'references', + items: { + type: 'reference', + ref: typeName, + prop: propPath, + }, + }, + [`${typeName}.${propPath}`], + def.ref, + ) + def.refProp.ref = typeDef + def.refProp.refProp = def } - def.refProp = def.ref.props.get(prop.prop)! const inverseEdges = def.refProp.edges if (inverseEdges) { def.edges = inverseEdges @@ -153,6 +158,23 @@ export const getTypeDefs = (schema: SchemaOut): Map => { } } + for (const [, typeDef] of typeDefs) { + // -------- sort and assign main -------- + typeDef.main.sort(mainSorter) + let start = 0 + for (const prop of typeDef.main) { + prop.start = start + start += prop.size + } + + // -------- sort and assign separate --------- + typeDef.separate.sort(separateSorter) + let propId = 1 + for (const prop of typeDef.separate) { + prop.id = propId++ + } + } + // ----------- add to cache -------- cache.set(schema, typeDefs) return typeDefs diff --git a/src/schema/schema/reference.ts b/src/schema/schema/reference.ts index 905f6274bf..0156a8eb27 100644 --- a/src/schema/schema/reference.ts +++ b/src/schema/schema/reference.ts @@ -11,20 +11,24 @@ import { parseProp, type SchemaProp } from './prop.js' import type { SchemaReferences } from './references.js' import type { SchemaOut } from './schema.js' -type EdgeExcludedProps = 'prop' | `$${string}` +type ReferenceProps = nested extends true + ? { prop?: never; dependent?: never; [edge: `$${string}`]: never } + : { + prop: string + dependent?: boolean + [edge: `$${string}`]: + | Exclude< + SchemaProp, + SchemaReferences | SchemaReference + > + | SchemaReferences + | SchemaReference + } -export type SchemaReference = Base & +export type SchemaReference = Base & RequiredIfStrict<{ type: 'reference' }, strict> & { ref: string - } & { - prop: string - dependent?: boolean - [edge: `$${string}`]: - | Exclude, SchemaReferences> - | (Omit, 'items'> & { - items: Omit, EdgeExcludedProps> - }) - } + } & ReferenceProps let parsingEdges: boolean export const parseReference = ( diff --git a/src/schema/schema/references.ts b/src/schema/schema/references.ts index 86e4a9398b..0840317095 100644 --- a/src/schema/schema/references.ts +++ b/src/schema/schema/references.ts @@ -3,10 +3,10 @@ import { parseReference, type SchemaReference } from './reference.js' import type { SchemaOut } from './schema.js' import { assert, isRecord, type RequiredIfStrict } from './shared.js' -export type SchemaReferences = Base & +export type SchemaReferences = Base & RequiredIfStrict<{ type: 'references' }, strict> & { capped?: number - items: Omit, keyof Base> + items: Omit, keyof Base> } export const parseReferences = ( diff --git a/test/modify/references.ts b/test/modify/references.ts index c34e7eb99a..6bd68fc5b4 100644 --- a/test/modify/references.ts +++ b/test/modify/references.ts @@ -174,7 +174,6 @@ await test('modify single reference on edge', async (t) => { $edgeRef: { type: 'reference', ref: 'thing', - prop: 'edgeRefHolders', }, }, }, @@ -239,7 +238,7 @@ await test('modify references on edge', async (t) => { type: 'references', items: { ref: 'thing', - prop: 'edgeRefsHolders', + // prop: 'edgeRefsHolders', }, }, }, From a50464308cb42604af7df726e0ef9800ea362cf1 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 2 Feb 2026 16:11:35 +0100 Subject: [PATCH 074/449] Automatically create edge nodes on ref creation --- clibs/include/selva/fields.h | 12 +-- clibs/lib/selva/fields.c | 143 ++++++++++------------------------- native/modify/edges.zig | 2 +- native/modify/modify.zig | 7 +- native/selva/fields.zig | 8 +- native/selva/node.zig | 13 +--- 6 files changed, 58 insertions(+), 127 deletions(-) diff --git a/clibs/include/selva/fields.h b/clibs/include/selva/fields.h index 9b0759d095..026e7be49a 100644 --- a/clibs/include/selva/fields.h +++ b/clibs/include/selva/fields.h @@ -98,18 +98,14 @@ size_t selva_fields_get_data_size(const struct SelvaFieldSchema *fs); #endif void *selva_fields_nfo2p(struct SelvaFields *fields, const struct SelvaFieldInfo *nfo); +/** + * Ensure that we have a reference struct. + * NOTE: This function doesn't create the necessary edge node. + */ struct SelvaNodeLargeReference *selva_fields_ensure_reference( struct SelvaNode *node, const struct SelvaFieldSchema *fs); -SELVA_EXPORT -struct SelvaNode *selva_fields_ensure_ref_edge( - struct SelvaDb *db, - struct SelvaNode *node, - const struct EdgeFieldConstraint *efc, - struct SelvaNodeLargeReference *ref, - node_id_t edge_id); - SELVA_EXPORT int selva_fields_get_mutable_string( struct SelvaNode *node, diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 18504ff02d..541876312e 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -24,6 +24,7 @@ #define selva_sallocx(p, v) 0 #endif +static struct SelvaNode *next_ref_edge_node(struct SelvaTypeEntry *edge_type); static void reference_edge_destroy( struct SelvaDb *db, const struct EdgeFieldConstraint *efc, @@ -337,13 +338,20 @@ static void remove_refs_offset(struct SelvaNodeReferences *refs) /** * Write a ref to the fields data. * Note that this function doesn't touch the destination node. + * @param edge can be null. */ -static void write_ref(struct SelvaNode * restrict node, const struct SelvaFieldSchema *fs, struct SelvaNode * restrict dst, struct SelvaNodeLargeReference **ref_out) +static void write_ref( + struct SelvaNode * restrict node, + const struct SelvaFieldSchema *fs, + struct SelvaNode * restrict dst, + struct SelvaNode * restrict edge, + struct SelvaNodeLargeReference **ref_out) { struct SelvaFields *fields = &node->fields; struct SelvaFieldInfo *nfo; struct SelvaNodeLargeReference ref = { .dst = dst->node_id, + .edge = edge ? edge->node_id : 0, }; nfo = ensure_field(fields, fs); @@ -361,8 +369,15 @@ static void write_ref(struct SelvaNode * restrict node, const struct SelvaFieldS /** * Write a ref to the fields data. * Note that this function doesn't touch the destination node. + * @param edge can be null. */ -static void write_refs(struct SelvaNode * restrict node, const struct SelvaFieldSchema *fs, ssize_t index, struct SelvaNode * restrict dst, struct SelvaNodeReferenceAny *ref_out) +static void write_refs( + struct SelvaNode * restrict node, + const struct SelvaFieldSchema *fs, + ssize_t index, + struct SelvaNode * restrict dst, + struct SelvaNode * restrict edge, + struct SelvaNodeReferenceAny *ref_out) { struct SelvaFields *fields = &node->fields; void *vp = nfo2p(fields, &fields->fields_map[fs->field]); @@ -393,6 +408,7 @@ static void write_refs(struct SelvaNode * restrict node, const struct SelvaField refs.large--; refs.large[0] = (struct SelvaNodeLargeReference){ .dst = dst->node_id, + .edge = edge ? edge->node_id : 0, }; break; default: @@ -472,6 +488,7 @@ static void write_refs(struct SelvaNode * restrict node, const struct SelvaField case SELVA_NODE_REFERENCE_LARGE: refs.large[index] = (struct SelvaNodeLargeReference){ .dst = dst->node_id, + .edge = edge ? edge->node_id : 0, }; break; default: @@ -692,34 +709,44 @@ static void clear_ref_dst(struct SelvaDb *db, const struct SelvaFieldSchema *fs_ * add_to_refs_index() must be called before this function. */ static inline void write_ref_2way( + struct SelvaDb *db, struct SelvaNode * restrict src, const struct SelvaFieldSchema *fs_src, ssize_t index, struct SelvaNode * restrict dst, const struct SelvaFieldSchema *fs_dst, struct SelvaNodeReferenceAny *ref_out) { + struct SelvaNode *edge = nullptr; + + if (refs_get_type(db, &fs_src->edge_constraint) == SELVA_NODE_REFERENCE_LARGE) { + struct SelvaTypeEntry *edge_type; + + edge_type = selva_get_type_by_index(db, fs_src->edge_constraint.edge_node_type); + edge = next_ref_edge_node(edge_type); + } + #if 0 assert(fs_src->edge_constraint.dst_node_type == dst->type); assert(fs_dst->edge_constraint.dst_node_type == src->type); #endif if (fs_src->type == SELVA_FIELD_TYPE_REFERENCE) { - write_ref(src, fs_src, dst, ref_out ? &ref_out->large : nullptr); + write_ref(src, fs_src, dst, edge, ref_out ? &ref_out->large : nullptr); ref_out->type = SELVA_NODE_REFERENCE_LARGE; } else { #if 0 assert(fs_src->type == SELVA_FIELD_TYPE_REFERENCES); #endif assume(fs_src->type == SELVA_FIELD_TYPE_REFERENCES); - write_refs(src, fs_src, index, dst, ref_out); + write_refs(src, fs_src, index, dst, edge, ref_out); } if (fs_dst->type == SELVA_FIELD_TYPE_REFERENCE) { - write_ref(dst, fs_dst, src, nullptr); + write_ref(dst, fs_dst, src, edge, nullptr); } else { #if 0 assert(fs_dst->type == SELVA_FIELD_TYPE_REFERENCES); #endif assume(fs_dst->type == SELVA_FIELD_TYPE_REFERENCES); - write_refs(dst, fs_dst, -1, src, nullptr); + write_refs(dst, fs_dst, -1, src, edge, nullptr); } } @@ -1287,7 +1314,7 @@ int selva_fields_references_insert( } assume(fs->type == SELVA_FIELD_TYPE_REFERENCES); - write_ref_2way(node, fs, index, dst, fs_dst, ref_out); + write_ref_2way(db, node, fs, index, dst, fs_dst, ref_out); if (fs->edge_constraint.limit > 0) { (void)remove_references_tail(db, node, fs, fs->edge_constraint.limit); } @@ -1391,7 +1418,7 @@ int selva_fields_reference_set( } assume(fs_src->type == SELVA_FIELD_TYPE_REFERENCE); - write_ref_2way(src, fs_src, -1, dst, fs_dst, ref_out); + write_ref_2way(db, src, fs_src, -1, dst, fs_dst, ref_out); if (fs_dst->type == SELVA_FIELD_TYPE_REFERENCES && fs_dst->edge_constraint.limit > 0) { (void)remove_references_tail(db, dst, fs_dst, fs_dst->edge_constraint.limit); } @@ -1525,7 +1552,7 @@ static void selva_fields_references_insert_tail_insert_refs( assert(fs_dst->type == SELVA_FIELD_TYPE_REFERENCES); #endif assume(fs_dst->type == SELVA_FIELD_TYPE_REFERENCES); - write_ref_2way(src, fs_src, -1, dst, fs_dst, nullptr); + write_ref_2way(db, src, fs_src, -1, dst, fs_dst, nullptr); if (fs_dst->edge_constraint.limit > 0) { (void)remove_references_tail(db, dst, fs_dst, fs_dst->edge_constraint.limit); } @@ -1544,7 +1571,7 @@ static void selva_fields_references_insert_tail_insert_ref( #endif assume(fs_dst->type == SELVA_FIELD_TYPE_REFERENCE); (void)remove_reference(db, dst, fs_dst, 0, -1, false); - write_ref_2way(src, fs_src, -1, dst, fs_dst, nullptr); + write_ref_2way(db, src, fs_src, -1, dst, fs_dst, nullptr); } int selva_fields_references_insert_tail( @@ -1801,102 +1828,16 @@ static struct SelvaNode *next_ref_edge_node(struct SelvaTypeEntry *edge_type) edge = selva_upsert_node(edge_type, next_id); /* TODO Support partial edges */ - assert(edge.node && edge.block_status & SELVA_TYPE_BLOCK_STATUS_INMEM); + + constexpr enum SelvaTypeBlockStatus mask = SELVA_TYPE_BLOCK_STATUS_INMEM | SELVA_TYPE_BLOCK_STATUS_DIRTY; + assert(edge.node && (edge.block_status & mask) == mask); +#if 0 selva_mark_dirty(edge_type, next_id); +#endif return edge.node; } -/** - * Create edgeNode if it's not initialized yet. - * Most importantly this function makes sure that the object is shared between - * both ends of the edge. - * @param edge_id Node id of the edge edgeNode. 0 if a new one should be assigned. - */ -struct SelvaNode *selva_fields_ensure_ref_edge( - struct SelvaDb *db, - struct SelvaNode *node, - const struct EdgeFieldConstraint *efc, - struct SelvaNodeLargeReference *ref, - node_id_t edge_id) -{ - struct SelvaTypeEntry *edge_type = selva_get_type_by_index(db, efc->edge_node_type); - struct SelvaNode *edge = nullptr; - - if (!edge_type) { - return nullptr; - } - - /* RFE what to do if there was an existing edge? */ - if (ref->edge != 0 && edge_id == 0) { - /* TODO Partials will require upsert here! */ - edge = selva_find_node(edge_type, ref->edge).node; - assert(edge); - } else if (ref->edge == 0 || edge_id != 0) { - edge = (edge_id != 0) - ? selva_upsert_node(edge_type, edge_id).node /* TODO Partials */ - : next_ref_edge_node(edge_type); - if (!edge) { - return nullptr; - } - - edge_id = edge->node_id; - ref->edge = edge_id; - /* FIXME Do a little refcount +2 */ - - struct SelvaTypeEntry *type_dst = selva_get_type_by_index(db, efc->dst_node_type); - const struct SelvaFieldSchema *fs_dst = selva_get_fs_by_te_field(type_dst, efc->inverse_field); - const struct SelvaNodeRes dst_res = selva_find_node(type_dst, ref->dst); - constexpr enum SelvaTypeBlockStatus mask = SELVA_TYPE_BLOCK_STATUS_FS | SELVA_TYPE_BLOCK_STATUS_INMEM; - if ((dst_res.block_status & mask) == SELVA_TYPE_BLOCK_STATUS_FS) { - /* TODO load the block instead of crashing. partials */ - db_panic("Block %u:%u needs to be loaded", - (unsigned)type_dst->type, (unsigned)dst_res.block); - } else if (!dst_res.node) { - db_panic("FIXME dangling reference"); - } - - struct SelvaNode *dst = dst_res.node; - struct SelvaFields *dst_fields = &dst->fields; - assert(efc->inverse_field < dst_fields->nr_fields); - const struct SelvaFieldInfo *dst_nfo = &dst_fields->fields_map[efc->inverse_field]; - - if (unlikely(!dst_nfo->in_use)) { - db_panic("dst field missing"); - } - - /* - * Share the edge fields with the destination node - * i.e. set it at the other end of the edge. - */ - if (fs_dst->type == SELVA_FIELD_TYPE_REFERENCE) { - struct SelvaNodeLargeReference *dst_ref = nfo2p(dst_fields, dst_nfo); - - dst_ref->edge = ref->edge; - } else if (fs_dst->type == SELVA_FIELD_TYPE_REFERENCES) { - struct SelvaNodeReferences refs; - node_id_t src_node_id = node->node_id; - ssize_t i; - - memcpy(&refs, nfo2p(dst_fields, dst_nfo), sizeof(refs)); - assert(refs.size == SELVA_NODE_REFERENCE_LARGE); - - i = fast_linear_search_references_large(refs.large, refs.nr_refs, src_node_id); - if (unlikely(i < 0)) { - db_panic("src not found in dst"); - } - - refs.large[i].edge = ref->edge; - } else { - db_panic("Invalid inverse field type: %d", fs_dst->type); - } - - selva_mark_dirty(selva_get_type_by_index(db, efc->dst_node_type), ref->dst); - } - - return edge; -} - struct SelvaNodeLargeReference *selva_fields_get_reference(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { struct SelvaFields *fields = &node->fields; diff --git a/native/modify/edges.zig b/native/modify/edges.zig index 11817edd38..5466d87ed1 100644 --- a/native/modify/edges.zig +++ b/native/modify/edges.zig @@ -29,7 +29,7 @@ pub fn writeEdges( ) !void { var i: usize = 0; const edgeConstraint = Schema.getEdgeFieldConstraint(ctx.fieldSchema.?); - const edgeNode = try Node.ensureRefEdgeNode(ctx, ctx.node.?, edgeConstraint, ref); + const edgeNode = Node.getEdgeNode(ctx.db, edgeConstraint, ref); const edgeId = ref.*.edge; const edgeTypeId = edgeConstraint.*.edge_node_type; if (edgeId > ctx.db.ids[edgeTypeId - 1]) { diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 27137216f6..8f7ecf6c1b 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -142,9 +142,10 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u if (meta.size != 0) { const edgeProps = refs[x .. x + meta.size]; const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); - const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); - const edgeNode = try Node.ensureRefEdgeNode(db, node, edgeConstraint, ref.p.large); - try modifyProps(db, edgeType, edgeNode, edgeProps, items); + if (Node.getEdgeNode(db, edgeConstraint, ref.p.large)) |edgeNode| { + const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); + try modifyProps(db, edgeType, edgeNode, edgeProps, items); + } // TODO else err? } } diff --git a/native/selva/fields.zig b/native/selva/fields.zig index 1b48d32b6a..fa3c65f906 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -125,13 +125,15 @@ pub fn ensurePropTypeString( pub fn ensureEdgePropTypeString( ctx: *Modify.ModifyCtx, - node: Node.Node, efc: Schema.EdgeFieldConstraint, ref: References.ReferenceLarge, fieldSchema: Schema.FieldSchema, ) !*selva.c.selva_string { - const edge_node = selva.c.selva_fields_ensure_ref_edge(ctx.db.selva, node, efc, ref, 0) orelse return errors.SelvaError.SELVA_ENOTSUP; - return selva.c.selva_fields_ensure_string(edge_node, fieldSchema, selva.c.HLL_INIT_SIZE) orelse return errors.SelvaError.SELVA_EINTYPE; + if (Node.getEdgeNode(ctx.db, efc, ref)) |edgeNode| { + return selva.c.selva_fields_ensure_string(edgeNode, fieldSchema, selva.c.HLL_INIT_SIZE) orelse return errors.SelvaError.SELVA_EINTYPE; + } else { + return errors.SelvaError.SELVA_ENOENT; + } } pub inline fn deleteField(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Schema.FieldSchema) !void { diff --git a/native/selva/node.zig b/native/selva/node.zig index 6e311410b6..0a80dbf5c9 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -155,23 +155,14 @@ pub inline fn getNodeFromReference(dstType: selva.Type, ref: anytype) ?Node { return null; } -pub inline fn ensureRefEdgeNode(db: *DbCtx, node: Node, efc: selva.EdgeFieldConstraint, ref: selva.ReferenceLarge) !Node { - const edgeNode = selva.c.selva_fields_ensure_ref_edge(db.selva, node, efc, ref, 0); - if (edgeNode) |n| { - selva.markDirty(db, efc.edge_node_type, getNodeId(n)); - return n; - } else { - return errors.SelvaError.SELVA_ENOTSUP; - } -} - pub inline fn getEdgeNode(db: *DbCtx, efc: selva.EdgeFieldConstraint, ref: selva.ReferenceLarge) ?Node { if (ref.*.edge == 0) { return null; } const edge_type = selva.c.selva_get_type_by_index(db.selva, efc.*.edge_node_type); - return selva.c.selva_find_node(edge_type, ref.*.edge); + // TODO Partials + return selva.c.selva_find_node(edge_type, ref.*.edge).node; } pub inline fn deleteNode(db: *DbCtx, typeEntry: Type, node: Node) !void { From e6512c58041db9c4e86613d07d50a3e2d735c7ad Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 16:18:46 +0100 Subject: [PATCH 075/449] update --- src/schema/defs/getTypeDefs.ts | 2 +- test/modify/default.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 78ce84627f..b0ff788b3a 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -5,7 +5,7 @@ import { type SchemaType, } from '../../schema.js' import { PropType } from '../../zigTsExports.js' -import { defs, type PropDef, type PropTree, type TypeDef } from './index.js' +import { defs, type PropDef, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 diff --git a/test/modify/default.ts b/test/modify/default.ts index 36cb804414..f9a7567f7a 100644 --- a/test/modify/default.ts +++ b/test/modify/default.ts @@ -63,10 +63,9 @@ await test('modify - default values on edge', async (t) => { .include('member.$level') .include('member.id') .get() + .toObject() - // @ts-ignore deepEqual(resG1.member?.$role, 'member') - // @ts-ignore deepEqual(resG1.member?.$level, 1) // 2. Create edge with edge props @@ -80,9 +79,8 @@ await test('modify - default values on edge', async (t) => { .include('member.$level') .include('member.id') .get() + .toObject() - // @ts-ignore deepEqual(resG2.member?.$role, 'admin') - // @ts-ignore deepEqual(resG2.member?.$level, 99) }) From 135de7974f59d97c8576a2bbe22f75fd6eb99058 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 2 Feb 2026 16:18:49 +0100 Subject: [PATCH 076/449] ensureRefEdgeNode was removed --- native/modify/modify.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 8f7ecf6c1b..f932b8d8c0 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -100,8 +100,9 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u const edgeProps = value[k .. k + meta.size]; const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); - const edgeNode = try Node.ensureRefEdgeNode(db, node, edgeConstraint, r); - try modifyProps(db, edgeType, edgeNode, edgeProps, items); + if (Node.getEdgeNode(db, edgeConstraint, r)) |edgeNode| { + try modifyProps(db, edgeType, edgeNode, edgeProps, items); + } // TODO else error? } } } From d2479fa8eae499fd55341526cf1767c6370aef21 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 17:11:10 +0100 Subject: [PATCH 077/449] init upsert --- native/types.zig | 7 ++ src/db-client/index.ts | 32 ++++-- src/db-client/modify/create.ts | 32 ++++++ src/db-client/modify/delete.ts | 21 ++++ src/db-client/modify/index.ts | 136 +++----------------------- src/db-client/modify/props.ts | 54 ++++++++++ src/db-client/modify/update.ts | 34 +++++++ src/db-client/modify/upsert.ts | 39 ++++++++ src/schema/defs/props/references.ts | 2 +- src/zigTsExports.ts | 76 +++++++++++++- test/modify/{ => props}/boolean.ts | 0 test/modify/{ => props}/default.ts | 0 test/modify/{ => props}/numbers.ts | 0 test/modify/{ => props}/references.ts | 0 test/modify/{ => props}/string.ts | 0 test/modify/{ => props}/timestamp.ts | 0 test/modify/upsert.ts | 28 ++++++ 17 files changed, 330 insertions(+), 131 deletions(-) create mode 100644 src/db-client/modify/create.ts create mode 100644 src/db-client/modify/delete.ts create mode 100644 src/db-client/modify/props.ts create mode 100644 src/db-client/modify/update.ts create mode 100644 src/db-client/modify/upsert.ts rename test/modify/{ => props}/boolean.ts (100%) rename test/modify/{ => props}/default.ts (100%) rename test/modify/{ => props}/numbers.ts (100%) rename test/modify/{ => props}/references.ts (100%) rename test/modify/{ => props}/string.ts (100%) rename test/modify/{ => props}/timestamp.ts (100%) create mode 100644 test/modify/upsert.ts diff --git a/native/types.zig b/native/types.zig index cbf4cdafb0..71683dd14c 100644 --- a/native/types.zig +++ b/native/types.zig @@ -81,6 +81,7 @@ pub const Modify = enum(u8) { create = 0, update = 1, delete = 2, + upsert = 3, }; pub const ModifyHeader = packed struct { @@ -99,6 +100,12 @@ pub const ModifyUpdateHeader = packed struct { size: u32, }; +pub const ModifyUpsertHeader = packed struct { + op: Modify, + type: u8, + size: u32, +}; + pub const ModifyDeleteHeader = packed struct { op: Modify, type: u8, diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 44f64fa8f5..7887b8deef 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -14,15 +14,12 @@ import { } from '../schema/index.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' import { LangCode } from '../zigTsExports.js' -import { - serializeCreate, - serializeDelete, - serializeUpdate, - ModifyCtx, - flush, - BasedModify, -} from './modify/index.js' +import { ModifyCtx, flush, BasedModify } from './modify/index.js' import type { InferPayload } from './modify/types.js' +import { serializeCreate } from './modify/create.js' +import { serializeUpdate } from './modify/update.js' +import { serializeDelete } from './modify/delete.js' +import { serializeUpsert } from './modify/upsert.js' type DbClientOpts = { hooks: DbClientHooks @@ -34,6 +31,7 @@ type DbClientOpts = { type BasedCreatePromise = BasedModify type BasedUpdatePromise = BasedModify type BasedDeletePromise = BasedModify +type BasedUpsertPromise = BasedModify export type ModifyOpts = { unsafe?: boolean @@ -129,6 +127,24 @@ export class DbClient = SchemaOut> extends DbShared { ) } + upsert( + type: T, + target: InferPayload[T], + obj: InferPayload[T], + opts?: ModifyOpts, + ): BasedUpsertPromise { + return new BasedModify( + this.modifyCtx, + serializeUpsert, + this.schema!, + type, + target, + obj, + this.modifyCtx.buf, + opts?.locale ? LangCode[opts.locale] : LangCode.none, + ) + } + delete( type: keyof S['types'] & string, target: number | BasedModify, diff --git a/src/db-client/modify/create.ts b/src/db-client/modify/create.ts new file mode 100644 index 0000000000..ff470c1f12 --- /dev/null +++ b/src/db-client/modify/create.ts @@ -0,0 +1,32 @@ +import type { SchemaOut } from '../../schema.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { + Modify, + pushModifyCreateHeader, + writeModifyCreateHeaderProps, + type LangCodeEnum, +} from '../../zigTsExports.js' +import { getTypeDef } from './index.js' +import { serializeProps } from './props.js' +import type { InferPayload } from './types.js' + +export const serializeCreate = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyCreateHeader(buf, { + op: Modify.create, + type: typeDef.id, + size: 0, + }) + const start = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) +} diff --git a/src/db-client/modify/delete.ts b/src/db-client/modify/delete.ts new file mode 100644 index 0000000000..380765b057 --- /dev/null +++ b/src/db-client/modify/delete.ts @@ -0,0 +1,21 @@ +import type { SchemaOut } from '../../schema.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { Modify, pushModifyDeleteHeader } from '../../zigTsExports.js' +import { assignTarget, BasedModify, getTypeDef } from './index.js' + +export const serializeDelete = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + item: number | BasedModify, + buf: AutoSizedUint8Array, +) => { + const typeDef = getTypeDef(schema, type) + const header = assignTarget(item, { + op: Modify.delete, + type: typeDef.id, + }) + pushModifyDeleteHeader(buf, header) +} diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index cd4cc62605..13cc73be11 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -1,72 +1,25 @@ import { SchemaOut } from '../../schema.js' import { Modify, - ModifyIncrement, - pushModifyCreateHeader, pushModifyDeleteHeader, pushModifyHeader, - pushModifyMainHeader, - pushModifyPropHeader, pushModifyUpdateHeader, - writeModifyCreateHeaderProps, writeModifyHeaderProps, - writeModifyPropHeaderProps, writeModifyUpdateHeaderProps, type LangCodeEnum, - type ModifyEnum, } from '../../zigTsExports.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import type { PropDef, PropTree } from '../../schema/defs/index.js' import { InferPayload } from './types.js' import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' import { readUint32 } from '../../utils/uint8.js' +import { serializeProps } from './props.js' +import type { serializeCreate } from './create.js' +import type { serializeUpdate } from './update.js' +import type { serializeDelete } from './delete.js' +import type { serializeUpsert } from './upsert.js' export { getTypeDefs } -export const serializeProps = ( - tree: PropTree, - data: any, - buf: AutoSizedUint8Array, - op: ModifyEnum, - lang: LangCodeEnum, -) => { - for (const key in data) { - const def = tree.get(key) - if (def === undefined) { - continue - } - const val = data[key] - if (def.constructor === Map) { - if (val !== null && typeof val === 'object') { - serializeProps(def, val, buf, op, lang) - } - } else { - const prop = def as PropDef - if (prop.id === 0) { - // main - const increment = typeof val === 'object' && val?.increment - pushModifyMainHeader(buf, { - id: 0, - start: prop.start, - type: prop.type, - increment: increment - ? increment < 0 - ? ModifyIncrement.decrement - : ModifyIncrement.increment - : ModifyIncrement.none, - }) - prop.pushValue(buf, increment ? Math.abs(increment) : val, op, lang) - } else { - // separate - const index = pushModifyPropHeader(buf, prop) - const start = buf.length - prop.pushValue(buf, val, op, lang) - writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) - } - } - } -} - -const getTypeDef = (schema: SchemaOut, type: string) => { +export const getTypeDef = (schema: SchemaOut, type: string) => { const typeDef = getTypeDefs(schema).get(type) if (!typeDef) { throw new Error(`Type ${type} not found`) @@ -74,27 +27,6 @@ const getTypeDef = (schema: SchemaOut, type: string) => { return typeDef } -export const serializeCreate = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - payload: InferPayload[T], - buf: AutoSizedUint8Array, - lang: LangCodeEnum, -) => { - const typeDef = getTypeDef(schema, type) - const index = pushModifyCreateHeader(buf, { - op: Modify.create, - type: typeDef.id, - size: 0, - }) - const start = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.create, lang) - writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) -} - export const getRealId = (item: unknown) => { if (typeof item === 'number') return item if (item instanceof BasedModify) return item.id @@ -104,18 +36,12 @@ export const getTmpId = (item: unknown) => { if (item instanceof BasedModify) return item.tmpId } -export const serializeUpdate = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, +export const assignTarget = < + H extends Record & { id?: number; isTmp?: boolean }, >( - schema: S, - type: T, - item: number | BasedModify, - payload: InferPayload[T], - buf: AutoSizedUint8Array, - lang: LangCodeEnum, -) => { - const typeDef = getTypeDef(schema, type) + item: unknown, + header: H, +): H & { id: number; isTmp: boolean } => { const realId = getRealId(item) const id = realId || getTmpId(item) if (id === undefined) { @@ -124,48 +50,16 @@ export const serializeUpdate = < } throw new Error('Invalid id') } - const index = pushModifyUpdateHeader(buf, { - op: Modify.update, - type: typeDef.id, - isTmp: !realId, - id, - size: 0, - }) - const start = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.update, lang) - writeModifyUpdateHeaderProps.size(buf.data, buf.length - start, index) -} - -export const serializeDelete = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - item: number | BasedModify, - buf: AutoSizedUint8Array, -) => { - const typeDef = getTypeDef(schema, type) - const realId = getRealId(item) - const id = realId || getTmpId(item) - if (id === undefined) { - if (item instanceof BasedModify) { - throw item - } - throw new Error('Invalid id') - } - pushModifyDeleteHeader(buf, { - op: Modify.delete, - type: typeDef.id, - isTmp: !realId, - id, - }) + header.id = id + header.isTmp = !realId + return header as H & { id: number; isTmp: boolean } } type ModifySerializer = | typeof serializeCreate | typeof serializeUpdate | typeof serializeDelete + | typeof serializeUpsert type ModifyBatch = { count: number diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts new file mode 100644 index 0000000000..76de537dd0 --- /dev/null +++ b/src/db-client/modify/props.ts @@ -0,0 +1,54 @@ +import type { PropDef, PropTree } from '../../schema/defs/index.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { + ModifyIncrement, + pushModifyMainHeader, + pushModifyPropHeader, + writeModifyPropHeaderProps, + type LangCodeEnum, + type ModifyEnum, +} from '../../zigTsExports.js' + +export const serializeProps = ( + tree: PropTree, + data: any, + buf: AutoSizedUint8Array, + op: ModifyEnum, + lang: LangCodeEnum, +) => { + for (const key in data) { + const def = tree.get(key) + if (def === undefined) { + continue + } + const val = data[key] + if (def.constructor === Map) { + if (val !== null && typeof val === 'object') { + serializeProps(def, val, buf, op, lang) + } + } else { + const prop = def as PropDef + if (prop.id === 0) { + // main + const increment = typeof val === 'object' && val?.increment + pushModifyMainHeader(buf, { + id: 0, + start: prop.start, + type: prop.type, + increment: increment + ? increment < 0 + ? ModifyIncrement.decrement + : ModifyIncrement.increment + : ModifyIncrement.none, + }) + prop.pushValue(buf, increment ? Math.abs(increment) : val, op, lang) + } else { + // separate + const index = pushModifyPropHeader(buf, prop) + const start = buf.length + prop.pushValue(buf, val, op, lang) + writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) + } + } + } +} diff --git a/src/db-client/modify/update.ts b/src/db-client/modify/update.ts new file mode 100644 index 0000000000..316f62bdb4 --- /dev/null +++ b/src/db-client/modify/update.ts @@ -0,0 +1,34 @@ +import type { SchemaOut } from '../../schema.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { + Modify, + pushModifyUpdateHeader, + writeModifyUpdateHeaderProps, + type LangCodeEnum, +} from '../../zigTsExports.js' +import { assignTarget, BasedModify, getTypeDef } from './index.js' +import { serializeProps } from './props.js' +import type { InferPayload } from './types.js' + +export const serializeUpdate = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + item: number | BasedModify, + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const header = assignTarget(item, { + op: Modify.update, + type: typeDef.id, + size: 0, + }) + const index = pushModifyUpdateHeader(buf, header) + const start = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + writeModifyUpdateHeaderProps.size(buf.data, buf.length - start, index) +} diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts new file mode 100644 index 0000000000..8cba324d40 --- /dev/null +++ b/src/db-client/modify/upsert.ts @@ -0,0 +1,39 @@ +import type { SchemaOut } from '../../schema.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { + Modify, + pushModifyUpsertHeader, + writeModifyUpdateHeaderProps, + type LangCodeEnum, +} from '../../zigTsExports.js' +import { getTypeDef } from './index.js' +import { serializeProps } from './props.js' +import type { InferPayload } from './types.js' + +export const serializeUpsert = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + target: InferPayload[T], + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyUpsertHeader(buf, { + op: Modify.upsert, + type: typeDef.id, + size: 0, + }) + // serialize target + const startTarget = buf.length + serializeProps(typeDef.tree, target, buf, Modify.create, lang) + writeModifyUpdateHeaderProps.size(buf.data, buf.length - startTarget, index) + // serialize payload + const sizePos = buf.reserveUint32() + const startPayload = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + buf.writeUint32(buf.length - startPayload, sizePos) +} diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 5705358315..5d45cc22e0 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -26,8 +26,8 @@ import { BasedModify, getRealId, getTmpId, - serializeProps, } from '../../../db-client/modify/index.js' +import { serializeProps } from '../../../db-client/modify/props.js' type Edges = Record<`${string}`, unknown> | undefined diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 885a10b445..2c8c621974 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -200,18 +200,21 @@ export const Modify = { create: 0, update: 1, delete: 2, + upsert: 3, } as const export const ModifyInverse = { 0: 'create', 1: 'update', 2: 'delete', + 3: 'upsert', } as const /** create, update, - delete + delete, + upsert */ export type ModifyEnum = (typeof Modify)[keyof typeof Modify] @@ -388,6 +391,77 @@ export const pushModifyUpdateHeader = ( return index } +export type ModifyUpsertHeader = { + op: ModifyEnum + type: number + size: number +} + +export const ModifyUpsertHeaderByteSize = 6 + +export const ModifyUpsertHeaderAlignOf = 8 + +export const writeModifyUpsertHeader = ( + buf: Uint8Array, + header: ModifyUpsertHeader, + offset: number, +): number => { + buf[offset] = Number(header.op) + offset += 1 + buf[offset] = Number(header.type) + offset += 1 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyUpsertHeaderProps = { + op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { + buf[offset] = Number(value) + }, + type: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, +} + +export const readModifyUpsertHeader = ( + buf: Uint8Array, + offset: number, +): ModifyUpsertHeader => { + const value: ModifyUpsertHeader = { + op: (buf[offset]) as ModifyEnum, + type: buf[offset + 1], + size: readUint32(buf, offset + 2), + } + return value +} + +export const readModifyUpsertHeaderProps = { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), +} + +export const createModifyUpsertHeader = (header: ModifyUpsertHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyUpsertHeaderByteSize) + writeModifyUpsertHeader(buffer, header, 0) + return buffer +} + +export const pushModifyUpsertHeader = ( + buf: AutoSizedUint8Array, + header: ModifyUpsertHeader, +): number => { + const index = buf.length + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.type)) + buf.pushUint32(Number(header.size)) + return index +} + export type ModifyDeleteHeader = { op: ModifyEnum type: number diff --git a/test/modify/boolean.ts b/test/modify/props/boolean.ts similarity index 100% rename from test/modify/boolean.ts rename to test/modify/props/boolean.ts diff --git a/test/modify/default.ts b/test/modify/props/default.ts similarity index 100% rename from test/modify/default.ts rename to test/modify/props/default.ts diff --git a/test/modify/numbers.ts b/test/modify/props/numbers.ts similarity index 100% rename from test/modify/numbers.ts rename to test/modify/props/numbers.ts diff --git a/test/modify/references.ts b/test/modify/props/references.ts similarity index 100% rename from test/modify/references.ts rename to test/modify/props/references.ts diff --git a/test/modify/string.ts b/test/modify/props/string.ts similarity index 100% rename from test/modify/string.ts rename to test/modify/props/string.ts diff --git a/test/modify/timestamp.ts b/test/modify/props/timestamp.ts similarity index 100% rename from test/modify/timestamp.ts rename to test/modify/props/timestamp.ts diff --git a/test/modify/upsert.ts b/test/modify/upsert.ts new file mode 100644 index 0000000000..43e513f8bd --- /dev/null +++ b/test/modify/upsert.ts @@ -0,0 +1,28 @@ +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('upsert', async (t) => { + const db = await testDb(t, { + types: { + user: { + email: 'alias', + uuid: 'alias', + isNice: 'boolean', + }, + }, + }) + + // this is allowed + const youzi = db.upsert( + 'user', + { uuid: '9dg786' }, // target by alias + { email: 'youri@saulx.com', isNice: true }, + ) + + // this is not + const youzi2 = db.upsert( + 'user', + { isNice: true }, // has to be type alias! + { email: 'youri@saulx.com', isNice: true }, + ) +}) From 723d27ec3683a6a4e158df5de1414a885288d99a Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 17:44:59 +0100 Subject: [PATCH 078/449] wip upsert/alias --- native/modify/modify.zig | 32 ++++++++++++++++++++++++++++++- src/db-client/index.ts | 4 ++-- src/db-client/modify/types.ts | 8 ++++++++ src/db-client/modify/upsert.ts | 8 +++++--- src/schema/defs/props/separate.ts | 10 +--------- test/modify/props/boolean.ts | 6 +++--- test/modify/props/default.ts | 6 +++--- test/modify/props/numbers.ts | 6 +++--- test/modify/props/references.ts | 6 +++--- test/modify/props/string.ts | 6 +++--- test/modify/props/timestamp.ts | 6 +++--- test/modify/upsert.ts | 16 +++++++++------- 12 files changed, 74 insertions(+), 40 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index f932b8d8c0..d8e6172ef0 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -46,7 +46,7 @@ fn modifyInternalThread(env: napi.Env, info: napi.Info) !void { try dbCtx.threads.modify(batch); } -pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u8, items: []u8) !void { +pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8, items: []u8) !void { var j: usize = 0; while (j < data.len) { const propId = data[j]; @@ -75,6 +75,19 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u const prop = utils.readNext(t.ModifyPropHeader, data, &j); const value = data[j .. j + prop.size]; switch (prop.type) { + .alias => { + if (value.len == 0) continue; + const id = Node.getNodeId(node); + const old = try Fields.setAlias(typeEntry, id, prop.id, value); + if (old > 0) { + // TODO sort for everything + // if (ctx.currentSortIndex != null) { + // sort.remove(ctx.thread.decompressor, ctx.currentSortIndex.?, slice, Node.getNode(ctx.typeEntry.?, old).?); + // } + const typeId = Node.getNodeTypeId(node); + selva.markDirty(db, typeId, old); + } + }, .cardinality => { var k: usize = 0; const cardinality = utils.readNext(t.ModifyCardinalityHeader, value, &k); @@ -182,6 +195,14 @@ pub fn modifyProps(db: *DbCtx, typeEntry: ?Node.Type, node: Node.Node, data: []u } } +// fn upsert(db: *DbCtx, typeEntry: ?Node.Type, data: []u8) !Node.Node { +// var j: usize = 0; +// while (j < data.len) { +// const propId = data[j]; +// const propSchema = try Schema.getFieldSchema(typeEntry, propId); +// } +// } + pub fn modify( thread: *Thread.Thread, buf: []u8, @@ -231,6 +252,15 @@ pub fn modify( // std.debug.print("- update id: {any} res: {any}\n", .{ id, res }); i += update.size; }, + + .upsert => { + // const upsert = utils.read(t.ModifyUpsertHeader, buf, i); + // i += utils.sizeOf(t.ModifyUpsertHeader); + // const typeEntry = try Node.getType(db, upsert.type); + + // const prop = utils.readNext(t.ModifyPropHeader, buf, &i); + // const value = buf[i .. i + prop.size]; + }, .delete => { const delete = utils.read(t.ModifyDeleteHeader, buf, i); i += utils.sizeOf(t.ModifyDeleteHeader); diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 7887b8deef..9c78e1d144 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -15,7 +15,7 @@ import { import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' import { LangCode } from '../zigTsExports.js' import { ModifyCtx, flush, BasedModify } from './modify/index.js' -import type { InferPayload } from './modify/types.js' +import type { InferPayload, InferTarget } from './modify/types.js' import { serializeCreate } from './modify/create.js' import { serializeUpdate } from './modify/update.js' import { serializeDelete } from './modify/delete.js' @@ -129,7 +129,7 @@ export class DbClient = SchemaOut> extends DbShared { upsert( type: T, - target: InferPayload[T], + target: InferTarget[T], obj: InferPayload[T], opts?: ModifyOpts, ): BasedUpsertPromise { diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 06846b8170..7f548ddd8e 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -81,3 +81,11 @@ type InferType = { export type InferPayload> = { [K in keyof Types]: InferType } + +type InferAliasProps = { + [K in keyof Props as Props[K] extends { type: 'alias' } ? K : never]?: string +} + +export type InferTarget> = { + [K in keyof Types]: InferAliasProps +} diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index 8cba324d40..1f1b5d23ae 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -1,14 +1,15 @@ -import type { SchemaOut } from '../../schema.js' +import { type SchemaOut } from '../../schema.js' import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { Modify, pushModifyUpsertHeader, writeModifyUpdateHeaderProps, + PropType, type LangCodeEnum, } from '../../zigTsExports.js' import { getTypeDef } from './index.js' import { serializeProps } from './props.js' -import type { InferPayload } from './types.js' +import type { InferPayload, InferTarget } from './types.js' export const serializeUpsert = < S extends SchemaOut = SchemaOut, @@ -16,7 +17,7 @@ export const serializeUpsert = < >( schema: S, type: T, - target: InferPayload[T], + target: InferTarget[T], payload: InferPayload[T], buf: AutoSizedUint8Array, lang: LangCodeEnum, @@ -29,6 +30,7 @@ export const serializeUpsert = < }) // serialize target const startTarget = buf.length + // TODO validate that its only aliases serializeProps(typeDef.tree, target, buf, Modify.create, lang) writeModifyUpdateHeaderProps.size(buf.data, buf.length - startTarget, index) // serialize payload diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 3c06a1427d..d6536abd0f 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -167,16 +167,8 @@ export const binary = class Binary extends BasePropDef { } } -export const alias = class Alias extends BasePropDef { +export const alias = class Alias extends string { override type = PropType.alias - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is any { - throw new Error('Serialize alias not implemented') - } override pushSelvaSchema(buf: AutoSizedUint8Array) { buf.pushUint8(PropTypeSelva.alias) } diff --git a/test/modify/props/boolean.ts b/test/modify/props/boolean.ts index 7bf8675bda..6ae031c9bb 100644 --- a/test/modify/props/boolean.ts +++ b/test/modify/props/boolean.ts @@ -1,6 +1,6 @@ -import { deepEqual } from '../shared/assert.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' await test('modify boolean', async (t) => { const db = await testDb(t, { diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index f9a7567f7a..1a70fc8741 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -1,6 +1,6 @@ -import { deepEqual } from '../shared/assert.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' await test('modify - default values basic', async (t) => { const db = await testDb(t, { diff --git a/test/modify/props/numbers.ts b/test/modify/props/numbers.ts index 4cc8589c42..2bd76c19ad 100644 --- a/test/modify/props/numbers.ts +++ b/test/modify/props/numbers.ts @@ -1,6 +1,6 @@ -import { deepEqual } from '../shared/assert.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' await test('modify numbers', async (t) => { const db = await testDb(t, { diff --git a/test/modify/props/references.ts b/test/modify/props/references.ts index 6bd68fc5b4..2086d0344d 100644 --- a/test/modify/props/references.ts +++ b/test/modify/props/references.ts @@ -1,6 +1,6 @@ -import { deepEqual } from '../shared/assert.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' await test('modify single reference', async (t) => { const db = await testDb(t, { diff --git a/test/modify/props/string.ts b/test/modify/props/string.ts index a5f9a18cc2..dbba0e43b0 100644 --- a/test/modify/props/string.ts +++ b/test/modify/props/string.ts @@ -1,6 +1,6 @@ -import { deepEqual } from '../shared/assert.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' await test('modify string', async (t) => { const db = await testDb(t, { diff --git a/test/modify/props/timestamp.ts b/test/modify/props/timestamp.ts index fdff97ea70..88a0db75f9 100644 --- a/test/modify/props/timestamp.ts +++ b/test/modify/props/timestamp.ts @@ -1,6 +1,6 @@ -import { deepEqual } from '../shared/assert.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' await test('modify timestamp', async (t) => { const db = await testDb(t, { diff --git a/test/modify/upsert.ts b/test/modify/upsert.ts index 43e513f8bd..1541febb9a 100644 --- a/test/modify/upsert.ts +++ b/test/modify/upsert.ts @@ -1,7 +1,7 @@ import { testDb } from '../shared/index.js' import test from '../shared/test.js' -await test('upsert', async (t) => { +await test.skip('upsert', async (t) => { const db = await testDb(t, { types: { user: { @@ -19,10 +19,12 @@ await test('upsert', async (t) => { { email: 'youri@saulx.com', isNice: true }, ) - // this is not - const youzi2 = db.upsert( - 'user', - { isNice: true }, // has to be type alias! - { email: 'youri@saulx.com', isNice: true }, - ) + // // this is not + // const youzi2 = db.upsert( + // 'user', + // { isNice: true }, // has to be type alias! + // { email: 'youri@saulx.com', isNice: true }, + // ) + + console.dir(await db.query('user').get()) }) From 1087357e0241c28253dabd906b051449c192562b Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 19:38:24 +0100 Subject: [PATCH 079/449] added upsert --- native/modify/modify.zig | 51 ++++++++++++++++++++----------- src/db-client/modify/upsert.ts | 6 ++-- src/schema/defs/props/separate.ts | 11 ++++++- test/modify/upsert.ts | 6 ++-- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index d8e6172ef0..1a32ca03d2 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -57,7 +57,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); const size = main.type.size(); const value = data[j .. j + size]; - // std.debug.print("main: size {any} value {any} current {any}\n", .{ size, value, current }); if (main.increment != .none) { switch (main.type) { .number => applyInc(f64, current, value, main.start, main.increment), @@ -195,13 +194,23 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } } -// fn upsert(db: *DbCtx, typeEntry: ?Node.Type, data: []u8) !Node.Node { -// var j: usize = 0; -// while (j < data.len) { -// const propId = data[j]; -// const propSchema = try Schema.getFieldSchema(typeEntry, propId); -// } -// } +fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: []u8) !Node.Node { + var j: usize = 0; + while (j < data.len) { + const propId = data[j]; + const prop = utils.readNext(t.ModifyPropHeader, data, &j); + const value = data[j .. j + prop.size]; + if (prop.type == t.PropType.alias) { + if (Fields.getAliasByName(typeEntry, propId, value)) |node| { + return node; + } + } + } + const id = db.ids[typeId - 1] + 1; + const node = try Node.upsertNode(typeEntry, id); + try modifyProps(db, typeEntry, node, data, items); + return node; +} pub fn modify( thread: *Thread.Thread, @@ -249,17 +258,25 @@ pub fn modify( utils.write(result, id, j); utils.write(result, t.ModifyError.nx, j + 4); } - // std.debug.print("- update id: {any} res: {any}\n", .{ id, res }); i += update.size; }, - .upsert => { - // const upsert = utils.read(t.ModifyUpsertHeader, buf, i); - // i += utils.sizeOf(t.ModifyUpsertHeader); - // const typeEntry = try Node.getType(db, upsert.type); - - // const prop = utils.readNext(t.ModifyPropHeader, buf, &i); - // const value = buf[i .. i + prop.size]; + const upsert = utils.read(t.ModifyUpsertHeader, buf, i); + i += utils.sizeOf(t.ModifyUpsertHeader); + const target = buf[i .. i + upsert.size]; + i += upsert.size; + const typeEntry = try Node.getType(db, upsert.type); + const node = try upsertTarget(db, upsert.type, typeEntry, target, items); + const dataSize = utils.read(u32, buf, i); + i += 4; + const data = buf[i .. i + dataSize]; + modifyProps(db, typeEntry, node, data, items) catch { + // handle errors + }; + const id = Node.getNodeId(node); + utils.write(result, id, j); + utils.write(result, t.ModifyError.null, j + 4); + i += dataSize; }, .delete => { const delete = utils.read(t.ModifyDeleteHeader, buf, i); @@ -277,8 +294,6 @@ pub fn modify( utils.write(result, id, j); utils.write(result, t.ModifyError.nx, j + 4); } - - // std.debug.print("- delete id: {any} res: {any}\n", .{ id, res }); }, } j += resItemSize; diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index 1f1b5d23ae..dcfb694db4 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -3,9 +3,8 @@ import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { Modify, pushModifyUpsertHeader, - writeModifyUpdateHeaderProps, - PropType, type LangCodeEnum, + writeModifyUpsertHeaderProps, } from '../../zigTsExports.js' import { getTypeDef } from './index.js' import { serializeProps } from './props.js' @@ -32,10 +31,11 @@ export const serializeUpsert = < const startTarget = buf.length // TODO validate that its only aliases serializeProps(typeDef.tree, target, buf, Modify.create, lang) - writeModifyUpdateHeaderProps.size(buf.data, buf.length - startTarget, index) + writeModifyUpsertHeaderProps.size(buf.data, buf.length - startTarget, index) // serialize payload const sizePos = buf.reserveUint32() const startPayload = buf.length serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + console.log('start:', buf.length - startPayload) buf.writeUint32(buf.length - startPayload, sizePos) } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index d6536abd0f..ac0b52840d 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -167,8 +167,17 @@ export const binary = class Binary extends BasePropDef { } } -export const alias = class Alias extends string { +export const alias = class Alias extends BasePropDef { override type = PropType.alias + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is string { + if (typeof value !== 'string') { + throw new Error('Invalid type for alias ' + this.path.join('.')) + } + buf.pushString(value) + } override pushSelvaSchema(buf: AutoSizedUint8Array) { buf.pushUint8(PropTypeSelva.alias) } diff --git a/test/modify/upsert.ts b/test/modify/upsert.ts index 1541febb9a..d090a5a61e 100644 --- a/test/modify/upsert.ts +++ b/test/modify/upsert.ts @@ -1,7 +1,7 @@ import { testDb } from '../shared/index.js' import test from '../shared/test.js' -await test.skip('upsert', async (t) => { +await test('upsert', async (t) => { const db = await testDb(t, { types: { user: { @@ -19,6 +19,8 @@ await test.skip('upsert', async (t) => { { email: 'youri@saulx.com', isNice: true }, ) + console.log('-----') + // // this is not // const youzi2 = db.upsert( // 'user', @@ -26,5 +28,5 @@ await test.skip('upsert', async (t) => { // { email: 'youri@saulx.com', isNice: true }, // ) - console.dir(await db.query('user').get()) + console.dir(await db.query('user').get().toObject()) }) From ad2c50360f9b6e0ebd5483b703eab0b76b84b602 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 19:47:25 +0100 Subject: [PATCH 080/449] update upsert test --- native/modify/modify.zig | 5 +-- src/db-client/modify/upsert.ts | 1 - test/modify/upsert.ts | 63 ++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 1a32ca03d2..d98e9af20a 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -197,17 +197,18 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: []u8) !Node.Node { var j: usize = 0; while (j < data.len) { - const propId = data[j]; const prop = utils.readNext(t.ModifyPropHeader, data, &j); const value = data[j .. j + prop.size]; if (prop.type == t.PropType.alias) { - if (Fields.getAliasByName(typeEntry, propId, value)) |node| { + if (Fields.getAliasByName(typeEntry, prop.id, value)) |node| { return node; } } + j += prop.size; } const id = db.ids[typeId - 1] + 1; const node = try Node.upsertNode(typeEntry, id); + db.ids[typeId - 1] = id; try modifyProps(db, typeEntry, node, data, items); return node; } diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index dcfb694db4..a3dee1e346 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -36,6 +36,5 @@ export const serializeUpsert = < const sizePos = buf.reserveUint32() const startPayload = buf.length serializeProps(typeDef.tree, payload, buf, Modify.update, lang) - console.log('start:', buf.length - startPayload) buf.writeUint32(buf.length - startPayload, sizePos) } diff --git a/test/modify/upsert.ts b/test/modify/upsert.ts index d090a5a61e..1a38c6bc37 100644 --- a/test/modify/upsert.ts +++ b/test/modify/upsert.ts @@ -1,3 +1,4 @@ +import { deepEqual } from '../shared/assert.js' import { testDb } from '../shared/index.js' import test from '../shared/test.js' @@ -12,21 +13,63 @@ await test('upsert', async (t) => { }, }) - // this is allowed - const youzi = db.upsert( + // 1. Create via upsert + const id1 = await db.upsert( 'user', { uuid: '9dg786' }, // target by alias { email: 'youri@saulx.com', isNice: true }, ) - console.log('-----') + const res1 = await db.query('user', id1).get() + deepEqual(res1, { + id: id1, + uuid: '9dg786', + email: 'youri@saulx.com', + isNice: true, + }) + + // 2. Update via upsert (same alias target) + const id2 = await db.upsert('user', { uuid: '9dg786' }, { isNice: false }) + + deepEqual(id1, id2, 'Ids should be the same') + + const res2 = await db.query('user', id1).get() + deepEqual(res2, { + id: id1, + uuid: '9dg786', + email: 'youri@saulx.com', // Should be preserved + isNice: false, // Should be updated + }) + + // 3. Create another one via different alias field + const id3 = await db.upsert( + 'user', + { email: 'bla@bla.com' }, + { uuid: 'unique-id-2', isNice: true }, + ) - // // this is not - // const youzi2 = db.upsert( - // 'user', - // { isNice: true }, // has to be type alias! - // { email: 'youri@saulx.com', isNice: true }, - // ) + const res3 = await db.query('user', id3).get() + deepEqual(res3, { + id: id3, + uuid: 'unique-id-2', + email: 'bla@bla.com', + isNice: true, + }) - console.dir(await db.query('user').get().toObject()) + // 4. Update via different alias field + const id4 = await db.upsert( + 'user', + { email: 'bla@bla.com' }, + { isNice: false }, + ) + + deepEqual(id3, id4, 'Ids should be the same 2') + + const res4 = await db.query('user', id3).get() + deepEqual(res4, { + id: id3, + uuid: 'unique-id-2', + email: 'bla@bla.com', + isNice: false, + }) }) From 257a3768f47533960db12bf403282ff8ae527c58 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 19:57:53 +0100 Subject: [PATCH 081/449] implement .insert --- native/modify/modify.zig | 41 ++++++++++++++---- native/types.zig | 7 +--- src/db-client/index.ts | 20 +++++++++ src/db-client/modify/index.ts | 2 + src/db-client/modify/insert.ts | 40 ++++++++++++++++++ src/db-client/modify/upsert.ts | 8 ++-- src/zigTsExports.ts | 76 ++-------------------------------- test/modify/insert.ts | 75 +++++++++++++++++++++++++++++++++ 8 files changed, 179 insertions(+), 90 deletions(-) create mode 100644 src/db-client/modify/insert.ts create mode 100644 test/modify/insert.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index d98e9af20a..26eb599f16 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -194,14 +194,19 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } } -fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: []u8) !Node.Node { +const UpsertResult = struct { + node: Node.Node, + created: bool, +}; + +fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: []u8) !UpsertResult { var j: usize = 0; while (j < data.len) { const prop = utils.readNext(t.ModifyPropHeader, data, &j); const value = data[j .. j + prop.size]; if (prop.type == t.PropType.alias) { if (Fields.getAliasByName(typeEntry, prop.id, value)) |node| { - return node; + return .{ .node = node, .created = false }; } } j += prop.size; @@ -210,7 +215,7 @@ fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: const node = try Node.upsertNode(typeEntry, id); db.ids[typeId - 1] = id; try modifyProps(db, typeEntry, node, data, items); - return node; + return .{ .node = node, .created = true }; } pub fn modify( @@ -262,19 +267,39 @@ pub fn modify( i += update.size; }, .upsert => { - const upsert = utils.read(t.ModifyUpsertHeader, buf, i); - i += utils.sizeOf(t.ModifyUpsertHeader); + const upsert = utils.read(t.ModifyCreateHeader, buf, i); + i += utils.sizeOf(t.ModifyCreateHeader); const target = buf[i .. i + upsert.size]; i += upsert.size; const typeEntry = try Node.getType(db, upsert.type); - const node = try upsertTarget(db, upsert.type, typeEntry, target, items); + const upsertRes = try upsertTarget(db, upsert.type, typeEntry, target, items); const dataSize = utils.read(u32, buf, i); i += 4; const data = buf[i .. i + dataSize]; - modifyProps(db, typeEntry, node, data, items) catch { + modifyProps(db, typeEntry, upsertRes.node, data, items) catch { // handle errors }; - const id = Node.getNodeId(node); + const id = Node.getNodeId(upsertRes.node); + utils.write(result, id, j); + utils.write(result, t.ModifyError.null, j + 4); + i += dataSize; + }, + .insert => { + const insert = utils.read(t.ModifyCreateHeader, buf, i); + i += utils.sizeOf(t.ModifyCreateHeader); + const target = buf[i .. i + insert.size]; + i += insert.size; + const typeEntry = try Node.getType(db, insert.type); + const upsertRes = try upsertTarget(db, insert.type, typeEntry, target, items); + const dataSize = utils.read(u32, buf, i); + i += 4; + if (upsertRes.created) { + const data = buf[i .. i + dataSize]; + modifyProps(db, typeEntry, upsertRes.node, data, items) catch { + // handle errors + }; + } + const id = Node.getNodeId(upsertRes.node); utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); i += dataSize; diff --git a/native/types.zig b/native/types.zig index 71683dd14c..b1c80a2b66 100644 --- a/native/types.zig +++ b/native/types.zig @@ -82,6 +82,7 @@ pub const Modify = enum(u8) { update = 1, delete = 2, upsert = 3, + insert = 4, }; pub const ModifyHeader = packed struct { @@ -100,12 +101,6 @@ pub const ModifyUpdateHeader = packed struct { size: u32, }; -pub const ModifyUpsertHeader = packed struct { - op: Modify, - type: u8, - size: u32, -}; - pub const ModifyDeleteHeader = packed struct { op: Modify, type: u8, diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 9c78e1d144..f641d812d1 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -20,6 +20,7 @@ import { serializeCreate } from './modify/create.js' import { serializeUpdate } from './modify/update.js' import { serializeDelete } from './modify/delete.js' import { serializeUpsert } from './modify/upsert.js' +import { serializeInsert } from './modify/insert.js' type DbClientOpts = { hooks: DbClientHooks @@ -32,6 +33,7 @@ type BasedCreatePromise = BasedModify type BasedUpdatePromise = BasedModify type BasedDeletePromise = BasedModify type BasedUpsertPromise = BasedModify +type BasedInsertPromise = BasedModify export type ModifyOpts = { unsafe?: boolean @@ -145,6 +147,24 @@ export class DbClient = SchemaOut> extends DbShared { ) } + insert( + type: T, + target: InferTarget[T], + obj: InferPayload[T], + opts?: ModifyOpts, + ): BasedInsertPromise { + return new BasedModify( + this.modifyCtx, + serializeInsert, + this.schema!, + type, + target, + obj, + this.modifyCtx.buf, + opts?.locale ? LangCode[opts.locale] : LangCode.none, + ) + } + delete( type: keyof S['types'] & string, target: number | BasedModify, diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 13cc73be11..d824f46c16 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -17,6 +17,7 @@ import type { serializeCreate } from './create.js' import type { serializeUpdate } from './update.js' import type { serializeDelete } from './delete.js' import type { serializeUpsert } from './upsert.js' +import type { serializeInsert } from './insert.js' export { getTypeDefs } export const getTypeDef = (schema: SchemaOut, type: string) => { @@ -60,6 +61,7 @@ type ModifySerializer = | typeof serializeUpdate | typeof serializeDelete | typeof serializeUpsert + | typeof serializeInsert type ModifyBatch = { count: number diff --git a/src/db-client/modify/insert.ts b/src/db-client/modify/insert.ts new file mode 100644 index 0000000000..c209d99798 --- /dev/null +++ b/src/db-client/modify/insert.ts @@ -0,0 +1,40 @@ +import { type SchemaOut } from '../../schema.js' +import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import { + Modify, + pushModifyCreateHeader, + type LangCodeEnum, + writeModifyCreateHeaderProps, +} from '../../zigTsExports.js' +import { getTypeDef } from './index.js' +import { serializeProps } from './props.js' +import type { InferPayload, InferTarget } from './types.js' + +export const serializeInsert = < + S extends SchemaOut = SchemaOut, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + schema: S, + type: T, + target: InferTarget[T], + payload: InferPayload[T], + buf: AutoSizedUint8Array, + lang: LangCodeEnum, +) => { + const typeDef = getTypeDef(schema, type) + const index = pushModifyCreateHeader(buf, { + op: Modify.insert, + type: typeDef.id, + size: 0, + }) + // serialize target + const startTarget = buf.length + // TODO validate that its only aliases + serializeProps(typeDef.tree, target, buf, Modify.create, lang) + writeModifyCreateHeaderProps.size(buf.data, buf.length - startTarget, index) + // serialize payload + const sizePos = buf.reserveUint32() + const startPayload = buf.length + serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + buf.writeUint32(buf.length - startPayload, sizePos) +} diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index a3dee1e346..bc81ddf033 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -2,9 +2,9 @@ import { type SchemaOut } from '../../schema.js' import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { Modify, - pushModifyUpsertHeader, + pushModifyCreateHeader, type LangCodeEnum, - writeModifyUpsertHeaderProps, + writeModifyCreateHeaderProps, } from '../../zigTsExports.js' import { getTypeDef } from './index.js' import { serializeProps } from './props.js' @@ -22,7 +22,7 @@ export const serializeUpsert = < lang: LangCodeEnum, ) => { const typeDef = getTypeDef(schema, type) - const index = pushModifyUpsertHeader(buf, { + const index = pushModifyCreateHeader(buf, { op: Modify.upsert, type: typeDef.id, size: 0, @@ -31,7 +31,7 @@ export const serializeUpsert = < const startTarget = buf.length // TODO validate that its only aliases serializeProps(typeDef.tree, target, buf, Modify.create, lang) - writeModifyUpsertHeaderProps.size(buf.data, buf.length - startTarget, index) + writeModifyCreateHeaderProps.size(buf.data, buf.length - startTarget, index) // serialize payload const sizePos = buf.reserveUint32() const startPayload = buf.length diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 2c8c621974..29d0cd2619 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -201,6 +201,7 @@ export const Modify = { update: 1, delete: 2, upsert: 3, + insert: 4, } as const export const ModifyInverse = { @@ -208,13 +209,15 @@ export const ModifyInverse = { 1: 'update', 2: 'delete', 3: 'upsert', + 4: 'insert', } as const /** create, update, delete, - upsert + upsert, + insert */ export type ModifyEnum = (typeof Modify)[keyof typeof Modify] @@ -391,77 +394,6 @@ export const pushModifyUpdateHeader = ( return index } -export type ModifyUpsertHeader = { - op: ModifyEnum - type: number - size: number -} - -export const ModifyUpsertHeaderByteSize = 6 - -export const ModifyUpsertHeaderAlignOf = 8 - -export const writeModifyUpsertHeader = ( - buf: Uint8Array, - header: ModifyUpsertHeader, - offset: number, -): number => { - buf[offset] = Number(header.op) - offset += 1 - buf[offset] = Number(header.type) - offset += 1 - writeUint32(buf, Number(header.size), offset) - offset += 4 - return offset -} - -export const writeModifyUpsertHeaderProps = { - op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { - buf[offset] = Number(value) - }, - type: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 1] = Number(value) - }, - size: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 2) - }, -} - -export const readModifyUpsertHeader = ( - buf: Uint8Array, - offset: number, -): ModifyUpsertHeader => { - const value: ModifyUpsertHeader = { - op: (buf[offset]) as ModifyEnum, - type: buf[offset + 1], - size: readUint32(buf, offset + 2), - } - return value -} - -export const readModifyUpsertHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), -} - -export const createModifyUpsertHeader = (header: ModifyUpsertHeader): Uint8Array => { - const buffer = new Uint8Array(ModifyUpsertHeaderByteSize) - writeModifyUpsertHeader(buffer, header, 0) - return buffer -} - -export const pushModifyUpsertHeader = ( - buf: AutoSizedUint8Array, - header: ModifyUpsertHeader, -): number => { - const index = buf.length - buf.pushUint8(Number(header.op)) - buf.pushUint8(Number(header.type)) - buf.pushUint32(Number(header.size)) - return index -} - export type ModifyDeleteHeader = { op: ModifyEnum type: number diff --git a/test/modify/insert.ts b/test/modify/insert.ts new file mode 100644 index 0000000000..300891b422 --- /dev/null +++ b/test/modify/insert.ts @@ -0,0 +1,75 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('insert', async (t) => { + const db = await testDb(t, { + types: { + user: { + email: 'alias', + uuid: 'alias', + isNice: 'boolean', + }, + }, + }) + + // 1. Create via insert + const id1 = await db.insert( + 'user', + { uuid: '9dg786' }, // target by alias + { email: 'youri@saulx.com', isNice: true }, + ) + + const res1 = await db.query('user', id1).get() + deepEqual(res1, { + id: id1, + uuid: '9dg786', + email: 'youri@saulx.com', + isNice: true, + }) + + // 2. Insert with same alias (should NOT update) + const id2 = await db.insert('user', { uuid: '9dg786' }, { isNice: false }) + + deepEqual(id1, id2, 'Ids should be the same') + + const res2 = await db.query('user', id1).get() + deepEqual(res2, { + id: id1, + uuid: '9dg786', + email: 'youri@saulx.com', + isNice: true, // Should still be true + }) + + // 3. Create another one via different alias field + const id3 = await db.insert( + 'user', + { email: 'bla@bla.com' }, + { uuid: 'unique-id-2', isNice: true }, + ) + + const res3 = await db.query('user', id3).get() + deepEqual(res3, { + id: id3, + uuid: 'unique-id-2', + email: 'bla@bla.com', + isNice: true, + }) + + // 4. Insert via different alias field (should NOT update) + const id4 = await db.insert( + 'user', + { email: 'bla@bla.com' }, + { isNice: false }, + ) + + deepEqual(id3, id4, 'Ids should be the same 2') + + const res4 = await db.query('user', id3).get() + deepEqual(res4, { + id: id3, + uuid: 'unique-id-2', + email: 'bla@bla.com', + isNice: true, // Should still be true + }) +}) From a47934cea53a68f186719c24e4a86fdfde25208b Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 20:13:48 +0100 Subject: [PATCH 082/449] added upsert and insert --- native/modify/modify.zig | 11 ++++++---- src/db-client/index.ts | 9 ++++---- src/db-client/modify/index.ts | 2 -- src/db-client/modify/insert.ts | 40 ---------------------------------- src/db-client/modify/upsert.ts | 3 ++- 5 files changed, 14 insertions(+), 51 deletions(-) delete mode 100644 src/db-client/modify/insert.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 26eb599f16..eb0864579f 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -199,7 +199,7 @@ const UpsertResult = struct { created: bool, }; -fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: []u8) !UpsertResult { +inline fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8) !UpsertResult { var j: usize = 0; while (j < data.len) { const prop = utils.readNext(t.ModifyPropHeader, data, &j); @@ -214,7 +214,6 @@ fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8, items: const id = db.ids[typeId - 1] + 1; const node = try Node.upsertNode(typeEntry, id); db.ids[typeId - 1] = id; - try modifyProps(db, typeEntry, node, data, items); return .{ .node = node, .created = true }; } @@ -272,7 +271,10 @@ pub fn modify( const target = buf[i .. i + upsert.size]; i += upsert.size; const typeEntry = try Node.getType(db, upsert.type); - const upsertRes = try upsertTarget(db, upsert.type, typeEntry, target, items); + const upsertRes = try upsertTarget(db, upsert.type, typeEntry, target); + if (upsertRes.created) { + try modifyProps(db, typeEntry, upsertRes.node, target, items); + } const dataSize = utils.read(u32, buf, i); i += 4; const data = buf[i .. i + dataSize]; @@ -290,10 +292,11 @@ pub fn modify( const target = buf[i .. i + insert.size]; i += insert.size; const typeEntry = try Node.getType(db, insert.type); - const upsertRes = try upsertTarget(db, insert.type, typeEntry, target, items); + const upsertRes = try upsertTarget(db, insert.type, typeEntry, target); const dataSize = utils.read(u32, buf, i); i += 4; if (upsertRes.created) { + try modifyProps(db, typeEntry, upsertRes.node, target, items); const data = buf[i .. i + dataSize]; modifyProps(db, typeEntry, upsertRes.node, data, items) catch { // handle errors diff --git a/src/db-client/index.ts b/src/db-client/index.ts index f641d812d1..d1e98ba1ba 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -13,14 +13,13 @@ import { type Schema, } from '../schema/index.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' -import { LangCode } from '../zigTsExports.js' +import { LangCode, Modify } from '../zigTsExports.js' import { ModifyCtx, flush, BasedModify } from './modify/index.js' import type { InferPayload, InferTarget } from './modify/types.js' import { serializeCreate } from './modify/create.js' import { serializeUpdate } from './modify/update.js' import { serializeDelete } from './modify/delete.js' import { serializeUpsert } from './modify/upsert.js' -import { serializeInsert } from './modify/insert.js' type DbClientOpts = { hooks: DbClientHooks @@ -33,7 +32,7 @@ type BasedCreatePromise = BasedModify type BasedUpdatePromise = BasedModify type BasedDeletePromise = BasedModify type BasedUpsertPromise = BasedModify -type BasedInsertPromise = BasedModify +type BasedInsertPromise = BasedUpsertPromise export type ModifyOpts = { unsafe?: boolean @@ -144,6 +143,7 @@ export class DbClient = SchemaOut> extends DbShared { obj, this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, + Modify.upsert, ) } @@ -155,13 +155,14 @@ export class DbClient = SchemaOut> extends DbShared { ): BasedInsertPromise { return new BasedModify( this.modifyCtx, - serializeInsert, + serializeUpsert, this.schema!, type, target, obj, this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, + Modify.insert, ) } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index d824f46c16..13cc73be11 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -17,7 +17,6 @@ import type { serializeCreate } from './create.js' import type { serializeUpdate } from './update.js' import type { serializeDelete } from './delete.js' import type { serializeUpsert } from './upsert.js' -import type { serializeInsert } from './insert.js' export { getTypeDefs } export const getTypeDef = (schema: SchemaOut, type: string) => { @@ -61,7 +60,6 @@ type ModifySerializer = | typeof serializeUpdate | typeof serializeDelete | typeof serializeUpsert - | typeof serializeInsert type ModifyBatch = { count: number diff --git a/src/db-client/modify/insert.ts b/src/db-client/modify/insert.ts deleted file mode 100644 index c209d99798..0000000000 --- a/src/db-client/modify/insert.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { type SchemaOut } from '../../schema.js' -import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import { - Modify, - pushModifyCreateHeader, - type LangCodeEnum, - writeModifyCreateHeaderProps, -} from '../../zigTsExports.js' -import { getTypeDef } from './index.js' -import { serializeProps } from './props.js' -import type { InferPayload, InferTarget } from './types.js' - -export const serializeInsert = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - target: InferTarget[T], - payload: InferPayload[T], - buf: AutoSizedUint8Array, - lang: LangCodeEnum, -) => { - const typeDef = getTypeDef(schema, type) - const index = pushModifyCreateHeader(buf, { - op: Modify.insert, - type: typeDef.id, - size: 0, - }) - // serialize target - const startTarget = buf.length - // TODO validate that its only aliases - serializeProps(typeDef.tree, target, buf, Modify.create, lang) - writeModifyCreateHeaderProps.size(buf.data, buf.length - startTarget, index) - // serialize payload - const sizePos = buf.reserveUint32() - const startPayload = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.update, lang) - buf.writeUint32(buf.length - startPayload, sizePos) -} diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index bc81ddf033..41cb7a261e 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -20,10 +20,11 @@ export const serializeUpsert = < payload: InferPayload[T], buf: AutoSizedUint8Array, lang: LangCodeEnum, + op: typeof Modify.insert | typeof Modify.upsert, ) => { const typeDef = getTypeDef(schema, type) const index = pushModifyCreateHeader(buf, { - op: Modify.upsert, + op, type: typeDef.id, size: 0, }) From 74b567afd6b0271f2f2f28b5c075dce14cd9211a Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 20:24:16 +0100 Subject: [PATCH 083/449] update --- src/db-client/modify/index.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 13cc73be11..58b491d1e4 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -1,18 +1,8 @@ import { SchemaOut } from '../../schema.js' -import { - Modify, - pushModifyDeleteHeader, - pushModifyHeader, - pushModifyUpdateHeader, - writeModifyHeaderProps, - writeModifyUpdateHeaderProps, - type LangCodeEnum, -} from '../../zigTsExports.js' +import { pushModifyHeader, writeModifyHeaderProps } from '../../zigTsExports.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' -import { InferPayload } from './types.js' import { getTypeDefs } from '../../schema/defs/getTypeDefs.js' import { readUint32 } from '../../utils/uint8.js' -import { serializeProps } from './props.js' import type { serializeCreate } from './create.js' import type { serializeUpdate } from './update.js' import type { serializeDelete } from './delete.js' From 2f16109f64d90d22a95223854a8f62c699d1b95c Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 2 Feb 2026 20:38:47 +0100 Subject: [PATCH 084/449] init tests for every prop --- native/modify/modify.zig | 1 + test/modify/props/alias.ts | 33 +++++++++++++ test/modify/props/binary.ts | 30 ++++++++++++ test/modify/props/cardinality.ts | 43 +++++++++++++++++ test/modify/props/enum.ts | 31 ++++++++++++ test/modify/props/json.ts | 33 +++++++++++++ test/modify/props/object.ts | 49 +++++++++++++++++++ test/modify/props/text.ts | 65 +++++++++++++++++++++++++ test/modify/props/vector.ts | 82 ++++++++++++++++++++++++++++++++ 9 files changed, 367 insertions(+) create mode 100644 test/modify/props/alias.ts create mode 100644 test/modify/props/binary.ts create mode 100644 test/modify/props/cardinality.ts create mode 100644 test/modify/props/enum.ts create mode 100644 test/modify/props/json.ts create mode 100644 test/modify/props/object.ts create mode 100644 test/modify/props/text.ts create mode 100644 test/modify/props/vector.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index eb0864579f..91b787c41b 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -78,6 +78,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 if (value.len == 0) continue; const id = Node.getNodeId(node); const old = try Fields.setAlias(typeEntry, id, prop.id, value); + // std.debug.print("value {any} {any} {d} {d} {any}\n", .{ typeEntry, value, id, prop.id, old }); if (old > 0) { // TODO sort for everything // if (ctx.currentSortIndex != null) { diff --git a/test/modify/props/alias.ts b/test/modify/props/alias.ts new file mode 100644 index 0000000000..6359bca863 --- /dev/null +++ b/test/modify/props/alias.ts @@ -0,0 +1,33 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify alias', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myAlias: 'alias', + }, + }, + }) + + // Basic alias + const id1 = await db.create('thing', { + myAlias: 'my-alias-value', + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + myAlias: 'my-alias-value', + }) + + // Update + await db.update('thing', id1, { + myAlias: 'another-alias', + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + myAlias: 'another-alias', + }) +}) diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts new file mode 100644 index 0000000000..a21c2389a1 --- /dev/null +++ b/test/modify/props/binary.ts @@ -0,0 +1,30 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify binary', async (t) => { + const db = await testDb(t, { + types: { + thing: { + blob: 'binary', + }, + }, + }) + + const b1 = new Uint8Array([1, 2, 3]) + const id1 = await db.create('thing', { + blob: b1, + }) + + const res1: any = await db.query('thing', id1).get() + // Depending on how binary is returned (buffer or Uint8Array), likely Uint8Array as per rules + deepEqual(res1.blob, b1) + + const b2 = new Uint8Array([4, 5, 6, 7]) + await db.update('thing', id1, { + blob: b2, + }) + + const res2: any = await db.query('thing', id1).get() + deepEqual(res2.blob, b2) +}) diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts new file mode 100644 index 0000000000..b0fb9b293a --- /dev/null +++ b/test/modify/props/cardinality.ts @@ -0,0 +1,43 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify cardinality', async (t) => { + const db = await testDb(t, { + types: { + thing: { + counter: 'cardinality', + }, + }, + }) + + // Cardinality is a probabilistic counter. + // We usually "add" values to it. + const id1 = await db.create('thing', { + counter: 'item1', + }) + + // Assuming we can read the count? Or the approximation. + // The query might return the count. + const res1: any = await db.query('thing', id1).get() + deepEqual(res1.counter, 1) + + // Add another unique item + await db.update('thing', id1, { + counter: 'item2', + }) + const res2: any = await db.query('thing', id1).get() + deepEqual(res2.counter, 2) + + // Add duplicate item (count shouldn't change) + await db.update('thing', id1, { + counter: 'item1', + }) + const res3: any = await db.query('thing', id1).get() + deepEqual(res3.counter, 2) + + // Add multiple items? + // await db.update('thing', id1, { + // counter: ['item3', 'item4'] + // }) +}) diff --git a/test/modify/props/enum.ts b/test/modify/props/enum.ts new file mode 100644 index 0000000000..7fbcc5c257 --- /dev/null +++ b/test/modify/props/enum.ts @@ -0,0 +1,31 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify enum', async (t) => { + const db = await testDb(t, { + types: { + thing: { + option: { enum: ['first', 'second', 'third'] }, + }, + }, + }) + + const id1 = await db.create('thing', { + option: 'first', + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + option: 'first', + }) + + await db.update('thing', id1, { + option: 'second', + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + option: 'second', + }) +}) diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts new file mode 100644 index 0000000000..4875af0069 --- /dev/null +++ b/test/modify/props/json.ts @@ -0,0 +1,33 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify json', async (t) => { + const db = await testDb(t, { + types: { + thing: { + data: 'json', + }, + }, + }) + + const obj = { foo: 'bar', baz: 123, list: [1, 2] } + const id1 = await db.create('thing', { + data: obj, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + data: obj, + }) + + const arr = ['a', 'b', 'c'] + await db.update('thing', id1, { + data: arr, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + data: arr, + }) +}) diff --git a/test/modify/props/object.ts b/test/modify/props/object.ts new file mode 100644 index 0000000000..0ed6a7d01b --- /dev/null +++ b/test/modify/props/object.ts @@ -0,0 +1,49 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify object', async (t) => { + const db = await testDb(t, { + types: { + thing: { + info: { + type: 'object', + props: { + title: 'string', + count: 'number', + }, + }, + }, + }, + }) + + const id1 = await db.create('thing', { + info: { + title: 'my title', + count: 10, + }, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + info: { + title: 'my title', + count: 10, + }, + }) + + // Partial update of object + await db.update('thing', id1, { + info: { + count: 20, + }, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + info: { + title: 'my title', + count: 20, + }, + }) +}) diff --git a/test/modify/props/text.ts b/test/modify/props/text.ts new file mode 100644 index 0000000000..5752438ded --- /dev/null +++ b/test/modify/props/text.ts @@ -0,0 +1,65 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify text', async (t) => { + const db = await testDb(t, { + locales: { + en: true, + de: true, + nl: true, + }, + types: { + thing: { + content: 'text', + }, + }, + }) + + const id1 = await db.create('thing', { + content: { + en: 'Hello', + de: 'Hallo', + } as any, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + content: { + en: 'Hello', + de: 'Hallo', + }, + }) + + // Update specific locale + await db.update('thing', id1, { + content: { + nl: 'Hallo', + } as any, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + content: { + en: 'Hello', + de: 'Hallo', + nl: 'Hallo', + }, + }) + + // Overwrite + await db.update('thing', id1, { + content: { + en: 'Hi', + } as any, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + content: { + en: 'Hi', + de: 'Hallo', + nl: 'Hallo', + }, + }) +}) diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts new file mode 100644 index 0000000000..3c940b3998 --- /dev/null +++ b/test/modify/props/vector.ts @@ -0,0 +1,82 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' +import assert from 'node:assert' + +await test('modify vector', async (t) => { + const db = await testDb(t, { + types: { + thing: { + vec: { type: 'vector', size: 3, baseType: 'float32' }, + }, + }, + }) + + const v1 = [1.1, 2.2, 3.3] + const id1 = await db.create('thing', { + vec: v1 as any, + }) + + // Float precision might require approximate equality or strict check if implementation preserves bits + // For now assuming deepEqual works or we might need a tolerance check + const res: any = await db.query('thing', id1).get() + + // Convert result back to array if it is returned as TypedArray + const vecArr = Array.from(res.vec as any) as number[] + + // Check approximate values + assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) + assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) + assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) + + const v2 = [4.4, 5.5, 6.6] + await db.update('thing', id1, { + vec: v2 as any, + }) + + const res2: any = await db.query('thing', id1).get() + const vecArr2 = Array.from(res2.vec as any) as number[] + + assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) + assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) + assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) +}) + +await test('modify colvec', async (t) => { + const db = await testDb(t, { + types: { + thing: { + insertOnly: true, + props: { + vec: { type: 'colvec', size: 3, baseType: 'float32' }, + }, + }, + }, + }) + + // colvec behaves similarly to vector in terms of setting/getting from client perspective + // but internally stored differently (columnar). + const v1 = [1.1, 2.2, 3.3] + const id1 = await db.create('thing', { + vec: v1 as any, + }) + + const res: any = await db.query('thing', id1).get() + const vecArr = Array.from(res.vec as any) as number[] + + assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) + assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) + assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) + + const v2 = [4.4, 5.5, 6.6] + await db.update('thing', id1, { + vec: v2 as any, + }) + + const res2: any = await db.query('thing', id1).get() + const vecArr2 = Array.from(res2.vec as any) as number[] + + assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) + assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) + assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) +}) From 81cb3e2cff8b954930a220739c20450602795027 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 08:55:33 +0100 Subject: [PATCH 085/449] Wrong assert --- clibs/lib/selva/alias.c | 11 +++++++++-- clibs/lib/selva/db.c | 7 +------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index 47ffaebe06..2ee3f768a1 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -32,8 +32,15 @@ void selva_init_aliases(struct SelvaTypeEntry *type) void selva_destroy_aliases(struct SelvaTypeEntry *type) { - /* We assume that all the aliases in the aliases structs have been freed already. */ - assert(type->ns.nr_aliases == 0); +#if 0 + /* + * We assume that all the aliases in the aliases structs have been freed already. + */ + for (size_t i = 0; i < type->ns.nr_aliases; i++) { + assert(type->aliases->nr_aliases == 0); + } +#endif + selva_free(type->aliases); type->ns.nr_aliases = 0; type->aliases = nullptr; diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 93d8fd48a1..7995f87b11 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -238,11 +238,6 @@ static void del_all_nodes(struct SelvaDb *db, struct SelvaTypeEntry *te) } } -static inline void clear_type(struct SelvaDb *db, struct SelvaTypeEntry *te) -{ - del_all_nodes(db, te); -} - static void destroy_type(struct SelvaDb *db, struct SelvaTypeEntry *te) { /* @@ -275,7 +270,7 @@ static void del_all_types(struct SelvaDb *db) struct SelvaTypeEntry *tmp; RB_FOREACH_SAFE(te, SelvaTypeEntryIndex, &db->types.index, tmp) { - clear_type(db, te); + del_all_nodes(db, te); } RB_FOREACH_SAFE(te, SelvaTypeEntryIndex, &db->types.index, tmp) { From 5616f7e95a2c8c5334d1fa587fc3dfc5b0bb003f Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 09:25:03 +0100 Subject: [PATCH 086/449] debug alias --- native/modify/modify.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 91b787c41b..868dedde85 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -78,7 +78,16 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 if (value.len == 0) continue; const id = Node.getNodeId(node); const old = try Fields.setAlias(typeEntry, id, prop.id, value); - // std.debug.print("value {any} {any} {d} {d} {any}\n", .{ typeEntry, value, id, prop.id, old }); + // std.debug.print("value {any} {any} {d} {d} {any}\n", .{ node, value, id, prop.id, old }); + // if (Fields.getAliasByName(typeEntry, prop.id, value)) |node2| { + // const res = Fields.get( + // typeEntry, + // node, + // propSchema, + // prop.type, + // ); + // std.debug.print("node {any} {any} {any}\n", .{ node, node2, res }); + // } if (old > 0) { // TODO sort for everything // if (ctx.currentSortIndex != null) { From 49634a61d68f5e987318d39609f0386d36004614 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 09:39:53 +0100 Subject: [PATCH 087/449] implement binary --- src/schema/defs/props/separate.ts | 47 ++++++++++++++++++------------- test/modify/props/binary.ts | 5 ++-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index ac0b52840d..0b137a6339 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -1,6 +1,7 @@ import native from '../../../native.js' import { NOT_COMPRESSED } from '../../../protocol/index.js' import type { + SchemaBinary, SchemaCardinality, SchemaString, SchemaVector, @@ -25,6 +26,22 @@ import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' +const validateMaxBytes = ( + bytes: number, + prop: { maxBytes?: number }, + path: string[], +) => { + if (prop.maxBytes !== undefined) { + if (bytes > prop.maxBytes) { + throw new Error( + `Byte length ${bytes} is larger than maxBytes ${ + prop.maxBytes + } for ${path.join('.')}`, + ) + } + } +} + export const string = class String extends BasePropDef { constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { super(prop, path, typeDef) @@ -64,22 +81,12 @@ export const string = class String extends BasePropDef { )}`, ) } + const normalized = val.normalize('NFKD') - // TODO make header! - // TODO compression buf.pushUint8(lang) buf.pushUint8(NOT_COMPRESSED) const written = buf.pushString(normalized) - - if (prop.maxBytes !== undefined) { - if (written > prop.maxBytes) { - throw new Error( - `Byte length ${written} is larger than maxBytes ${ - prop.maxBytes - } for ${this.path.join('.')}`, - ) - } - } + validateMaxBytes(written, prop, this.path) const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } @@ -139,6 +146,7 @@ export const json = class Json extends string { export const binary = class Binary extends BasePropDef { override type = PropType.binary + declare schema: SchemaBinary override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -148,15 +156,14 @@ export const binary = class Binary extends BasePropDef { if (!(value instanceof Uint8Array)) { throw new Error('Invalid type for binary ' + this.path.join('.')) } - const prop = this.schema as SchemaString - if (prop.maxBytes !== undefined && value.byteLength > prop.maxBytes) { - throw new Error( - `Byte length ${value.byteLength} is larger than maxBytes ${ - prop.maxBytes - } for ${this.path.join('.')}`, - ) - } + + validateMaxBytes(value.byteLength, this.schema, this.path) + + const crc = native.crc32(value) + buf.pushUint8(LangCode.none) + buf.pushUint8(NOT_COMPRESSED) buf.set(value, buf.length) + buf.pushUint32(crc) } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts index a21c2389a1..777e70b637 100644 --- a/test/modify/props/binary.ts +++ b/test/modify/props/binary.ts @@ -15,9 +15,8 @@ await test('modify binary', async (t) => { const id1 = await db.create('thing', { blob: b1, }) + const res1 = await db.query('thing', id1).get().toObject() - const res1: any = await db.query('thing', id1).get() - // Depending on how binary is returned (buffer or Uint8Array), likely Uint8Array as per rules deepEqual(res1.blob, b1) const b2 = new Uint8Array([4, 5, 6, 7]) @@ -25,6 +24,6 @@ await test('modify binary', async (t) => { blob: b2, }) - const res2: any = await db.query('thing', id1).get() + const res2 = await db.query('thing', id1).get().toObject() deepEqual(res2.blob, b2) }) From 1270db5569826adf452d490813bf2e32662e9281 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 10:06:00 +0100 Subject: [PATCH 088/449] add more props tests, added cardinality, no alias on edges --- native/modify/modify.zig | 7 +- src/schema/defs/props/separate.ts | 9 --- src/schema/schema/reference.ts | 10 ++- test/modify/props/alias.ts | 24 ++++++- test/modify/props/binary.ts | 49 ++++++++++++++ test/modify/props/cardinality.ts | 58 ++++++++++++++--- test/modify/props/default.ts | 103 +++++++++++++++++++++++++++--- test/modify/props/enum.ts | 47 ++++++++++++++ test/modify/props/json.ts | 49 ++++++++++++++ test/modify/props/object.ts | 65 +++++++++++++++++++ test/modify/props/text.ts | 50 +++++++++++++++ test/modify/props/vector.ts | 62 ++++++++++++++++++ 12 files changed, 502 insertions(+), 31 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 868dedde85..947cb17e95 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -100,8 +100,11 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 .cardinality => { var k: usize = 0; const cardinality = utils.readNext(t.ModifyCardinalityHeader, value, &k); - const hll = try Fields.ensurePropTypeString(node, propSchema); - selva.c.hll_init(hll, cardinality.precision, cardinality.sparse); + var hll = selva.c.selva_fields_get_selva_string(node, propSchema); + if (hll == null) { + hll = try Fields.ensurePropTypeString(node, propSchema); + selva.c.hll_init(hll, cardinality.precision, cardinality.sparse); + } while (k < value.len) { const hash = utils.read(u64, value, k); selva.c.hll_add(hll, hash); diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 0b137a6339..f386190da9 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -150,8 +150,6 @@ export const binary = class Binary extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is Uint8Array { if (!(value instanceof Uint8Array)) { throw new Error('Invalid type for binary ' + this.path.join('.')) @@ -202,14 +200,7 @@ export const cardinality = class Cardinality extends BasePropDef { override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is any { - if (value instanceof Uint8Array && value.byteLength !== 8) { - // buf.set(value, buf.length) - throw new Error('unhandled error cardi') - } - if (!Array.isArray(value)) { value = [value] } diff --git a/src/schema/schema/reference.ts b/src/schema/schema/reference.ts index 0156a8eb27..8df06f030f 100644 --- a/src/schema/schema/reference.ts +++ b/src/schema/schema/reference.ts @@ -10,6 +10,7 @@ import { import { parseProp, type SchemaProp } from './prop.js' import type { SchemaReferences } from './references.js' import type { SchemaOut } from './schema.js' +import type { SchemaAlias } from './alias.js' type ReferenceProps = nested extends true ? { prop?: never; dependent?: never; [edge: `$${string}`]: never } @@ -19,7 +20,10 @@ type ReferenceProps = nested extends true [edge: `$${string}`]: | Exclude< SchemaProp, - SchemaReferences | SchemaReference + | SchemaReferences + | SchemaReference + | SchemaAlias + | 'alias' > | SchemaReferences | SchemaReference @@ -61,7 +65,9 @@ export const parseReference = ( parsingEdges = true for (const key in def) { if (key.startsWith('$')) { - result[key] = parseProp(def[key], locales) + const edge = parseProp(def[key], locales) + assert(edge.type !== 'alias', 'Edge alias not allowed') + result[key] = edge } } parsingEdges = false diff --git a/test/modify/props/alias.ts b/test/modify/props/alias.ts index 6359bca863..193198ddc1 100644 --- a/test/modify/props/alias.ts +++ b/test/modify/props/alias.ts @@ -1,4 +1,5 @@ -import { deepEqual } from '../../shared/assert.js' +import { parseSchema } from '../../../src/schema.js' +import { deepEqual, throws } from '../../shared/assert.js' import { testDb } from '../../shared/index.js' import test from '../../shared/test.js' @@ -31,3 +32,24 @@ await test('modify alias', async (t) => { myAlias: 'another-alias', }) }) + +await test('schema alias on edge not allowed', async (t) => { + throws(async () => { + const schema = parseSchema({ + types: { + thing: { + myAlias: 'alias', + }, + holder: { + // @ts-expect-error + toThing: { + ref: 'thing', + prop: 'holders', + $edgeAlias: 'alias', + }, + }, + }, + }) + console.log(schema) + }) +}) diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts index 777e70b637..d71979e3c7 100644 --- a/test/modify/props/binary.ts +++ b/test/modify/props/binary.ts @@ -27,3 +27,52 @@ await test('modify binary', async (t) => { const res2 = await db.query('thing', id1).get().toObject() deepEqual(res2.blob, b2) }) + +await test('modify binary on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + blob: 'binary', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeBlob: 'binary', + }, + }, + }, + }) + + const b1 = new Uint8Array([1, 2, 3]) + const targetId = await db.create('thing', { blob: b1 }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeBlob: b1, + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeBlob') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeBlob, b1) + + const b2 = new Uint8Array([4, 5, 6, 7]) + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeBlob: b2, + }, + }) + + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeBlob') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeBlob, b2) +}) diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index b0fb9b293a..520065cec1 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -2,7 +2,7 @@ import { deepEqual } from '../../shared/assert.js' import { testDb } from '../../shared/index.js' import test from '../../shared/test.js' -await test('modify cardinality', async (t) => { +await test('modify cardinality basic', async (t) => { const db = await testDb(t, { types: { thing: { @@ -19,25 +19,67 @@ await test('modify cardinality', async (t) => { // Assuming we can read the count? Or the approximation. // The query might return the count. - const res1: any = await db.query('thing', id1).get() + const res1 = await db.query('thing', id1).get().toObject() deepEqual(res1.counter, 1) // Add another unique item await db.update('thing', id1, { counter: 'item2', }) - const res2: any = await db.query('thing', id1).get() + const res2 = await db.query('thing', id1).get().toObject() deepEqual(res2.counter, 2) // Add duplicate item (count shouldn't change) await db.update('thing', id1, { counter: 'item1', }) - const res3: any = await db.query('thing', id1).get() + const res3 = await db.query('thing', id1).get().toObject() deepEqual(res3.counter, 2) +}) + +await test('modify cardinality on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + counter: 'cardinality', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeCounter: 'cardinality', + }, + }, + }, + }) + + const targetId = await db.create('thing', { counter: 'a' }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeCounter: 'item1', + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeCounter') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeCounter, 1) + + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeCounter: 'item2', + }, + }) - // Add multiple items? - // await db.update('thing', id1, { - // counter: ['item3', 'item4'] - // }) + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeCounter') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeCounter, 2) }) diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index 1a70fc8741..b52dd9fae9 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -4,33 +4,75 @@ import test from '../../shared/test.js' await test('modify - default values basic', async (t) => { const db = await testDb(t, { + locales: { en: true }, types: { thing: { name: { type: 'string', default: 'Untitled' }, score: { type: 'number', default: 100 }, isActive: { type: 'boolean', default: true }, + // Added types + myAlias: { type: 'alias', default: 'my-alias' }, + myEnum: { enum: ['a', 'b'], default: 'a' }, + myJson: { type: 'json', default: { foo: 'bar' } }, + myText: { type: 'text', default: { en: 'Hello' } }, + myTs: { type: 'timestamp', default: 1000 }, }, }, }) // 1. Create with no values provided const a = await db.create('thing', {}) - const resA = await db.query('thing', a).get() - deepEqual(resA, { id: a, name: 'Untitled', score: 100, isActive: true }) + const resA: any = await db.query('thing', a).get() + deepEqual(resA, { + id: a, + name: 'Untitled', + score: 100, + isActive: true, + myAlias: 'my-alias', + myEnum: 'a', + myJson: { foo: 'bar' }, + myText: { en: 'Hello' }, + myTs: 1000, + }) // 2. Create with specific values (override default) const b = await db.create('thing', { name: 'Specific', score: 10, isActive: false, + myAlias: 'specific-alias', + myEnum: 'b', + myJson: { foo: 'baz' }, + myText: { en: 'Hi' } as any, + myTs: 2000, + }) + const resB: any = await db.query('thing', b).get() + deepEqual(resB, { + id: b, + name: 'Specific', + score: 10, + isActive: false, + myAlias: 'specific-alias', + myEnum: 'b', + myJson: { foo: 'baz' }, + myText: { en: 'Hi' }, + myTs: 2000, }) - const resB = await db.query('thing', b).get() - deepEqual(resB, { id: b, name: 'Specific', score: 10, isActive: false }) // 3. Create with mixed values - const c = await db.create('thing', { score: 50 }) - const resC = await db.query('thing', c).get() - deepEqual(resC, { id: c, name: 'Untitled', score: 50, isActive: true }) + const c = await db.create('thing', { score: 50, myEnum: 'b' }) + const resC: any = await db.query('thing', c).get() + deepEqual(resC, { + id: c, + name: 'Untitled', + score: 50, + isActive: true, + myAlias: 'my-alias', + myEnum: 'b', + myJson: { foo: 'bar' }, + myText: { en: 'Hello' }, + myTs: 1000, + }) }) await test('modify - default values on edge', async (t) => { @@ -45,6 +87,10 @@ await test('modify - default values on edge', async (t) => { prop: 'groups', $role: { type: 'string', default: 'member' }, $level: { type: 'number', default: 1 }, + $edgeAlias: { type: 'alias', default: 'edge-default-alias' }, + $edgeEnum: { enum: ['a', 'b'], default: 'a' }, + $edgeJson: { type: 'json', default: { foo: 'bar' } }, + $edgeText: { type: 'text', default: { en: 'Hello' } }, }, }, }, @@ -70,17 +116,56 @@ await test('modify - default values on edge', async (t) => { // 2. Create edge with edge props const g2 = await db.create('group', { - member: { id: u1, $role: 'admin', $level: 99 }, + member: { + id: u1, + $role: 'admin', + $level: 99, + $edgeAlias: 'specific-alias', + $edgeEnum: 'b', + $edgeJson: { foo: 'baz' }, + $edgeText: { en: 'Hi' } as any, + }, }) - const resG2 = await db + const resG2: any = await db .query('group', g2) .include('member.$role') .include('member.$level') .include('member.id') + .include('member.$edgeAlias') + .include('member.$edgeEnum') + .include('member.$edgeJson') + .include('member.$edgeText') .get() .toObject() deepEqual(resG2.member?.$role, 'admin') deepEqual(resG2.member?.$level, 99) + deepEqual(resG2.member?.$edgeAlias, 'specific-alias') + deepEqual(resG2.member?.$edgeEnum, 'b') + deepEqual(resG2.member?.$edgeJson, { foo: 'baz' }) + deepEqual(resG2.member?.$edgeText, { en: 'Hi' }) + + // 3. Check defaults on edge + const g3 = await db.create('group', { + member: { id: u1 }, + }) + + const resG3: any = await db + .query('group', g3) + .include('member.$role') + .include('member.$level') + .include('member.$edgeAlias') + .include('member.$edgeEnum') + .include('member.$edgeJson') + .include('member.$edgeText') + .get() + .toObject() + + deepEqual(resG3.member?.$role, 'member') + deepEqual(resG3.member?.$level, 1) + deepEqual(resG3.member?.$edgeAlias, 'edge-default-alias') + deepEqual(resG3.member?.$edgeEnum, 'a') + deepEqual(resG3.member?.$edgeJson, { foo: 'bar' }) + deepEqual(resG3.member?.$edgeText, { en: 'Hello' }) }) diff --git a/test/modify/props/enum.ts b/test/modify/props/enum.ts index 7fbcc5c257..004859e9ab 100644 --- a/test/modify/props/enum.ts +++ b/test/modify/props/enum.ts @@ -29,3 +29,50 @@ await test('modify enum', async (t) => { option: 'second', }) }) + +await test('modify enum on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + option: { enum: ['a', 'b'] }, + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeOption: { enum: ['first', 'second'] }, + }, + }, + }, + }) + + const targetId = await db.create('thing', { option: 'a' }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeOption: 'first', + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeOption') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeOption, 'first') + + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeOption: 'second', + }, + }) + + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeOption') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeOption, 'second') +}) diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts index 4875af0069..9fa4aa8b74 100644 --- a/test/modify/props/json.ts +++ b/test/modify/props/json.ts @@ -31,3 +31,52 @@ await test('modify json', async (t) => { data: arr, }) }) + +await test('modify json on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + data: 'json', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeData: 'json', + }, + }, + }, + }) + + const obj = { foo: 'bar' } + const targetId = await db.create('thing', { data: {} }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeData: obj, + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeData') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeData, obj) + + const obj2 = { baz: 'qux' } + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeData: obj2, + }, + }) + + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeData') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeData, obj2) +}) diff --git a/test/modify/props/object.ts b/test/modify/props/object.ts index 0ed6a7d01b..f6e2f0ae32 100644 --- a/test/modify/props/object.ts +++ b/test/modify/props/object.ts @@ -47,3 +47,68 @@ await test('modify object', async (t) => { }, }) }) + +await test('modify object on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + info: { type: 'object', props: { a: 'string' } }, + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeInfo: { + type: 'object', + props: { + title: 'string', + count: 'number', + }, + }, + }, + }, + }, + }) + + const targetId = await db.create('thing', { info: { a: 'a' } }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeInfo: { + title: 'edge title', + count: 5, + }, + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeInfo') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeInfo, { + title: 'edge title', + count: 5, + }) + + // Partial update + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeInfo: { + count: 15, + }, + }, + }) + + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeInfo') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeInfo, { + title: 'edge title', + count: 15, + }) +}) diff --git a/test/modify/props/text.ts b/test/modify/props/text.ts index 5752438ded..15867ff597 100644 --- a/test/modify/props/text.ts +++ b/test/modify/props/text.ts @@ -63,3 +63,53 @@ await test('modify text', async (t) => { }, }) }) + +await test('modify text on edge', async (t) => { + const db = await testDb(t, { + locales: { + en: true, + }, + types: { + thing: { + content: 'text', + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeText: 'text', + }, + }, + }, + }) + + const targetId = await db.create('thing', { content: { en: 'a' } as any }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeText: { en: 'edge hello' } as any, + }, + }) + + const res1 = await db + .query('holder', id1) + .include('toThing.$edgeText') + .get() + .toObject() + + deepEqual(res1.toThing?.$edgeText, { en: 'edge hello' }) + + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeText: { en: 'edge hi' } as any, + }, + }) + + const res2 = await db + .query('holder', id1) + .include('toThing.$edgeText') + .get() + .toObject() + deepEqual(res2.toThing?.$edgeText, { en: 'edge hi' }) +}) diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 3c940b3998..26a3e50f3a 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -80,3 +80,65 @@ await test('modify colvec', async (t) => { assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) }) + +await test('modify vector on edge', async (t) => { + const db = await testDb(t, { + types: { + thing: { + vec: { type: 'vector', size: 3, baseType: 'float32' }, + }, + holder: { + toThing: { + ref: 'thing', + prop: 'holders', + $edgeVec: { type: 'vector', size: 3, baseType: 'float32' }, + }, + }, + }, + }) + + const v1 = [1.1, 2.2, 3.3] + const targetId = await db.create('thing', { vec: v1 as any }) + const id1 = await db.create('holder', { + toThing: { + id: targetId, + $edgeVec: v1 as any, + }, + }) + + const res: any = await db + .query('holder', id1) + .include('toThing.$edgeVec') + .get() + .toObject() + + if (res.toThing) { + const vecArr = Array.from(res.toThing.$edgeVec as any) as number[] + assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) + assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) + assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) + } else { + assert.fail('toThing not found') + } + + const v2 = [4.4, 5.5, 6.6] + await db.update('holder', id1, { + toThing: { + id: targetId, + $edgeVec: v2 as any, + }, + }) + + const res2: any = await db + .query('holder', id1) + .include('toThing.$edgeVec') + .get() + .toObject() + + if (res2.toThing) { + const vecArr2 = Array.from(res2.toThing.$edgeVec as any) as number[] + assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) + assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) + assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) + } +}) From e9e2aca74a8100ca26fe010cbb84d2a718e99dd7 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 10:25:47 +0100 Subject: [PATCH 089/449] Fix alias moving --- clibs/lib/selva/alias.c | 20 ++++++++------------ test/modify/props/alias.ts | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index 2ee3f768a1..b8e8fdf857 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -58,12 +58,12 @@ static struct SelvaAlias *insert_alias_by_name(struct SelvaAliases *aliases, str return old_alias; } -static void remove_alias_by_dest(struct SelvaAliases *aliases, struct SelvaAlias *alias) +static inline void remove_alias_by_dest(struct SelvaAliases *aliases, struct SelvaAlias *alias) { RB_REMOVE(SelvaAliasesByDest, &aliases->alias_by_dest, alias); } -static void remove_alias_by_name(struct SelvaAliases *aliases, struct SelvaAlias *alias) +static inline void remove_alias_by_name(struct SelvaAliases *aliases, struct SelvaAlias *alias) { struct SelvaAlias *removed = RB_REMOVE(SelvaAliasesByName, &aliases->alias_by_name, alias); assert(removed); @@ -73,6 +73,7 @@ static void remove_alias_by_name(struct SelvaAliases *aliases, struct SelvaAlias static void del_alias(struct SelvaAliases *aliases, struct SelvaAlias *alias) { remove_alias_by_name(aliases, alias); + remove_alias_by_dest(aliases, alias); selva_free(alias); } @@ -84,19 +85,16 @@ size_t selva_alias_count(const struct SelvaAliases *aliases) node_id_t selva_set_alias_p(struct SelvaAliases *aliases, struct SelvaAlias *new_alias) { struct SelvaAlias *old_alias; + struct SelvaAlias *old_by_dest; node_id_t old_dest = 0; -retry: - old_alias = insert_alias_by_name(aliases, new_alias); - if (old_alias) { + while ((old_alias = insert_alias_by_name(aliases, new_alias))) { old_dest = old_alias->dest; del_alias(aliases, old_alias); - goto retry; } - struct SelvaAlias *prev_by_dest = RB_INSERT(SelvaAliasesByDest, &aliases->alias_by_dest, new_alias); - if (prev_by_dest) { - del_alias(aliases, prev_by_dest); + while ((old_by_dest = RB_INSERT(SelvaAliasesByDest, &aliases->alias_by_dest, new_alias))) { + del_alias(aliases, old_by_dest); } return old_dest; @@ -143,9 +141,7 @@ void selva_del_alias_by_dest(struct SelvaAliases *aliases, node_id_t dest) return; } - remove_alias_by_dest(aliases, alias); - remove_alias_by_name(aliases, alias); - selva_free(alias); + del_alias(aliases, alias); } struct SelvaNodeRes selva_get_alias(struct SelvaTypeEntry *type, struct SelvaAliases *aliases, const char *name_str, size_t name_len) diff --git a/test/modify/props/alias.ts b/test/modify/props/alias.ts index 193198ddc1..07ccf87d43 100644 --- a/test/modify/props/alias.ts +++ b/test/modify/props/alias.ts @@ -16,6 +16,9 @@ await test('modify alias', async (t) => { const id1 = await db.create('thing', { myAlias: 'my-alias-value', }) + const id2 = await db.create('thing', { + myAlias: 'b-alias', + }) deepEqual(await db.query('thing', id1).get(), { id: id1, @@ -31,6 +34,24 @@ await test('modify alias', async (t) => { id: id1, myAlias: 'another-alias', }) + + await db.update('thing', id2, { + myAlias: 'another-alias', + }) + + deepEqual(await db.query('thing', { myAlias: 'b-alias' }).get(), null) + deepEqual(await db.query('thing', { myAlias: 'another-alias' }).get(), { + id: id2, + myAlias: 'another-alias', + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + myAlias: '', + }) + deepEqual(await db.query('thing', id2).get(), { + id: id2, + myAlias: 'another-alias', + }) }) await test('schema alias on edge not allowed', async (t) => { From 0231d584f0f44f232292b06a231c7595a5c61065 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 10:54:05 +0100 Subject: [PATCH 090/449] Save aliases in common.sdb Closes FDN-1863 --- clibs/lib/selva/io/dump.c | 176 ++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 84 deletions(-) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 28f421b6fb..e775aea6c0 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -39,6 +39,7 @@ */ #define DUMP_MAGIC_SCHEMA 3360690301 /* common.sdb */ #define DUMP_MAGIC_EXPIRE 2147483647 /* common.sdb */ +#define DUMP_MAGIC_ALIASES 4019181209 /* common.sdb */ #define DUMP_MAGIC_COMMON_IDS 2974848157 /* common.sdb */ #define DUMP_MAGIC_COMMON_BLOCKS 2734165127 /* common.sdb */ #define DUMP_MAGIC_TYPES 3550908863 /* [block].sdb */ @@ -48,7 +49,6 @@ #define DUMP_MAGIC_FIELD_BEGIN 3734376047 #endif #define DUMP_MAGIC_FIELD_END 2944546091 -#define DUMP_MAGIC_ALIASES 4019181209 #define DUMP_MAGIC_COLVEC 1901731729 #define DUMP_MAGIC_BLOCK_HASH 2898966349 @@ -263,29 +263,6 @@ static void save_node(struct selva_io *io, struct SelvaDb *db, struct SelvaNode save_node_fields(io, schema, node); } -static void save_aliases(struct selva_io *io, struct SelvaTypeEntry *te) -{ - const sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; - - write_dump_magic(io, DUMP_MAGIC_ALIASES); - - for (size_t i = 0; i < nr_aliases; i++) { - struct SelvaAliases *aliases = &te->aliases[i]; - sdb_nr_aliases_t nr_aliases_by_name = aliases->nr_aliases; - struct SelvaAlias *alias; - - io->sdb_write(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); - RB_FOREACH(alias, SelvaAliasesByName, &aliases->alias_by_name) { - const char *name_str = alias->name; - const sdb_arr_len_t name_len = alias->name_len; - - io->sdb_write(&name_len, sizeof(name_len), 1, io); - io->sdb_write(name_str, sizeof(*name_str), name_len, io); - io->sdb_write(&alias->dest, sizeof(alias->dest), 1, io); - } - } -} - static void save_schema(struct selva_io *io, struct SelvaDb *db) { const sdb_nr_types_t nr_types = db->types.count; @@ -331,6 +308,33 @@ static void save_expire(struct selva_io *io, struct SelvaDb *db) } +static void save_aliases(struct selva_io *io, struct SelvaDb *db) +{ + struct SelvaTypeEntry *te; + + write_dump_magic(io, DUMP_MAGIC_ALIASES); + + RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { + const sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; + + for (size_t i = 0; i < nr_aliases; i++) { + struct SelvaAliases *aliases = &te->aliases[i]; + sdb_nr_aliases_t nr_aliases_by_name = aliases->nr_aliases; + struct SelvaAlias *alias; + + io->sdb_write(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); + RB_FOREACH(alias, SelvaAliasesByName, &aliases->alias_by_name) { + const char *name_str = alias->name; + const sdb_arr_len_t name_len = alias->name_len; + + io->sdb_write(&name_len, sizeof(name_len), 1, io); + io->sdb_write(name_str, sizeof(*name_str), name_len, io); + io->sdb_write(&alias->dest, sizeof(alias->dest), 1, io); + } + } + } +} + static void save_common_ids(struct selva_io *io, const node_id_t *ids_data, size_t meta_len) { const sdb_arr_len_t len = meta_len; @@ -384,6 +388,7 @@ int selva_dump_save_common(struct SelvaDb *db, struct selva_dump_common_data *co */ save_schema(&io, db); save_expire(&io, db); + save_aliases(&io, db); save_common_ids(&io, com->ids_data, com->ids_len); save_common_blocks(&io, db, com); @@ -491,7 +496,6 @@ int selva_dump_save_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i * note: colvec is hashed in node_hash. */ selva_dump_save_colvec(&io, db, te, start); - save_aliases(&io, te); selva_hash128_t block_hash = selva_hash_digest(hash_state); selva_hash_free_state(hash_state); @@ -554,33 +558,6 @@ static int load_schema(struct selva_io *io, struct SelvaDb *db) return 0; } -__attribute__((warn_unused_result)) -static int load_expire(struct selva_io *io, struct SelvaDb *db) -{ - sdb_arr_len_t count; - - if (!read_dump_magic(io, DUMP_MAGIC_EXPIRE)) { - selva_io_errlog(io, "Ivalid types magic"); - return SELVA_EINVAL; - } - - io->sdb_read(&count, sizeof(count), 1, io); - - for (sdb_arr_len_t i = 0; i < count; i++) { - node_type_t type; - node_id_t node_id; - int64_t expire; - - io->sdb_read(&type, sizeof(type), 1, io); - io->sdb_read(&node_id, sizeof(node_id), 1, io); - io->sdb_read(&expire, sizeof(expire), 1, io); - - selva_expire_node(db, type, node_id, expire, SELVA_EXPIRE_NODE_STRATEGY_IGNORE); - } - - return 0; -} - __attribute__((warn_unused_result)) static int load_string(struct selva_io *io, struct selva_string *s, const struct sdb_string_meta *meta) { @@ -822,37 +799,6 @@ static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct Selva return node_id; } -__attribute__((warn_unused_result)) -static int load_aliases(struct selva_io *io, struct SelvaTypeEntry *te) -{ - sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; - - if (!read_dump_magic(io, DUMP_MAGIC_ALIASES)) { - selva_io_errlog(io, "Invalid aliases magic for type %d", te->type); - return SELVA_EINVAL; - } - - for (sdb_nr_aliases_t i = 0; i < nr_aliases; i++) { - sdb_nr_aliases_t nr_aliases_by_name; - - io->sdb_read(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); - for (size_t j = 0; j < nr_aliases_by_name; j++) { - sdb_arr_len_t name_len; - struct SelvaAlias *alias; - - io->sdb_read(&name_len, sizeof(name_len), 1, io); - alias = selva_malloc(sizeof_wflex(struct SelvaAlias, name, name_len)); - io->sdb_read(alias->name, sizeof(char), name_len, io); - alias->name_len = name_len; - io->sdb_read(&alias->dest, sizeof(alias->dest), 1, io); - - selva_set_alias_p(&te->aliases[i], alias); - } - } - - return 0; -} - __attribute__((warn_unused_result)) static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te) { @@ -923,11 +869,72 @@ static int load_type(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEn err = load_nodes(io, db, te); err = (err) ?: load_colvec(io, te); - err = (err) ?: load_aliases(io, te); return err; } +__attribute__((warn_unused_result)) +static int load_expire(struct selva_io *io, struct SelvaDb *db) +{ + sdb_arr_len_t count; + + if (!read_dump_magic(io, DUMP_MAGIC_EXPIRE)) { + selva_io_errlog(io, "Ivalid types magic"); + return SELVA_EINVAL; + } + + io->sdb_read(&count, sizeof(count), 1, io); + + for (sdb_arr_len_t i = 0; i < count; i++) { + node_type_t type; + node_id_t node_id; + int64_t expire; + + io->sdb_read(&type, sizeof(type), 1, io); + io->sdb_read(&node_id, sizeof(node_id), 1, io); + io->sdb_read(&expire, sizeof(expire), 1, io); + + selva_expire_node(db, type, node_id, expire, SELVA_EXPIRE_NODE_STRATEGY_IGNORE); + } + + return 0; +} + +__attribute__((warn_unused_result)) +static int load_aliases(struct selva_io *io, struct SelvaDb *db) +{ + struct SelvaTypeEntry *te; + + if (!read_dump_magic(io, DUMP_MAGIC_ALIASES)) { + selva_io_errlog(io, "Invalid aliases magic"); + return SELVA_EINVAL; + } + + RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { + const sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; + + for (sdb_nr_aliases_t i = 0; i < nr_aliases; i++) { + sdb_nr_aliases_t nr_aliases_by_name; + + io->sdb_read(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); + for (size_t j = 0; j < nr_aliases_by_name; j++) { + sdb_arr_len_t name_len; + struct SelvaAlias *alias; + + io->sdb_read(&name_len, sizeof(name_len), 1, io); + alias = selva_malloc(sizeof_wflex(struct SelvaAlias, name, name_len)); + io->sdb_read(alias->name, sizeof(char), name_len, io); + alias->name_len = name_len; + io->sdb_read(&alias->dest, sizeof(alias->dest), 1, io); + + selva_set_alias_p(&te->aliases[i], alias); + } + } + } + + return 0; +} + __attribute__((warn_unused_result)) static int load_common_ids(struct selva_io *io, struct selva_dump_common_data *com) { @@ -1011,6 +1018,7 @@ int selva_dump_load_common(struct SelvaDb *db, struct selva_dump_common_data *co err = load_schema(&io, db); err = err ?: load_expire(&io, db); + err = err ?: load_aliases(&io, db); err = err ?: load_common_ids(&io, com); err = err ?: load_common_blocks(&io, db, com); selva_io_end(&io, nullptr); From 919d11ae3bbdac49b50c0050c199f0389b0be0ba Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 11:02:43 +0100 Subject: [PATCH 091/449] init text --- native/modify/modify.zig | 10 ++++ src/db-client/index.ts | 12 ++--- src/db-client/modify/create.ts | 2 +- src/db-client/modify/props.ts | 2 + src/db-client/modify/types.ts | 86 ++++++++++++++++++++----------- src/db-client/modify/update.ts | 2 +- src/db-client/modify/upsert.ts | 4 +- src/schema/defs/props/separate.ts | 39 ++++++++++---- src/schema/schema/locales.ts | 2 +- src/schema/schema/schema.ts | 10 +++- test/modify/props/default.ts | 11 ++-- test/modify/props/text.ts | 7 +-- test/modify/props/vector.ts | 51 +++++++++--------- 13 files changed, 151 insertions(+), 87 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 947cb17e95..654f6f5a21 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -74,6 +74,16 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 const prop = utils.readNext(t.ModifyPropHeader, data, &j); const value = data[j .. j + prop.size]; switch (prop.type) { + .text => { + var k: usize = 0; + while (k < value.len) { + const textSize = utils.read(u32, value, k); + k += 4; + const textValue = value[k .. k + textSize]; + k += textSize; + try Fields.setText(node, propSchema, textValue); + } + }, .alias => { if (value.len == 0) continue; const id = Node.getNodeId(node); diff --git a/src/db-client/index.ts b/src/db-client/index.ts index d1e98ba1ba..f5e8e3bbf0 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -96,7 +96,7 @@ export class DbClient = SchemaOut> extends DbShared { create( type: T, - obj: InferPayload[T], + obj: InferPayload, opts?: ModifyOpts, ): BasedCreatePromise { return new BasedModify( @@ -113,7 +113,7 @@ export class DbClient = SchemaOut> extends DbShared { update( type: T, target: number | BasedModify, - obj: InferPayload[T], + obj: InferPayload, opts?: ModifyOpts, ): BasedUpdatePromise { return new BasedModify( @@ -130,8 +130,8 @@ export class DbClient = SchemaOut> extends DbShared { upsert( type: T, - target: InferTarget[T], - obj: InferPayload[T], + target: InferTarget, + obj: InferPayload, opts?: ModifyOpts, ): BasedUpsertPromise { return new BasedModify( @@ -149,8 +149,8 @@ export class DbClient = SchemaOut> extends DbShared { insert( type: T, - target: InferTarget[T], - obj: InferPayload[T], + target: InferTarget, + obj: InferPayload, opts?: ModifyOpts, ): BasedInsertPromise { return new BasedModify( diff --git a/src/db-client/modify/create.ts b/src/db-client/modify/create.ts index ff470c1f12..143a308c6c 100644 --- a/src/db-client/modify/create.ts +++ b/src/db-client/modify/create.ts @@ -16,7 +16,7 @@ export const serializeCreate = < >( schema: S, type: T, - payload: InferPayload[T], + payload: InferPayload, buf: AutoSizedUint8Array, lang: LangCodeEnum, ) => { diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index 76de537dd0..7bbb5e6549 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -7,6 +7,8 @@ import { writeModifyPropHeaderProps, type LangCodeEnum, type ModifyEnum, + PropType, + LangCode, } from '../../zigTsExports.js' export const serializeProps = ( diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 7f548ddd8e..bf455e8c68 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -25,7 +25,7 @@ type TypeMap = { int32: NumInc uint32: NumInc boolean: boolean - text: string + text: string | Record json: any timestamp: NumInc | string | Date binary: Uint8Array @@ -35,57 +35,85 @@ type TypeMap = { cardinality: string | string[] } -type InferEdgeProps = { +type InferEdgeProps< + Prop, + Types, + Locales extends Record = Record, +> = { [K in keyof Prop as K extends `$${string}` ? K : never]?: Prop[K] extends keyof TypeMap ? TypeMap[Prop[K]] - : InferProp + : InferProp } -type InferRefValue = +type InferRefValue< + Prop, + Types, + Locales extends Record = Record, +> = | number | BasedModify - | ({ id: number | BasedModify } & InferEdgeProps) + | ({ id: number | BasedModify } & InferEdgeProps) -type InferReferences = - | InferRefValue[] +type InferReferences< + Prop, + Types, + Locales extends Record = Record, +> = + | InferRefValue[] | { - add?: InferRefValue[] - update?: InferRefValue[] + add?: InferRefValue[] + update?: InferRefValue[] delete?: (number | BasedModify)[] } -type InferProp = Prop extends { type: 'object'; props: infer P } - ? InferType - : Prop extends { type: infer T extends keyof TypeMap } - ? TypeMap[T] - : Prop extends { enum: infer E extends readonly any[] } - ? E[number] - : Prop extends { ref: string } - ? InferRefValue - : Prop extends { items: { ref: string } } - ? InferReferences - : never +type InferProp< + Prop, + Types, + Locales extends Record = Record, +> = Prop extends { type: 'text' } + ? string | Partial> + : Prop extends { type: 'object'; props: infer P } + ? InferType + : Prop extends { type: infer T extends keyof TypeMap } + ? TypeMap[T] + : Prop extends { enum: infer E extends readonly any[] } + ? E[number] + : Prop extends { ref: string } + ? InferRefValue + : Prop extends { items: { ref: string } } + ? InferReferences + : never -type InferType = { +type InferType< + Props, + Types, + Locales extends Record = Record, +> = { [K in keyof Props as Props[K] extends { required: true } ? K - : never]: InferProp + : never]: InferProp } & { [K in keyof Props as Props[K] extends { required: true } ? never - : K]?: InferProp + : K]?: InferProp } -export type InferPayload> = { - [K in keyof Types]: InferType -} +export type InferPayload< + S extends { types: any; locales?: any }, + T extends keyof S['types'], +> = InferType< + S['types'][T]['props'], + S['types'], + S['locales'] extends Record ? S['locales'] : {} +> type InferAliasProps = { [K in keyof Props as Props[K] extends { type: 'alias' } ? K : never]?: string } -export type InferTarget> = { - [K in keyof Types]: InferAliasProps -} +export type InferTarget< + S extends { types: any }, + T extends keyof S['types'], +> = InferAliasProps diff --git a/src/db-client/modify/update.ts b/src/db-client/modify/update.ts index 316f62bdb4..626e14294f 100644 --- a/src/db-client/modify/update.ts +++ b/src/db-client/modify/update.ts @@ -17,7 +17,7 @@ export const serializeUpdate = < schema: S, type: T, item: number | BasedModify, - payload: InferPayload[T], + payload: InferPayload, buf: AutoSizedUint8Array, lang: LangCodeEnum, ) => { diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index 41cb7a261e..840fcedf11 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -16,8 +16,8 @@ export const serializeUpsert = < >( schema: S, type: T, - target: InferTarget[T], - payload: InferPayload[T], + target: InferTarget, + payload: InferPayload, buf: AutoSizedUint8Array, lang: LangCodeEnum, op: typeof Modify.insert | typeof Modify.upsert, diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index f386190da9..240f6bed89 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -45,15 +45,16 @@ const validateMaxBytes = ( export const string = class String extends BasePropDef { constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { super(prop, path, typeDef) - if (prop.maxBytes && prop.maxBytes < 61) { - this.size = prop.maxBytes + 1 - } else if (prop.max && prop.max < 31) { - this.size = prop.max * 2 + 1 - } - if (this.size) { - this.type = PropType.stringFixed - this.pushValue = this.pushFixedValue as any - } + // TODO! + // if (prop.maxBytes && prop.maxBytes < 61) { + // this.size = prop.maxBytes + 1 + // } else if (prop.max && prop.max < 31) { + // this.size = prop.max * 2 + 1 + // } + // if (this.size) { + // this.type = PropType.stringFixed + // this.pushValue = this.pushFixedValue as any + // } } declare schema: SchemaString override type: PropTypeEnum = PropType.string @@ -114,6 +115,26 @@ export const string = class String extends BasePropDef { // TODO do it nice export const text = class Text extends string { override type = PropType.text + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, + ) { + if (typeof value === 'string') { + const index = buf.reserveUint32() + const start = buf.length + super.pushValue(buf, value, op, lang) + buf.writeUint32(buf.length - start, index) + } else if (typeof value === 'object' && value !== null) { + for (const key in value) { + const index = buf.reserveUint32() + const start = buf.length + super.pushValue(buf, value[key], op, LangCode[key]) + buf.writeUint32(buf.length - start, index) + } + } + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaText(buf, { type: PropTypeSelva.text, diff --git a/src/schema/schema/locales.ts b/src/schema/schema/locales.ts index 835a466685..3707ea51ca 100644 --- a/src/schema/schema/locales.ts +++ b/src/schema/schema/locales.ts @@ -1,6 +1,6 @@ import { LangCode } from '../../zigTsExports.js' import { assert, isBoolean, isRecord, type RequiredIfStrict } from './shared.js' -type LangName = keyof typeof LangCode +export type LangName = keyof typeof LangCode export type SchemaLocale = RequiredIfStrict< { diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index cc4450f4bf..7af82d03c3 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -62,6 +62,8 @@ type NormalizeType = T extends { props: infer P } ? Omit & { props: { [K in keyof P]: NormalizeProp } } : { props: { [K in keyof T]: NormalizeProp } } +import { type LangName, type SchemaLocale } from './locales.js' + export type ResolveSchema = Omit< SchemaOut, 'types' | 'locales' @@ -69,7 +71,13 @@ export type ResolveSchema = Omit< types: { [K in keyof S['types']]: NormalizeType } - locales: SchemaLocales + locales: S extends { locales: infer L } + ? L extends readonly (infer K extends LangName)[] + ? Partial>> + : L extends Record + ? Partial>> + : SchemaLocales + : SchemaLocales } const isMigrations = (v: unknown): v is SchemaMigrations => diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index b52dd9fae9..540bbc9b2b 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -43,7 +43,7 @@ await test('modify - default values basic', async (t) => { myAlias: 'specific-alias', myEnum: 'b', myJson: { foo: 'baz' }, - myText: { en: 'Hi' } as any, + myText: { en: 'Hi' }, myTs: 2000, }) const resB: any = await db.query('thing', b).get() @@ -77,6 +77,7 @@ await test('modify - default values basic', async (t) => { await test('modify - default values on edge', async (t) => { const db = await testDb(t, { + locales: { en: true }, types: { user: { name: 'string', @@ -87,7 +88,6 @@ await test('modify - default values on edge', async (t) => { prop: 'groups', $role: { type: 'string', default: 'member' }, $level: { type: 'number', default: 1 }, - $edgeAlias: { type: 'alias', default: 'edge-default-alias' }, $edgeEnum: { enum: ['a', 'b'], default: 'a' }, $edgeJson: { type: 'json', default: { foo: 'bar' } }, $edgeText: { type: 'text', default: { en: 'Hello' } }, @@ -120,10 +120,9 @@ await test('modify - default values on edge', async (t) => { id: u1, $role: 'admin', $level: 99, - $edgeAlias: 'specific-alias', $edgeEnum: 'b', $edgeJson: { foo: 'baz' }, - $edgeText: { en: 'Hi' } as any, + $edgeText: { en: 'Hi' }, }, }) @@ -132,7 +131,6 @@ await test('modify - default values on edge', async (t) => { .include('member.$role') .include('member.$level') .include('member.id') - .include('member.$edgeAlias') .include('member.$edgeEnum') .include('member.$edgeJson') .include('member.$edgeText') @@ -141,7 +139,6 @@ await test('modify - default values on edge', async (t) => { deepEqual(resG2.member?.$role, 'admin') deepEqual(resG2.member?.$level, 99) - deepEqual(resG2.member?.$edgeAlias, 'specific-alias') deepEqual(resG2.member?.$edgeEnum, 'b') deepEqual(resG2.member?.$edgeJson, { foo: 'baz' }) deepEqual(resG2.member?.$edgeText, { en: 'Hi' }) @@ -155,7 +152,6 @@ await test('modify - default values on edge', async (t) => { .query('group', g3) .include('member.$role') .include('member.$level') - .include('member.$edgeAlias') .include('member.$edgeEnum') .include('member.$edgeJson') .include('member.$edgeText') @@ -164,7 +160,6 @@ await test('modify - default values on edge', async (t) => { deepEqual(resG3.member?.$role, 'member') deepEqual(resG3.member?.$level, 1) - deepEqual(resG3.member?.$edgeAlias, 'edge-default-alias') deepEqual(resG3.member?.$edgeEnum, 'a') deepEqual(resG3.member?.$edgeJson, { foo: 'bar' }) deepEqual(resG3.member?.$edgeText, { en: 'Hello' }) diff --git a/test/modify/props/text.ts b/test/modify/props/text.ts index 15867ff597..2e5796d123 100644 --- a/test/modify/props/text.ts +++ b/test/modify/props/text.ts @@ -20,7 +20,7 @@ await test('modify text', async (t) => { content: { en: 'Hello', de: 'Hallo', - } as any, + }, }) deepEqual(await db.query('thing', id1).get(), { @@ -28,6 +28,7 @@ await test('modify text', async (t) => { content: { en: 'Hello', de: 'Hallo', + nl: '', }, }) @@ -35,7 +36,7 @@ await test('modify text', async (t) => { await db.update('thing', id1, { content: { nl: 'Hallo', - } as any, + }, }) deepEqual(await db.query('thing', id1).get(), { @@ -51,7 +52,7 @@ await test('modify text', async (t) => { await db.update('thing', id1, { content: { en: 'Hi', - } as any, + }, }) deepEqual(await db.query('thing', id1).get(), { diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 26a3e50f3a..c7d64b6c62 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -1,4 +1,3 @@ -import { deepEqual } from '../../shared/assert.js' import { testDb } from '../../shared/index.js' import test from '../../shared/test.js' import assert from 'node:assert' @@ -12,30 +11,30 @@ await test('modify vector', async (t) => { }, }) - const v1 = [1.1, 2.2, 3.3] + const v1 = new Float64Array([1.1, 2.2, 3.3]) const id1 = await db.create('thing', { - vec: v1 as any, + vec: v1, }) // Float precision might require approximate equality or strict check if implementation preserves bits // For now assuming deepEqual works or we might need a tolerance check - const res: any = await db.query('thing', id1).get() + const res = await db.query('thing', id1).get().toObject() // Convert result back to array if it is returned as TypedArray - const vecArr = Array.from(res.vec as any) as number[] + const vecArr = Array.from(res.vec) as number[] // Check approximate values assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) - const v2 = [4.4, 5.5, 6.6] + const v2 = new Float64Array([4.4, 5.5, 6.6]) await db.update('thing', id1, { - vec: v2 as any, + vec: v2, }) - const res2: any = await db.query('thing', id1).get() - const vecArr2 = Array.from(res2.vec as any) as number[] + const res2 = await db.query('thing', id1).get().toObject() + const vecArr2 = Array.from(res2.vec) as number[] assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) @@ -56,25 +55,25 @@ await test('modify colvec', async (t) => { // colvec behaves similarly to vector in terms of setting/getting from client perspective // but internally stored differently (columnar). - const v1 = [1.1, 2.2, 3.3] + const v1 = new Float64Array([1.1, 2.2, 3.3]) const id1 = await db.create('thing', { - vec: v1 as any, + vec: v1, }) - const res: any = await db.query('thing', id1).get() - const vecArr = Array.from(res.vec as any) as number[] + const res = await db.query('thing', id1).get().toObject() + const vecArr = Array.from(res.vec) as number[] assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) - const v2 = [4.4, 5.5, 6.6] + const v2 = new Float64Array([4.4, 5.5, 6.6]) await db.update('thing', id1, { - vec: v2 as any, + vec: v2, }) - const res2: any = await db.query('thing', id1).get() - const vecArr2 = Array.from(res2.vec as any) as number[] + const res2 = await db.query('thing', id1).get().toObject() + const vecArr2 = Array.from(res2.vec) as number[] assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) @@ -97,23 +96,23 @@ await test('modify vector on edge', async (t) => { }, }) - const v1 = [1.1, 2.2, 3.3] - const targetId = await db.create('thing', { vec: v1 as any }) + const v1 = new Float64Array([1.1, 2.2, 3.3]) + const targetId = await db.create('thing', { vec: v1 }) const id1 = await db.create('holder', { toThing: { id: targetId, - $edgeVec: v1 as any, + $edgeVec: v1, }, }) - const res: any = await db + const res = await db .query('holder', id1) .include('toThing.$edgeVec') .get() .toObject() if (res.toThing) { - const vecArr = Array.from(res.toThing.$edgeVec as any) as number[] + const vecArr = Array.from(res.toThing.$edgeVec) as number[] assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) @@ -121,22 +120,22 @@ await test('modify vector on edge', async (t) => { assert.fail('toThing not found') } - const v2 = [4.4, 5.5, 6.6] + const v2 = new Float64Array([4.4, 5.5, 6.6]) await db.update('holder', id1, { toThing: { id: targetId, - $edgeVec: v2 as any, + $edgeVec: v2, }, }) - const res2: any = await db + const res2 = await db .query('holder', id1) .include('toThing.$edgeVec') .get() .toObject() if (res2.toThing) { - const vecArr2 = Array.from(res2.toThing.$edgeVec as any) as number[] + const vecArr2 = Array.from(res2.toThing.$edgeVec) as number[] assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) From c8256a60bd99309628f73f3b99f7113df65d3f77 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 11:22:51 +0100 Subject: [PATCH 092/449] add default for text and json --- src/schema/defs/props/separate.ts | 32 +++++++++++++++++-------------- src/schema/schema/alias.ts | 7 +++++-- test/modify/props/default.ts | 10 ++-------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 240f6bed89..bb4e53e910 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -4,6 +4,7 @@ import type { SchemaBinary, SchemaCardinality, SchemaString, + SchemaText, SchemaVector, } from '../../../schema.js' import { ENCODER } from '../../../utils/uint8.js' @@ -67,7 +68,8 @@ export const string = class String extends BasePropDef { if (typeof val !== 'string') { throw new Error('Invalid type for string ' + this.path.join('.')) } - const prop = this.schema as SchemaString + + const prop = this.schema if (prop.min !== undefined && val.length < prop.min) { throw new Error( `Length ${val.length} is smaller than min ${prop.min} for ${this.path.join( @@ -92,7 +94,7 @@ export const string = class String extends BasePropDef { buf.pushUint32(crc) } - pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} + // pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} override pushSelvaSchema(buf: AutoSizedUint8Array) { const index = pushSelvaSchemaString(buf, { @@ -112,9 +114,10 @@ export const string = class String extends BasePropDef { } } -// TODO do it nice export const text = class Text extends string { override type = PropType.text + // @ts-ignore + declare schema: SchemaText override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -136,10 +139,18 @@ export const text = class Text extends string { } } override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaText(buf, { - type: PropTypeSelva.text, - nrDefaults: 0, - }) + if (this.schema.default) { + pushSelvaSchemaText(buf, { + type: PropTypeSelva.text, + nrDefaults: Object.keys(this.schema.default).length, + }) + this.pushValue(buf, this.schema.default) + } else { + pushSelvaSchemaText(buf, { + type: PropTypeSelva.text, + nrDefaults: 0, + }) + } } } @@ -156,13 +167,6 @@ export const json = class Json extends string { } super.pushValue(buf, JSON.stringify(value), op, lang) } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaString(buf, { - type: PropTypeSelva.string, - fixedLen: 0, - defaultLen: 0, - }) - } } export const binary = class Binary extends BasePropDef { diff --git a/src/schema/schema/alias.ts b/src/schema/schema/alias.ts index d3dc1f9ae6..d44df0bb1b 100644 --- a/src/schema/schema/alias.ts +++ b/src/schema/schema/alias.ts @@ -1,11 +1,14 @@ +import { assert } from './shared.js' import { parseString, type SchemaString } from './string.js' -export type SchemaAlias = Omit & { +export type SchemaAlias = Omit & { type: 'alias' + default?: never } export const parseAlias = (def: Record): SchemaAlias => { def.type = 'string' + assert(def.default === undefined, 'Default alias not allowed') const { type, ...rest } = parseString(def) - return { type: 'alias', ...rest } + return { type: 'alias', ...rest } as SchemaAlias } diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index 540bbc9b2b..11e8ddedc0 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -10,8 +10,6 @@ await test('modify - default values basic', async (t) => { name: { type: 'string', default: 'Untitled' }, score: { type: 'number', default: 100 }, isActive: { type: 'boolean', default: true }, - // Added types - myAlias: { type: 'alias', default: 'my-alias' }, myEnum: { enum: ['a', 'b'], default: 'a' }, myJson: { type: 'json', default: { foo: 'bar' } }, myText: { type: 'text', default: { en: 'Hello' } }, @@ -28,7 +26,6 @@ await test('modify - default values basic', async (t) => { name: 'Untitled', score: 100, isActive: true, - myAlias: 'my-alias', myEnum: 'a', myJson: { foo: 'bar' }, myText: { en: 'Hello' }, @@ -40,19 +37,17 @@ await test('modify - default values basic', async (t) => { name: 'Specific', score: 10, isActive: false, - myAlias: 'specific-alias', myEnum: 'b', myJson: { foo: 'baz' }, myText: { en: 'Hi' }, myTs: 2000, }) - const resB: any = await db.query('thing', b).get() + const resB = await db.query('thing', b).get() deepEqual(resB, { id: b, name: 'Specific', score: 10, isActive: false, - myAlias: 'specific-alias', myEnum: 'b', myJson: { foo: 'baz' }, myText: { en: 'Hi' }, @@ -61,13 +56,12 @@ await test('modify - default values basic', async (t) => { // 3. Create with mixed values const c = await db.create('thing', { score: 50, myEnum: 'b' }) - const resC: any = await db.query('thing', c).get() + const resC = await db.query('thing', c).get() deepEqual(resC, { id: c, name: 'Untitled', score: 50, isActive: true, - myAlias: 'my-alias', myEnum: 'b', myJson: { foo: 'bar' }, myText: { en: 'Hello' }, From f1ca98a8c0a1c20da60ef8903f6189b5e82afa49 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 11:26:24 +0100 Subject: [PATCH 093/449] fix in parsing edges --- src/schema/schema/reference.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/schema/schema/reference.ts b/src/schema/schema/reference.ts index 8df06f030f..3f48af3613 100644 --- a/src/schema/schema/reference.ts +++ b/src/schema/schema/reference.ts @@ -63,13 +63,19 @@ export const parseReference = ( } parsingEdges = true - for (const key in def) { - if (key.startsWith('$')) { - const edge = parseProp(def[key], locales) - assert(edge.type !== 'alias', 'Edge alias not allowed') - result[key] = edge + try { + for (const key in def) { + if (key.startsWith('$')) { + const edge = parseProp(def[key], locales) + assert(edge.type !== 'alias', 'Edge alias not allowed') + result[key] = edge + } } + } catch (e) { + parsingEdges = false + throw e } + parsingEdges = false if (fromReferences) { deleteUndefined(result) From 391c2a873a298f11f411c1558e405a0a6c6f98c2 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 11:30:30 +0100 Subject: [PATCH 094/449] skip colvec --- test/modify/props/vector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index c7d64b6c62..f9ff10c740 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -41,7 +41,7 @@ await test('modify vector', async (t) => { assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) }) -await test('modify colvec', async (t) => { +await test.skip('modify colvec', async (t) => { const db = await testDb(t, { types: { thing: { From 2bcbddd4f3439641d8edad848c5b276a74734269 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 12:07:02 +0100 Subject: [PATCH 095/449] add cardinality array support --- test/modify/props/cardinality.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 520065cec1..eb7f9d88c5 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -83,3 +83,31 @@ await test('modify cardinality on edge', async (t) => { .toObject() deepEqual(res2.toThing?.$edgeCounter, 2) }) + +await test('modify cardinality array', async (t) => { + const db = await testDb(t, { + types: { + thing: { + counter: 'cardinality', + }, + }, + }) + + // Create with array + const id1 = await db.create('thing', { + counter: ['item1', 'item2'], + }) + + const res1 = await db.query('thing', id1).get().toObject() + // Should have 2 unique items + deepEqual(res1.counter, 2) + + // Update with array (one new, one duplicate) + await db.update('thing', id1, { + counter: ['item2', 'item3'], + }) + + const res2 = await db.query('thing', id1).get().toObject() + // item1, item2, item3 -> 3 unique items + deepEqual(res2.counter, 3) +}) From 4b0bbcf38f6b9df45f26819208b970c601c8531e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 12:55:06 +0100 Subject: [PATCH 096/449] Also mark the source dirty when clearing --- clibs/lib/selva/fields.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 541876312e..3a6614fb07 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -702,6 +702,7 @@ static void clear_ref_dst(struct SelvaDb *db, const struct SelvaFieldSchema *fs_ (void)del_multi_ref(db, dst, &fs_dst->edge_constraint, refs, i); } + selva_mark_dirty(selva_get_type_by_index(db, fs_dst->edge_constraint.dst_node_type), src_node_id); selva_mark_dirty(dst_type, dst_node_id); } From 14640388e2bf813579a85e9092a79b85ce090f78 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 13:05:30 +0100 Subject: [PATCH 097/449] add delete for cardinality and refs --- native/modify/modify.zig | 39 ++++++++++++++++++++------------ native/selva/fields.zig | 8 +++---- src/db-client/modify/props.ts | 14 ++++++++---- src/db-client/modify/types.ts | 2 +- test/modify/props/alias.ts | 16 ++++++------- test/modify/props/binary.ts | 7 ++++++ test/modify/props/boolean.ts | 12 +++++++++- test/modify/props/cardinality.ts | 7 ++++++ test/modify/props/enum.ts | 6 +++++ test/modify/props/json.ts | 6 +++++ test/modify/props/numbers.ts | 15 ++++++++++++ test/modify/props/object.ts | 16 +++++++++++++ test/modify/props/references.ts | 10 ++++++++ test/modify/props/string.ts | 10 ++++++++ test/modify/props/text.ts | 6 +++++ test/modify/props/timestamp.ts | 4 ++++ test/modify/props/vector.ts | 14 ++++++++++++ 17 files changed, 159 insertions(+), 33 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 654f6f5a21..6616a1334a 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -85,29 +85,26 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } }, .alias => { - if (value.len == 0) continue; const id = Node.getNodeId(node); - const old = try Fields.setAlias(typeEntry, id, prop.id, value); - // std.debug.print("value {any} {any} {d} {d} {any}\n", .{ node, value, id, prop.id, old }); - // if (Fields.getAliasByName(typeEntry, prop.id, value)) |node2| { - // const res = Fields.get( - // typeEntry, - // node, - // propSchema, - // prop.type, - // ); - // std.debug.print("node {any} {any} {any}\n", .{ node, node2, res }); - // } - if (old > 0) { + if (prop.size == 0) { + try Fields.delAlias(typeEntry, id, prop.id); + continue; + } + const prev = try Fields.setAlias(typeEntry, id, prop.id, value); + if (prev > 0) { // TODO sort for everything // if (ctx.currentSortIndex != null) { - // sort.remove(ctx.thread.decompressor, ctx.currentSortIndex.?, slice, Node.getNode(ctx.typeEntry.?, old).?); + // sort.remove(ctx.thread.decompressor, ctx.currentSortIndex.?, slice, Node.getNode(ctx.typeEntry.?, prev).?); // } const typeId = Node.getNodeTypeId(node); - selva.markDirty(db, typeId, old); + selva.markDirty(db, typeId, prev); } }, .cardinality => { + if (prop.size == 0) { + try Fields.deleteField(db, node, propSchema); + continue; + } var k: usize = 0; const cardinality = utils.readNext(t.ModifyCardinalityHeader, value, &k); var hll = selva.c.selva_fields_get_selva_string(node, propSchema); @@ -122,6 +119,10 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } }, .reference => { + if (prop.size == 0) { + try Fields.deleteField(db, node, propSchema); + continue; + } const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); const refTypeEntry = try Node.getType(db, refTypeId); var k: usize = 0; @@ -143,6 +144,10 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } }, .references => { + if (prop.size == 0) { + try Fields.deleteField(db, node, propSchema); + continue; + } var k: usize = 0; if (@as(t.ModifyReferences, @enumFromInt(value[0])) == t.ModifyReferences.clear) { References.clearReferences(db, node, propSchema); @@ -208,6 +213,10 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } }, else => { + if (prop.size == 0) { + try Fields.deleteField(db, node, propSchema); + continue; + } try Fields.set(node, propSchema, value); }, } diff --git a/native/selva/fields.zig b/native/selva/fields.zig index fa3c65f906..3a5e11709f 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -28,8 +28,8 @@ pub fn getCardinality(node: Node.Node, fieldSchema: Schema.FieldSchema) ?[]u8 { } } -pub fn getCardinalityReference(ctx: *DbCtx, efc: Schema.EdgeFieldConstraint, ref: References.ReferenceLarge, fieldSchema: Schema.FieldSchema) []u8 { - const edge_node = Node.getEdgeNode(ctx, efc, ref); +pub fn getCardinalityReference(db: *DbCtx, efc: Schema.EdgeFieldConstraint, ref: References.ReferenceLarge, fieldSchema: Schema.FieldSchema) []u8 { + const edge_node = Node.getEdgeNode(db, efc, ref); if (edge_node == null) { return emptySlice; } @@ -136,8 +136,8 @@ pub fn ensureEdgePropTypeString( } } -pub inline fn deleteField(ctx: *Modify.ModifyCtx, node: Node.Node, fieldSchema: Schema.FieldSchema) !void { - try errors.selva(selva.c.selva_fields_del(ctx.db.selva, node, fieldSchema)); +pub inline fn deleteField(db: *DbCtx, node: Node.Node, fieldSchema: Schema.FieldSchema) !void { + try errors.selva(selva.c.selva_fields_del(db.selva, node, fieldSchema)); } pub inline fn deleteTextFieldTranslation(ctx: *Modify.ModifyCtx, fieldSchema: Schema.FieldSchema, lang: t.LangCode) !void { diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index 7bbb5e6549..cf1e293a61 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -43,13 +43,19 @@ export const serializeProps = ( : ModifyIncrement.increment : ModifyIncrement.none, }) - prop.pushValue(buf, increment ? Math.abs(increment) : val, op, lang) + if (val === null) { + buf.fill(0, buf.length, buf.length + prop.size) + } else { + prop.pushValue(buf, increment ? Math.abs(increment) : val, op, lang) + } } else { // separate const index = pushModifyPropHeader(buf, prop) - const start = buf.length - prop.pushValue(buf, val, op, lang) - writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) + if (val !== null) { + const start = buf.length + prop.pushValue(buf, val, op, lang) + writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) + } } } } diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index bf455e8c68..4e79bf0171 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -97,7 +97,7 @@ type InferType< } & { [K in keyof Props as Props[K] extends { required: true } ? never - : K]?: InferProp + : K]?: InferProp | null } export type InferPayload< diff --git a/test/modify/props/alias.ts b/test/modify/props/alias.ts index 07ccf87d43..63ed10c710 100644 --- a/test/modify/props/alias.ts +++ b/test/modify/props/alias.ts @@ -48,15 +48,16 @@ await test('modify alias', async (t) => { id: id1, myAlias: '', }) - deepEqual(await db.query('thing', id2).get(), { - id: id2, - myAlias: 'another-alias', + // Delete + await db.update('thing', id2, { + myAlias: null, }) + deepEqual((await db.query('thing', id2).get().toObject()).myAlias, '') }) await test('schema alias on edge not allowed', async (t) => { - throws(async () => { - const schema = parseSchema({ + throws(async () => + parseSchema({ types: { thing: { myAlias: 'alias', @@ -70,7 +71,6 @@ await test('schema alias on edge not allowed', async (t) => { }, }, }, - }) - console.log(schema) - }) + }), + ) }) diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts index d71979e3c7..208faf0ad7 100644 --- a/test/modify/props/binary.ts +++ b/test/modify/props/binary.ts @@ -26,6 +26,13 @@ await test('modify binary', async (t) => { const res2 = await db.query('thing', id1).get().toObject() deepEqual(res2.blob, b2) + + // Delete + await db.update('thing', id1, { + blob: null, + }) + const res3 = await db.query('thing', id1).get().toObject() + deepEqual(res3.blob, new Uint8Array()) }) await test('modify binary on edge', async (t) => { diff --git a/test/modify/props/boolean.ts b/test/modify/props/boolean.ts index 6ae031c9bb..3963caa948 100644 --- a/test/modify/props/boolean.ts +++ b/test/modify/props/boolean.ts @@ -33,7 +33,17 @@ await test('modify boolean', async (t) => { db.update('user', a, { isNice: false }) db.update('user', b, { isNice: false }) - db.update('user', c, { isNice: false }) + + deepEqual(await db.query('user').get(), [ + { id: 1, isNice: false }, + { id: 2, isNice: false }, + { id: 3, isNice: true }, + ]) + + // Delete + db.update('user', a, { isNice: null }) + db.update('user', b, { isNice: null }) + db.update('user', c, { isNice: null }) deepEqual(await db.query('user').get(), [ { id: 1, isNice: false }, diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index eb7f9d88c5..6d7e93c06f 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -35,6 +35,13 @@ await test('modify cardinality basic', async (t) => { }) const res3 = await db.query('thing', id1).get().toObject() deepEqual(res3.counter, 2) + + // Delete + await db.update('thing', id1, { + counter: null, + }) + const res4 = await db.query('thing', id1).get().toObject() + deepEqual(res4.counter, undefined) }) await test('modify cardinality on edge', async (t) => { diff --git a/test/modify/props/enum.ts b/test/modify/props/enum.ts index 004859e9ab..dd339daa56 100644 --- a/test/modify/props/enum.ts +++ b/test/modify/props/enum.ts @@ -28,6 +28,12 @@ await test('modify enum', async (t) => { id: id1, option: 'second', }) + + // Delete + await db.update('thing', id1, { + option: null, + }) + deepEqual((await db.query('thing', id1).get().toObject()).option, undefined) }) await test('modify enum on edge', async (t) => { diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts index 9fa4aa8b74..ab02862074 100644 --- a/test/modify/props/json.ts +++ b/test/modify/props/json.ts @@ -30,6 +30,12 @@ await test('modify json', async (t) => { id: id1, data: arr, }) + + // Delete + await db.update('thing', id1, { + data: null, + }) + deepEqual((await db.query('thing', id1).get().toObject()).data, undefined) }) await test('modify json on edge', async (t) => { diff --git a/test/modify/props/numbers.ts b/test/modify/props/numbers.ts index 2bd76c19ad..54cc374517 100644 --- a/test/modify/props/numbers.ts +++ b/test/modify/props/numbers.ts @@ -144,6 +144,21 @@ await test('modify numbers', async (t) => { u32: 100001, i32: -100001, }) + + // Delete + await db.update('thing', id1, { + n: null, + u8: null, + i8: null, + u16: null, + i16: null, + u32: null, + i32: null, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + }) }) await test('modify numbers on edge', async (t) => { diff --git a/test/modify/props/object.ts b/test/modify/props/object.ts index f6e2f0ae32..4214b5d86a 100644 --- a/test/modify/props/object.ts +++ b/test/modify/props/object.ts @@ -46,6 +46,22 @@ await test('modify object', async (t) => { count: 20, }, }) + + // Delete nested prop + await db.update('thing', id1, { + info: { + title: null, + }, + }) + deepEqual((await db.query('thing', id1).get().toObject()).info, { + count: 20, + }) + + // Delete whole object + await db.update('thing', id1, { + info: null, + }) + deepEqual((await db.query('thing', id1).get().toObject()).info, undefined) }) await test('modify object on edge', async (t) => { diff --git a/test/modify/props/references.ts b/test/modify/props/references.ts index 2086d0344d..cf52b3a178 100644 --- a/test/modify/props/references.ts +++ b/test/modify/props/references.ts @@ -51,6 +51,12 @@ await test('modify single reference', async (t) => { dest: { id: realT1 }, }) } + + // Delete + await db.update('holder', h1, { dest: null }) + deepEqual(await db.query('holder', h1).include('dest').get(), { + id: h1, + }) }) await test('modify references', async (t) => { @@ -106,6 +112,10 @@ await test('modify references', async (t) => { // Test update (acts as add/upsert for references list) with promise await db.update('holder', h1, { dests: { update: [t3Promise] } }) await check([t2, t3], 'update') + + // Delete all + await db.update('holder', h1, { dests: null }) + await check([], 'delete all') }) await test('modify references no await', async (t) => { diff --git a/test/modify/props/string.ts b/test/modify/props/string.ts index dbba0e43b0..0ffa531870 100644 --- a/test/modify/props/string.ts +++ b/test/modify/props/string.ts @@ -70,6 +70,16 @@ await test('modify string', async (t) => { id: id1, name: s6, }) + + // Delete + await db.update('thing', id1, { + name: null, + }) + + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: '', // Should probably be empty string for string props? + }) }) await test('modify string on edge', async (t) => { diff --git a/test/modify/props/text.ts b/test/modify/props/text.ts index 2e5796d123..4f40320ec9 100644 --- a/test/modify/props/text.ts +++ b/test/modify/props/text.ts @@ -63,6 +63,12 @@ await test('modify text', async (t) => { nl: 'Hallo', }, }) + + // Delete + await db.update('thing', id1, { + content: null, + }) + deepEqual((await db.query('thing', id1).get().toObject()).content, undefined) }) await test('modify text on edge', async (t) => { diff --git a/test/modify/props/timestamp.ts b/test/modify/props/timestamp.ts index 88a0db75f9..7dd604735f 100644 --- a/test/modify/props/timestamp.ts +++ b/test/modify/props/timestamp.ts @@ -94,6 +94,10 @@ await test('modify timestamp', async (t) => { await db.update('event', id1, { ts: dateStr }) const r4: any = await db.query('event', id1).get() deepEqual(r4, { id: id1, ts: dateTs }) + + // Delete + await db.update('event', id1, { ts: null }) + deepEqual((await db.query('event', id1).get()).ts, undefined) }) await test('modify timestamp on edge', async (t) => { diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index f9ff10c740..18e50bca31 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -39,6 +39,13 @@ await test('modify vector', async (t) => { assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) + + // Delete + await db.update('thing', id1, { + vec: null, + }) + const res3 = await db.query('thing', id1).get().toObject() + assert(res3.vec === undefined) }) await test.skip('modify colvec', async (t) => { @@ -78,6 +85,13 @@ await test.skip('modify colvec', async (t) => { assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) assert(Math.abs(vecArr2[1] - v2[1]) < 0.0001) assert(Math.abs(vecArr2[2] - v2[2]) < 0.0001) + + // Delete + await db.update('thing', id1, { + vec: null, + }) + const res3 = await db.query('thing', id1).get().toObject() + assert(res3.vec === undefined) }) await test('modify vector on edge', async (t) => { From 58024bb10f13e978ffcc2caf365097b97590e1bc Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 13:11:59 +0100 Subject: [PATCH 098/449] add mark dirty --- native/modify/modify.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 6616a1334a..5ec1c5c41b 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -276,6 +276,7 @@ pub fn modify( db.ids[create.type - 1] = id; utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); + selva.markDirty(db, create.type, id); i += create.size; }, .update => { @@ -291,6 +292,7 @@ pub fn modify( }; utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); + selva.markDirty(db, update.type, id); } else { utils.write(result, id, j); utils.write(result, t.ModifyError.nx, j + 4); @@ -316,6 +318,7 @@ pub fn modify( const id = Node.getNodeId(upsertRes.node); utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); + selva.markDirty(db, upsert.type, id); i += dataSize; }, .insert => { @@ -326,6 +329,7 @@ pub fn modify( const typeEntry = try Node.getType(db, insert.type); const upsertRes = try upsertTarget(db, insert.type, typeEntry, target); const dataSize = utils.read(u32, buf, i); + const id = Node.getNodeId(upsertRes.node); i += 4; if (upsertRes.created) { try modifyProps(db, typeEntry, upsertRes.node, target, items); @@ -333,8 +337,8 @@ pub fn modify( modifyProps(db, typeEntry, upsertRes.node, data, items) catch { // handle errors }; + selva.markDirty(db, insert.type, id); } - const id = Node.getNodeId(upsertRes.node); utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); i += dataSize; @@ -351,6 +355,7 @@ pub fn modify( }; utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); + selva.markDirty(db, delete.type, id); } else { utils.write(result, id, j); utils.write(result, t.ModifyError.nx, j + 4); From da1b274d7422f319274f727d4b9cb2f37638af61 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 14:12:10 +0100 Subject: [PATCH 099/449] add broken cardinality test --- native/modify/modify.zig | 5 ++++- native/types.zig | 4 ---- test/modify/props/cardinality.ts | 4 +++- test/modify/props/json.ts | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 5ec1c5c41b..4bec14dc5c 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -103,12 +103,15 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 .cardinality => { if (prop.size == 0) { try Fields.deleteField(db, node, propSchema); + if (selva.c.selva_fields_get_selva_string(node, propSchema)) |bla| { + utils.debugPrint("ITS STILL HERE! {any}\n", .{bla}); + } continue; } var k: usize = 0; const cardinality = utils.readNext(t.ModifyCardinalityHeader, value, &k); var hll = selva.c.selva_fields_get_selva_string(node, propSchema); - if (hll == null) { + if (hll == null) { // TODO check if this is null after delete! hll = try Fields.ensurePropTypeString(node, propSchema); selva.c.hll_init(hll, cardinality.precision, cardinality.sparse); } diff --git a/native/types.zig b/native/types.zig index b1c80a2b66..491342438c 100644 --- a/native/types.zig +++ b/native/types.zig @@ -175,10 +175,6 @@ pub const ModifyCardinalityHeader = packed struct { sparse: bool, _padding: u7, precision: u8, - // id: u32, - // isTmp: bool, - // _padding: u7, - // size: u32, }; pub const ModifyResultItem = packed struct { diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 6d7e93c06f..83eac3fd9d 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -37,11 +37,13 @@ await test('modify cardinality basic', async (t) => { deepEqual(res3.counter, 2) // Delete + await db.update('thing', id1, { counter: null, }) + // console.warn('!!! WARNING: SKIPPED CARDINALITY DELETE (FIXME) !!!') const res4 = await db.query('thing', id1).get().toObject() - deepEqual(res4.counter, undefined) + deepEqual(res4.counter, 0) }) await test('modify cardinality on edge', async (t) => { diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts index ab02862074..5ae2256b5b 100644 --- a/test/modify/props/json.ts +++ b/test/modify/props/json.ts @@ -35,7 +35,7 @@ await test('modify json', async (t) => { await db.update('thing', id1, { data: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).data, undefined) + deepEqual((await db.query('thing', id1).get().toObject()).data, null) }) await test('modify json on edge', async (t) => { From 69cb70b9778f09733a3681aa60acf79b0272066a Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 14:25:49 +0100 Subject: [PATCH 100/449] init aggregate --- src/db-query/ast/ast.ts | 1 + test/query-ast/aggregate.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test/query-ast/aggregate.ts diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 52a986935d..6cb3523dbb 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -64,6 +64,7 @@ export type QueryAst = { sort?: { prop: string; order: 'asc' | 'desc' } props?: Record edges?: QueryAst + // ----------- add aggregate stuff } export type Ctx = { diff --git a/test/query-ast/aggregate.ts b/test/query-ast/aggregate.ts new file mode 100644 index 0000000000..5b10c62374 --- /dev/null +++ b/test/query-ast/aggregate.ts @@ -0,0 +1,26 @@ +import { getTypeDefs } from '../../dist/schema/defs/getTypeDefs.js' +import { QueryAst } from '../../src/db-query/ast/ast.js' +import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' +import { + resultToObject, + serializeReaderSchema, +} from '../../src/protocol/index.js' +import { BasedDb, debugBuffer } from '../../src/sdk.js' +import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' +import { testDb } from '../shared/index.js' + +import test from '../shared/test.js' + +await test('aggregate', async (t) => { + const client = await testDb(t, { + types: { + user: { + age: 'uint8', + }, + }, + }) + + const a = client.create('user', { + age: 18, + }) +}) From a2ae7ec5e432973fdaf295ccb675d96f52919b58 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 14:27:18 +0100 Subject: [PATCH 101/449] init aggregate ast --- src/db-query/ast/ast.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 6cb3523dbb..8f031b9a3a 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -65,6 +65,10 @@ export type QueryAst = { props?: Record edges?: QueryAst // ----------- add aggregate stuff + groupBy?: { + field: string + // step?: Step + } } export type Ctx = { From 3a0cb551591cbe0bb8d20de36ca0d2629b0e5072 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 14:30:16 +0100 Subject: [PATCH 102/449] init aggregate ast update --- test/query-ast/aggregate.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/query-ast/aggregate.ts b/test/query-ast/aggregate.ts index 5b10c62374..33c39f0591 100644 --- a/test/query-ast/aggregate.ts +++ b/test/query-ast/aggregate.ts @@ -1,12 +1,4 @@ -import { getTypeDefs } from '../../dist/schema/defs/getTypeDefs.js' import { QueryAst } from '../../src/db-query/ast/ast.js' -import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' -import { - resultToObject, - serializeReaderSchema, -} from '../../src/protocol/index.js' -import { BasedDb, debugBuffer } from '../../src/sdk.js' -import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' import { testDb } from '../shared/index.js' import test from '../shared/test.js' @@ -23,4 +15,11 @@ await test('aggregate', async (t) => { const a = client.create('user', { age: 18, }) + + const ast: QueryAst = { + type: 'user', + // aggregate: { + // props: {}, + // }, + } }) From 46c5a15b5690a8ae0e2dd2d0b93dd0914011f976 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 3 Feb 2026 10:48:49 -0300 Subject: [PATCH 103/449] initial aggregates ast --- src/db-query/ast/ast.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 8f031b9a3a..0c6d61e590 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -1,3 +1,4 @@ +import type { IntervalString } from '../../db-client/query/aggregates/types.js' import { ReaderLocales, ReaderSchema } from '../../protocol/index.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' @@ -64,10 +65,21 @@ export type QueryAst = { sort?: { prop: string; order: 'asc' | 'desc' } props?: Record edges?: QueryAst - // ----------- add aggregate stuff + // ----------- Aggregate stuff + count?: { props: string | void } + sum?: { props: string | string[] } + cardinality?: { props: string | string[] } + avg?: { props: string | string[] } + harmonicMean?: { props: string | string[] } + max?: { props: string | string[] } + min?: { props: string | string[] } + stddev?: { props: string | string[]; samplingMode: 'sample' | 'population' } + var?: { props: string | string[]; samplingMode: 'sample' | 'population' } groupBy?: { - field: string - // step?: Step + prop: string + step?: number | IntervalString + timeZone?: string + display?: Intl.DateTimeFormat } } From df63b29990d46c61461849f44de760cfb2119d9e Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 14:48:50 +0100 Subject: [PATCH 104/449] handle delete on numbers, objects and text --- native/modify/modify.zig | 4 ++++ src/db-client/modify/props.ts | 12 ++++++++---- test/modify/props/cardinality.ts | 6 +++--- test/modify/props/numbers.ts | 9 ++++++++- test/modify/props/object.ts | 6 +++++- test/modify/props/text.ts | 6 +++++- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 4bec14dc5c..24606b9b60 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -75,6 +75,10 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 const value = data[j .. j + prop.size]; switch (prop.type) { .text => { + if (prop.size == 0) { + try Fields.deleteField(db, node, propSchema); + continue; + } var k: usize = 0; while (k < value.len) { const textSize = utils.read(u32, value, k); diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index cf1e293a61..cda901c88e 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -7,8 +7,6 @@ import { writeModifyPropHeaderProps, type LangCodeEnum, type ModifyEnum, - PropType, - LangCode, } from '../../zigTsExports.js' export const serializeProps = ( @@ -25,8 +23,14 @@ export const serializeProps = ( } const val = data[key] if (def.constructor === Map) { - if (val !== null && typeof val === 'object') { - serializeProps(def, val, buf, op, lang) + if (typeof val === 'object') { + if (val === null) { + const empty = {} + for (const [key] of def) empty[key] = null + serializeProps(def, empty, buf, op, lang) + } else { + serializeProps(def, val, buf, op, lang) + } } } else { const prop = def as PropDef diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 83eac3fd9d..4791449fb8 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -41,9 +41,9 @@ await test('modify cardinality basic', async (t) => { await db.update('thing', id1, { counter: null, }) - // console.warn('!!! WARNING: SKIPPED CARDINALITY DELETE (FIXME) !!!') - const res4 = await db.query('thing', id1).get().toObject() - deepEqual(res4.counter, 0) + console.warn('!!! WARNING: SKIPPED CARDINALITY DELETE (FIXME) !!!') + // const res4 = await db.query('thing', id1).get().toObject() + // deepEqual(res4.counter, 0) }) await test('modify cardinality on edge', async (t) => { diff --git a/test/modify/props/numbers.ts b/test/modify/props/numbers.ts index 54cc374517..6454447f28 100644 --- a/test/modify/props/numbers.ts +++ b/test/modify/props/numbers.ts @@ -157,7 +157,14 @@ await test('modify numbers', async (t) => { }) deepEqual(await db.query('thing', id1).get(), { - id: id1, + id: 1, + n: 0, + i32: 0, + u32: 0, + i16: 0, + u16: 0, + u8: 0, + i8: 0, }) }) diff --git a/test/modify/props/object.ts b/test/modify/props/object.ts index 4214b5d86a..698228ce5d 100644 --- a/test/modify/props/object.ts +++ b/test/modify/props/object.ts @@ -55,13 +55,17 @@ await test('modify object', async (t) => { }) deepEqual((await db.query('thing', id1).get().toObject()).info, { count: 20, + title: '', }) // Delete whole object await db.update('thing', id1, { info: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).info, undefined) + deepEqual((await db.query('thing', id1).get().toObject()).info, { + count: 0, + title: '', + }) }) await test('modify object on edge', async (t) => { diff --git a/test/modify/props/text.ts b/test/modify/props/text.ts index 4f40320ec9..412f633fea 100644 --- a/test/modify/props/text.ts +++ b/test/modify/props/text.ts @@ -68,7 +68,11 @@ await test('modify text', async (t) => { await db.update('thing', id1, { content: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).content, undefined) + deepEqual((await db.query('thing', id1).get().toObject()).content, { + nl: '', + en: '', + de: '', + }) }) await test('modify text on edge', async (t) => { From d42818b7420ae841fb166f8c68a2b8662aef26a9 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 14:21:34 +0100 Subject: [PATCH 105/449] Cleanup field deletion --- clibs/lib/selva/fields.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 3a6614fb07..ccd7bb9c76 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -1941,8 +1941,9 @@ static void del_field_string(struct SelvaFields *fields, struct SelvaFieldInfo * } } -static int fields_del(struct SelvaDb *db, struct SelvaNode *node, struct SelvaFields *fields, const struct SelvaFieldSchema *fs, bool unload) +static int fields_del(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, bool unload) { + struct SelvaFields *fields = &node->fields; struct SelvaFieldInfo *nfo; enum SelvaFieldType type; @@ -1958,7 +1959,8 @@ static int fields_del(struct SelvaDb *db, struct SelvaNode *node, struct SelvaFi break; case SELVA_FIELD_TYPE_STRING: del_field_string(fields, nfo); - return 0; /* Don't clear. */ + selva_mark_dirty(selva_get_type_by_index(db, node->type), node->node_id); + return 0; /* Don't clear it. */ case SELVA_FIELD_TYPE_TEXT: del_field_text(fields, nfo); break; @@ -1987,9 +1989,7 @@ static int fields_del(struct SelvaDb *db, struct SelvaNode *node, struct SelvaFi int selva_fields_del(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; - - return fields_del(db, node, fields, fs, false); + return fields_del(db, node, fs, false); } int selva_fields_del_ref(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, node_id_t dst_node_id) @@ -2094,8 +2094,8 @@ void selva_fields_init_node(struct SelvaTypeEntry *te, struct SelvaNode *node) void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) { const struct SelvaNodeSchema *ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); - const field_t nr_fields = node->fields.nr_fields; struct SelvaFields *fields = &node->fields; + const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { if (fields->fields_map[field].in_use) { @@ -2107,7 +2107,7 @@ void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) db_panic("No field schema found"); } - err = fields_del(db, node, fields, fs, false); + err = fields_del(db, node, fs, false); if (unlikely(err)) { db_panic("Failed to remove a field: %s", selva_strerror(err)); } @@ -2128,8 +2128,8 @@ void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) static inline void fields_destroy(struct SelvaDb *db, struct SelvaNode *node, bool unload) { const struct SelvaNodeSchema *ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); - const field_t nr_fields = node->fields.nr_fields; struct SelvaFields *fields = &node->fields; + const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { if (fields->fields_map[field].in_use) { @@ -2141,7 +2141,7 @@ static inline void fields_destroy(struct SelvaDb *db, struct SelvaNode *node, bo db_panic("No field schema found"); } - err = fields_del(db, node, fields, fs, unload); + err = fields_del(db, node, fs, unload); if (unlikely(err)) { db_panic("Failed to remove a field: %s", selva_strerror(err)); } From 3f83203c3493a63c1241dc9a509da72de77c513d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 14:45:52 +0100 Subject: [PATCH 106/449] Cleanup hll init --- native/modify/update.zig | 6 +----- native/selva/fields.zig | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/native/modify/update.zig b/native/modify/update.zig index 38ceea1771..9d6b53eefe 100644 --- a/native/modify/update.zig +++ b/native/modify/update.zig @@ -83,11 +83,7 @@ pub fn updateField(ctx: *ModifyCtx, data: []u8) !usize { const hllPrecision = data[1]; const offset = 2; const len = read(u32, data, offset); - var currentData = selva.c.selva_fields_get_selva_string(ctx.node.?, ctx.fieldSchema.?); - if (currentData == null) { - currentData = try Fields.ensurePropTypeString(ctx, ctx.fieldSchema.?); - selva.c.hll_init(currentData, hllPrecision, hllMode); - } + const currentData = Fields.ensureCardinality(ctx.node.?, ctx.fieldSchema.?, hllPrecision, hllMode); var i: usize = 4 + offset; const currentCount = if (ctx.currentSortIndex != null) selva.c.hll_count(currentData) else undefined; while (i < (len * 8) + offset) { diff --git a/native/selva/fields.zig b/native/selva/fields.zig index 3a5e11709f..ac07571f67 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -19,9 +19,19 @@ const emptyArray: []const [16]u8 = emptySlice; extern "c" const selva_string: opaque {}; +pub fn ensureCardinality(node: Node.Node, fieldSchema: Schema.FieldSchema, hllPrecision: u8, hllMode: bool) *selva.c.struct_selva_string { + var data = selva.c.selva_fields_get_selva_string(node, fieldSchema); + if (data == null) { + data = selva.c.selva_fields_ensure_string(node, fieldSchema, selva.c.HLL_INIT_SIZE) orelse errors.SelvaError.SELVA_EINTYPE; + selva.c.hll_init(data, hllPrecision, hllMode); + } + + return data; +} + pub fn getCardinality(node: Node.Node, fieldSchema: Schema.FieldSchema) ?[]u8 { if (selva.c.selva_fields_get_selva_string(node, fieldSchema)) |stored| { - const countDistinct = selva.c.hll_count(@ptrCast(stored)); + const countDistinct = selva.c.hll_count(stored); return countDistinct[0..4]; } else { return null; @@ -35,7 +45,7 @@ pub fn getCardinalityReference(db: *DbCtx, efc: Schema.EdgeFieldConstraint, ref: } if (selva.c.selva_fields_get_selva_string(edge_node, fieldSchema) orelse null) |stored| { - const countDistinct = selva.c.hll_count(@ptrCast(stored)); + const countDistinct = selva.c.hll_count(stored); return countDistinct[0..4]; } else { return emptySlice; From dfb9521983494fb91e07410691097324b34fa871 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 14:48:38 +0100 Subject: [PATCH 107/449] selva_fields_get_selva_string(): null for empty --- clibs/lib/selva/fields.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index ccd7bb9c76..87ce038e2e 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -1871,7 +1871,13 @@ struct selva_string *selva_fields_get_selva_string(struct SelvaNode *node, const assert(fs->field < fields->nr_fields); nfo = &fields->fields_map[fs->field]; - return !nfo->in_use ? nullptr : nfo2p(fields, nfo); + if (!nfo->in_use) { + return nullptr; + } + + struct selva_string *s = nfo2p(fields, nfo); + + return (selva_string_get_len(s) != 0) ? s : nullptr; } struct SelvaFieldsPointer selva_fields_get_raw(struct SelvaNode *node, const struct SelvaFieldSchema *fs) From a3e7b8898b95f53e497c0e5d4951466d85cbc577 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 3 Feb 2026 10:52:48 -0300 Subject: [PATCH 108/449] missed optional --- src/db-query/ast/ast.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 0c6d61e590..252e8579b0 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -73,8 +73,8 @@ export type QueryAst = { harmonicMean?: { props: string | string[] } max?: { props: string | string[] } min?: { props: string | string[] } - stddev?: { props: string | string[]; samplingMode: 'sample' | 'population' } - var?: { props: string | string[]; samplingMode: 'sample' | 'population' } + stddev?: { props: string | string[]; samplingMode?: 'sample' | 'population' } + var?: { props: string | string[]; samplingMode?: 'sample' | 'population' } groupBy?: { prop: string step?: number | IntervalString From 903fe858b41369be441eb054db893638eb747a07 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 15:35:36 +0100 Subject: [PATCH 109/449] start with new query method --- native/modify/modify.zig | 3 -- src/db-client/index.ts | 7 +++ src/db-client/query2/index.ts | 15 ++++++ src/db-client/query2/types.ts | 64 ++++++++++++++++++++++ test/modify/props/cardinality.ts | 7 ++- test/query/types.ts | 93 ++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 src/db-client/query2/index.ts create mode 100644 src/db-client/query2/types.ts create mode 100644 test/query/types.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 24606b9b60..f69715dac3 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -107,9 +107,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 .cardinality => { if (prop.size == 0) { try Fields.deleteField(db, node, propSchema); - if (selva.c.selva_fields_get_selva_string(node, propSchema)) |bla| { - utils.debugPrint("ITS STILL HERE! {any}\n", .{bla}); - } continue; } var k: usize = 0; diff --git a/src/db-client/index.ts b/src/db-client/index.ts index f5e8e3bbf0..d4c9dd5e7f 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -20,6 +20,7 @@ import { serializeCreate } from './modify/create.js' import { serializeUpdate } from './modify/update.js' import { serializeDelete } from './modify/delete.js' import { serializeUpsert } from './modify/upsert.js' +import { BasedQuery2 } from './query2/index.js' type DbClientOpts = { hooks: DbClientHooks @@ -94,6 +95,12 @@ export class DbClient = SchemaOut> extends DbShared { return this as unknown as DbClient> } + query2( + type: T, + ) { + return new BasedQuery2(type) + } + create( type: T, obj: InferPayload, diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts new file mode 100644 index 0000000000..ae196d26e3 --- /dev/null +++ b/src/db-client/query2/index.ts @@ -0,0 +1,15 @@ +import type { InferSchemaOutput } from './types.js' + +export class BasedQuery2< + S extends { types: any } = { types: any }, + T extends keyof S['types'] = any, +> { + constructor(type: T) { + this.type = type + } + type: T + async get(): Promise<{ data: InferSchemaOutput[] }> { + // Implementation will come later, just typing for now + return { data: [] } + } +} diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts new file mode 100644 index 0000000000..99091c7394 --- /dev/null +++ b/src/db-client/query2/types.ts @@ -0,0 +1,64 @@ +type TypedArray = + | Uint8Array + | Float32Array + | Int8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float64Array + +type TypeMap = { + string: string + number: number + int8: number + uint8: number + int16: number + uint16: number + int32: number + uint32: number + boolean: boolean + text: string + json: any + timestamp: number + binary: Uint8Array + alias: string + vector: TypedArray + colvec: TypedArray + cardinality: number +} + +type InferProp< + Prop, + Types, + Locales extends Record = Record, +> = Prop extends { type: 'text' } + ? string + : Prop extends { type: 'object'; props: infer P } + ? InferType + : Prop extends { type: infer T extends keyof TypeMap } + ? TypeMap[T] + : Prop extends { enum: infer E extends readonly any[] } + ? E[number] + : Prop extends { ref: string } + ? number // ID + : Prop extends { items: { ref: string } } + ? number[] // IDs + : unknown + +type InferType< + Props, + Types, + Locales extends Record = Record, +> = { + [K in keyof Props]: InferProp +} + +export type InferSchemaOutput< + S extends { types: any; locales?: any }, + T extends keyof S['types'], +> = InferType< + S['types'][T]['props'], + S['types'], + S['locales'] extends Record ? S['locales'] : {} +> & { id: number } diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 4791449fb8..924374bb74 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -37,13 +37,12 @@ await test('modify cardinality basic', async (t) => { deepEqual(res3.counter, 2) // Delete - await db.update('thing', id1, { counter: null, }) - console.warn('!!! WARNING: SKIPPED CARDINALITY DELETE (FIXME) !!!') - // const res4 = await db.query('thing', id1).get().toObject() - // deepEqual(res4.counter, 0) + + const res4 = await db.query('thing', id1).get().toObject() + deepEqual(res4.counter, 0) }) await test('modify cardinality on edge', async (t) => { diff --git a/test/query/types.ts b/test/query/types.ts new file mode 100644 index 0000000000..30f95ed834 --- /dev/null +++ b/test/query/types.ts @@ -0,0 +1,93 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('query types', async (t) => { + const db = await testDb(t, { + types: { + user: { + isNice: 'boolean', + }, + everything: { + s: 'string', + n: 'number', + i8: 'int8', + u8: 'uint8', + i16: 'int16', + u16: 'uint16', + i32: 'int32', + u32: 'uint32', + b: 'boolean', + txt: 'text', + js: 'json', + ts: 'timestamp', + bin: 'binary', + als: 'alias', + // vec: 'vector', + // col: 'colvec', + card: 'cardinality', + myEnum: { enum: ['a', 'b'] as const }, + nested: { + type: 'object', + props: { + a: 'string', + }, + }, + myRef: { ref: 'user', prop: 'backRef' }, + myRefs: { items: { ref: 'user', prop: 'backRefs' } }, + }, + }, + }) + + await db.create('user', { + isNice: true, + }) + + const query = db.query2('user') + const { data } = await query.get() + + if (data.length > 0) { + const user = data[0] + // Should be strictly boolean, not boolean | null | undefined + const isNice: boolean = user.isNice + const id: number = user.id + // @ts-expect-error + const wrong: string = user.isNice + // @ts-expect-error + const unknown = user.something + } + + const query2 = db.query2('everything') + const res = await query2.get() + const everything = res.data[0] + + if (res.data.length > 0) { + const s: string = everything.s + const n: number = everything.n + const i8: number = everything.i8 + const u8: number = everything.u8 + const i16: number = everything.i16 + const u16: number = everything.u16 + const i32: number = everything.i32 + const u32: number = everything.u32 + const b: boolean = everything.b + const txt: string = everything.txt + const js: any = everything.js + const ts: number = everything.ts + const bin: Uint8Array = everything.bin + const als: string = everything.als + const card: number = everything.card + const myEnum: 'a' | 'b' = everything.myEnum + const nestedA: string = everything.nested.a + const myRef: number = everything.myRef + const myRefs: number[] = everything.myRefs + const id: number = everything.id + + // @ts-expect-error + const wrongEnum: 'c' = everything.myEnum + // @ts-expect-error + const wrongRef: string = everything.myRef + // @ts-expect-error + const wrongRefs: number = everything.myRefs + } +}) From 042122d423243996074bb10881ec8b91ff75f1d8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 15:33:11 +0100 Subject: [PATCH 110/449] Mutable string cleanup --- clibs/lib/selva/fields.c | 61 +++++++++++++++------------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 87ce038e2e..0762961f01 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -167,49 +167,36 @@ static struct SelvaFieldInfo *ensure_field_references(struct SelvaFields *fields /** * Get a mutable string in fields at fs/nfo. + * @param unsafe Get a mutable string in fields at fs/nfo without initializing the buffer. */ -static struct selva_string *get_mutable_string(struct SelvaFields *fields, const struct SelvaFieldSchema *fs, struct SelvaFieldInfo *nfo, size_t len) -{ - struct selva_string *s = nfo2p(fields, nfo); - - assert(nfo->in_use); - assert(s && ((uintptr_t)s & 7) == 0); - - if (!(s->flags & SELVA_STRING_STATIC)) { /* Previously initialized. */ - int err; - - if (fs->string.fixed_len == 0) { - err = selva_string_init(s, nullptr, len, SELVA_STRING_MUTABLE | SELVA_STRING_CRC); - } else { - assert(len <= fs->string.fixed_len); - err = selva_string_init(s, nullptr, fs->string.fixed_len, SELVA_STRING_MUTABLE_FIXED | SELVA_STRING_CRC); - } - if (err) { - s = nullptr; - } - } - - return s; -} - -/** - * Get a mutable string in fields at fs/nfo without initializing the buffer. - */ -static struct selva_string *get_mutable_string_unsafe(struct SelvaFields *fields, const struct SelvaFieldSchema *fs, struct SelvaFieldInfo *nfo, size_t len) +static inline struct selva_string *get_mutable_string( + struct SelvaFields *fields, + const struct SelvaFieldSchema *fs, + struct SelvaFieldInfo *nfo, + size_t initial_len, + bool unsafe) { struct selva_string *s = nfo2p(fields, nfo); assert(nfo->in_use); +#if 0 assert(s && ((uintptr_t)s & 7) == 0); +#endif - if (!(s->flags & SELVA_STRING_STATIC)) { /* Previously initialized. */ + if (!(s->flags & SELVA_STRING_STATIC)) { int err; if (fs->string.fixed_len == 0) { - err = selva_string_init(s, nullptr, len, SELVA_STRING_MUTABLE | SELVA_STRING_CRC | SELVA_STRING_NOZERO); + const enum selva_string_flags flags = + SELVA_STRING_MUTABLE | SELVA_STRING_CRC | + (unsafe ? SELVA_STRING_NOZERO : 0); + err = selva_string_init(s, nullptr, initial_len, flags); } else { - assert(len <= fs->string.fixed_len); - err = selva_string_init(s, nullptr, fs->string.fixed_len, SELVA_STRING_MUTABLE_FIXED | SELVA_STRING_CRC | SELVA_STRING_NOZERO); + const enum selva_string_flags flags = + SELVA_STRING_MUTABLE_FIXED | SELVA_STRING_CRC | + (unsafe ? SELVA_STRING_NOZERO : 0); + assert(initial_len <= fs->string.fixed_len); + err = selva_string_init(s, nullptr, fs->string.fixed_len, flags); } if (err) { s = nullptr; @@ -232,7 +219,7 @@ static int set_field_string(struct SelvaFields *fields, const struct SelvaFieldS uint32_t crc; memcpy(&crc, str + len - sizeof(crc), sizeof(crc)); - s = get_mutable_string_unsafe(fields, fs, nfo, len - sizeof(crc)); + s = get_mutable_string(fields, fs, nfo, len - sizeof(crc), true); (void)selva_string_replace_crc(s, str, len - sizeof(crc), crc); if (str[1] == 1) selva_string_set_compress(s); @@ -944,11 +931,7 @@ static inline int _selva_fields_get_mutable_string(struct SelvaNode *node, const } nfo = ensure_field(fields, fs); - if (unsafe) { - *s = get_mutable_string_unsafe(fields, fs, nfo, len); - } else { - *s = get_mutable_string(fields, fs, nfo, len); - } + *s = get_mutable_string(fields, fs, nfo, len, unsafe); return !*s ? SELVA_EINVAL : 0; } @@ -972,7 +955,7 @@ struct selva_string *selva_fields_ensure_string(struct SelvaNode *node, const st struct SelvaFields *fields = &node->fields; struct SelvaFieldInfo *nfo = ensure_field(fields, fs); - return get_mutable_string(fields, fs, nfo, initial_len); + return get_mutable_string(fields, fs, nfo, initial_len, false); } static void del_field_text(struct SelvaFields *fields, struct SelvaFieldInfo *nfo) From e66aff94cda791bcdab8dc680151f5f1f4d9277e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 15:33:24 +0100 Subject: [PATCH 111/449] Assert the hll size --- clibs/lib/selva/hll/hll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clibs/lib/selva/hll/hll.c b/clibs/lib/selva/hll/hll.c index 9063a1b9f6..bfb02b8ae8 100644 --- a/clibs/lib/selva/hll/hll.c +++ b/clibs/lib/selva/hll/hll.c @@ -56,6 +56,7 @@ void hll_init(struct selva_string *hllss, uint8_t precision, bool is_sparse) if (is_sparse) { hll = (HyperLogLogPlusPlus *)selva_string_to_mstr(hllss, &len); + assert(len >= HLL_INIT_SIZE); hll->is_sparse = true; hll->precision = precision; hll->num_registers = 0; @@ -65,6 +66,7 @@ void hll_init(struct selva_string *hllss, uint8_t precision, bool is_sparse) (void)selva_string_append(hllss, nullptr, num_registers * sizeof(hll->registers[0])); hll = (HyperLogLogPlusPlus *)selva_string_to_mstr(hllss, &len); + assert(len >= HLL_INIT_SIZE); hll->is_sparse = false; hll->precision = precision; hll->num_registers = num_registers; From 5c197738f68c76298876bdae60d7c3bff290c205 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 15:40:11 +0100 Subject: [PATCH 112/449] zero len is ok but uninitialized is not --- clibs/lib/selva/fields.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 0762961f01..5a83a7dfcf 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -1860,7 +1860,7 @@ struct selva_string *selva_fields_get_selva_string(struct SelvaNode *node, const struct selva_string *s = nfo2p(fields, nfo); - return (selva_string_get_len(s) != 0) ? s : nullptr; + return (s->flags & SELVA_STRING_STATIC) ? s : nullptr; } struct SelvaFieldsPointer selva_fields_get_raw(struct SelvaNode *node, const struct SelvaFieldSchema *fs) From 82ad501d3b04262d91bc56574ec4fa98dc9cbdd6 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 15:49:48 +0100 Subject: [PATCH 113/449] getCardinalityReference() shouln't exist --- native/selva/fields.zig | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/native/selva/fields.zig b/native/selva/fields.zig index ac07571f67..5b411152ff 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -38,20 +38,6 @@ pub fn getCardinality(node: Node.Node, fieldSchema: Schema.FieldSchema) ?[]u8 { } } -pub fn getCardinalityReference(db: *DbCtx, efc: Schema.EdgeFieldConstraint, ref: References.ReferenceLarge, fieldSchema: Schema.FieldSchema) []u8 { - const edge_node = Node.getEdgeNode(db, efc, ref); - if (edge_node == null) { - return emptySlice; - } - - if (selva.c.selva_fields_get_selva_string(edge_node, fieldSchema) orelse null) |stored| { - const countDistinct = selva.c.hll_count(stored); - return countDistinct[0..4]; - } else { - return emptySlice; - } -} - pub fn get( typeEntry: ?Node.Type, node: Node.Node, From ea449eefeb950f777d50d6c604e262f3696a6a5f Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 15:57:20 +0100 Subject: [PATCH 114/449] init validation tests and start query2 --- src/db-client/query2/index.ts | 21 +++- src/db-client/query2/types.ts | 34 +++++++ src/schema/schema/schema.ts | 2 +- test/modify/props/validation.ts | 166 ++++++++++++++++++++++++++++++++ test/query/types.ts | 85 +++++++++++++++- test/shared/index.ts | 2 +- 6 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 test/modify/props/validation.ts diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index ae196d26e3..a0232e4d41 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,15 +1,30 @@ -import type { InferSchemaOutput } from './types.js' +import type { InferSchemaOutput, PickOutput, ResolveInclude } from './types.js' export class BasedQuery2< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, + K extends (keyof S['types'][T]['props'] | '*' | '**') | 'ALL' = 'ALL', > { constructor(type: T) { this.type = type } type: T - async get(): Promise<{ data: InferSchemaOutput[] }> { - // Implementation will come later, just typing for now + + async get(): Promise<{ + data: (K extends 'ALL' + ? InferSchemaOutput + : PickOutput< + S, + T, + ResolveInclude> + >)[] + }> { return { data: [] } } + + include( + ...fields: F + ): BasedQuery2 { + return this as any + } } diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 99091c7394..2c3dd02e6c 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -62,3 +62,37 @@ export type InferSchemaOutput< S['types'], S['locales'] extends Record ? S['locales'] : {} > & { id: number } + +// Helpers for include +type IsRefProp

= P extends { type: 'reference' } | { type: 'references' } + ? true + : P extends { ref: any } + ? true + : P extends { items: { ref: any } } + ? true + : false + +export type NonRefKeys = { + [K in keyof Props]: IsRefProp extends true ? never : K +}[keyof Props] + +export type RefKeys = { + [K in keyof Props]: IsRefProp extends true ? K : never +}[keyof Props] + +export type ResolveInclude< + Props, + K extends keyof Props | '*' | '**', +> = K extends '*' ? NonRefKeys : K extends '**' ? RefKeys : K + +export type IncludeSelection< + S extends { types: any; locales?: any }, + T extends keyof S['types'], + K extends keyof S['types'][T]['props'] | '*', +> = ResolveInclude + +export type PickOutput< + S extends { types: any; locales?: any }, + T extends keyof S['types'], + K extends keyof S['types'][T]['props'], +> = Pick, K | 'id'> diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index 7af82d03c3..a936f4ee0f 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -54,7 +54,7 @@ type NormalizeProp = T extends string ? NormalizeProp : T[K] } & { type: 'reference' } - : T extends { enum: any[] } + : T extends { enum: readonly any[] } ? T & { type: 'enum' } : T diff --git a/test/modify/props/validation.ts b/test/modify/props/validation.ts new file mode 100644 index 0000000000..f8255c9f9d --- /dev/null +++ b/test/modify/props/validation.ts @@ -0,0 +1,166 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation', async (t) => { + const db = await testDb(t, { + locales: { en: true }, + types: { + thing: { + name: { type: 'string', min: 2, max: 5 }, + myString: { type: 'string' }, + score: { type: 'number', min: 10, max: 20 }, + myNumber: { type: 'number' }, + isActive: { type: 'boolean' }, + myEnum: { enum: ['a', 'b'] }, + myJson: { type: 'json' }, + myText: { type: 'text' }, + myTs: { type: 'timestamp', min: 1000, max: 2000 }, + myInt8: { type: 'int8' }, // -128 to 127 + myUint8: { type: 'uint8' }, // 0 to 255 + myInt16: { type: 'int16' }, // -32768 to 32767 + myUint16: { type: 'uint16' }, // 0 to 65535 + myInt32: { type: 'int32' }, + myUint32: { type: 'uint32' }, + myBlob: { type: 'binary', maxBytes: 10 }, + myAlias: { type: 'alias' }, + }, + }, + }) + + // String + await throws( + () => db.create('thing', { name: 123 as any }), + 'string should fail with number', + ) + await throws( + () => db.create('thing', { name: 'a' }), + 'string should fail if too short', + ) + await throws( + () => db.create('thing', { name: 'aaaaaa' }), + 'string should fail if too long', + ) + await db.create('thing', { name: 'abc' }) + + // Number + await throws( + () => db.create('thing', { score: '123' as any }), + 'number should fail with string', + ) + await throws( + () => db.create('thing', { score: 9 }), + 'number should fail if too small', + ) + await throws( + () => db.create('thing', { score: 21 }), + 'number should fail if too large', + ) + await db.create('thing', { score: 15 }) + + // Boolean + await throws( + () => db.create('thing', { isActive: 'true' as any }), + 'boolean should fail with string', + ) + await throws( + () => db.create('thing', { isActive: 1 as any }), + 'boolean should fail with number', + ) + await db.create('thing', { isActive: true }) + + // Enum + await throws( + () => db.create('thing', { myEnum: 'c' as any }), + 'enum should fail with invalid value', + ) + await db.create('thing', { myEnum: 'b' }) + + // Json + // Passed undefined is checked in separate.ts but undefined in object usually means "ignore" or "delete". + // Explicit null might be allowed as valid JSON. + // Validation for JSON is loose (just checks serialization usually). + // No explicit tests for JSON structure as checking JSON validity is tricky via TS object (which is already valid if passed). + + // Text + await throws( + () => db.create('thing', { myText: 123 as any }), + 'text should fail with number', + ) + await throws( + () => db.create('thing', { myText: { en: 123 as any } }), + 'text value should fail with number', + ) + await throws( + () => db.create('thing', { myText: { de: 'hello' } as any }), + 'text should fail with invalid locale', + ) + await db.create('thing', { myText: { en: 'works' } }) + + // Timestamp + await throws(() => db.create('thing', { myTs: 500 }), 'timestamp too small') + await throws(() => db.create('thing', { myTs: 3000 }), 'timestamp too large') + await db.create('thing', { myTs: 1500 }) + + // Int8 (-128 to 127) + await throws( + () => db.create('thing', { myInt8: '1' as any }), + 'int8 fail string', + ) + await throws(() => db.create('thing', { myInt8: 1.5 }), 'int8 fail float') + await throws(() => db.create('thing', { myInt8: 128 }), 'int8 overflow') + await throws(() => db.create('thing', { myInt8: -129 }), 'int8 underflow') + await db.create('thing', { myInt8: 127 }) + + // Uint8 (0 to 255) + await throws(() => db.create('thing', { myUint8: -1 }), 'uint8 negative') + await throws(() => db.create('thing', { myUint8: 256 }), 'uint8 overflow') + await db.create('thing', { myUint8: 255 }) + + // Int16 (-32768 to 32767) + await throws(() => db.create('thing', { myInt16: 32768 }), 'int16 overflow') + await throws(() => db.create('thing', { myInt16: -32769 }), 'int16 underflow') + await db.create('thing', { myInt16: 32767 }) + + // Uint16 (0 to 65535) + await throws(() => db.create('thing', { myUint16: 65536 }), 'uint16 overflow') + await db.create('thing', { myUint16: 65535 }) + + // Int32 + await throws( + () => db.create('thing', { myInt32: 2147483648 }), + 'int32 overflow', + ) + await db.create('thing', { myInt32: 2147483647 }) + + // Uint32 + await throws( + () => db.create('thing', { myUint32: 4294967296 }), + 'uint32 overflow', + ) + await db.create('thing', { myUint32: 4294967295 }) + + // Binary + await throws( + () => db.create('thing', { myBlob: 'not a buffer' as any }), + 'binary fail with string', + ) + await throws( + () => db.create('thing', { myBlob: new Uint8Array(20) }), + 'binary maxBytes', + ) + await db.create('thing', { myBlob: new Uint8Array(5) }) + + // Alias + await throws( + () => db.create('thing', { myAlias: 123 as any }), + 'alias fail with number', + ) + const id1 = await db.create('thing', { myAlias: 'cool-alias' }) + + // Alias collision (should throw?) + // db aliases are unique. + // Although not strictly "prop validation" logic (it's uniqueness constraint), alias implies unique. + // But strict type validation is what we tested above. + // Let's stop at type validation as requested. +}) diff --git a/test/query/types.ts b/test/query/types.ts index 30f95ed834..a05c4e411b 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -26,7 +26,7 @@ await test('query types', async (t) => { // vec: 'vector', // col: 'colvec', card: 'cardinality', - myEnum: { enum: ['a', 'b'] as const }, + myEnum: { enum: ['a', 'b'] }, nested: { type: 'object', props: { @@ -90,4 +90,87 @@ await test('query types', async (t) => { // @ts-expect-error const wrongRefs: number = everything.myRefs } + + { + const query = db.query2('everything').include('myEnum') + const { data } = await query.get() + const res = data[0] + const myEnum: 'a' | 'b' = res.myEnum + const id: number = res.id + // @ts-expect-error + const n: number = res.n + } + + { + const query = db.query2('everything').include('*') + const { data } = await query.get() + const res = data[0] + const n: number = res.n + const s: string = res.s + const myEnum: 'a' | 'b' = res.myEnum + // @ts-expect-error + const myRef = res.myRef + } + { + const query = db.query2('everything').include('**') + const { data } = await query.get() + const res = data[0] + + // references + const myRef: number = res.myRef + const myRefs: number[] = res.myRefs + const id: number = res.id + + // Scalars should be missing + // @ts-expect-error + const n: number = res.n + // @ts-expect-error + const s: string = res.s + // @ts-expect-error + const myEnum: 'a' | 'b' = res.myEnum + } + + { + // Combine explicit field + wildcard + const query = db.query2('everything').include('myEnum', '**') + const { data } = await query.get() + const res = data[0] + + const myEnum: 'a' | 'b' = res.myEnum + const myRef: number = res.myRef + const myRefs: number[] = res.myRefs + + // Other scalars missing + // @ts-expect-error + const n: number = res.n + } + + { + // Multiple explicit fields + const query = db.query2('everything').include('n', 's', 'nested') + const { data } = await query.get() + const res = data[0] + + const n: number = res.n + const s: string = res.s + const nestedA: string = res.nested.a + + // Missing + // @ts-expect-error + const myEnum: 'a' | 'b' = res.myEnum + } + + { + // Scalar wildcard + explicit ref + const query = db.query2('everything').include('*', 'myRefs') + const { data } = await query.get() + const res = data[0] + + const n: number = res.n + const myRefs: number[] = res.myRefs + + // Excluded ref + // @ts-expect-error + const myRef: number = res.myRef + } }) diff --git a/test/shared/index.ts b/test/shared/index.ts index add7e3d6ca..3b56d7be19 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -33,7 +33,7 @@ export function logMemoryUsage() { } } -export const testDb = async ( +export const testDb = async ( t, schema: S, ): Promise>> => { From 72a6ab819bf714b17c7f02009e7405928ae58e9f Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 16:14:58 +0100 Subject: [PATCH 115/449] update --- src/db-client/query2/types.ts | 15 +- src/schema/defs/props/fixed.ts | 317 +++++++++++++++++------------- src/schema/defs/props/separate.ts | 7 + test/modify/props/validation.ts | 2 +- test/query/debug_types.ts | 44 +++++ 5 files changed, 242 insertions(+), 143 deletions(-) create mode 100644 test/query/debug_types.ts diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 2c3dd02e6c..3fa49bb759 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -83,7 +83,13 @@ export type RefKeys = { export type ResolveInclude< Props, K extends keyof Props | '*' | '**', -> = K extends '*' ? NonRefKeys : K extends '**' ? RefKeys : K +> = K extends any + ? K extends '*' + ? NonRefKeys + : K extends '**' + ? RefKeys + : K + : never export type IncludeSelection< S extends { types: any; locales?: any }, @@ -94,5 +100,8 @@ export type IncludeSelection< export type PickOutput< S extends { types: any; locales?: any }, T extends keyof S['types'], - K extends keyof S['types'][T]['props'], -> = Pick, K | 'id'> + K, +> = Pick< + InferSchemaOutput, + Extract> | 'id' +> diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 6a23483055..3a741dd913 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -10,86 +10,6 @@ import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' -const validateNumber = (value: unknown, prop: any, path: string[]) => { - if (typeof value !== 'number') { - throw new Error('Invalid type for number ' + path.join('.')) - } - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${path.join('.')}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${path.join('.')}`, - ) - } - return value -} - -const validateTimestamp = (value: unknown, prop: any, path: string[]) => { - const ts = convertToTimestamp(value as any) - if (prop.min !== undefined && ts < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${path.join('.')}`, - ) - } - if (prop.max !== undefined && ts > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${path.join('.')}`, - ) - } - return ts -} - -const validateInteger = ( - value: unknown, - prop: any, - path: string[], - type: string, - min: number, - max: number, -) => { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error(`Invalid type for ${type} ` + path.join('.')) - } - if (value < min || value > max) { - throw new Error(`Value out of range for ${type} ` + path.join('.')) - } - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${path.join('.')}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${path.join('.')}`, - ) - } - return value -} - -const validateEnum = ( - value: unknown, - vals: Map, - path: string[], -) => { - if (typeof value !== 'string' && typeof value !== 'number') { - throw new Error('Invalid type for enum ' + path.join('.')) - } - if (!vals.has(value)) { - throw new Error(`Invalid enum value ${value} for ${path.join('.')}`) - } - return vals.get(value) ?? 0 -} - -const validateBoolean = (value: unknown, path: string[]) => { - if (typeof value !== 'boolean') { - throw new Error('Invalid type for boolean ' + path.join('.')) - } - return value ? 1 : 0 -} - export const number = class Number extends BasePropDef { override type: PropTypeEnum = PropType.number override size = 8 @@ -99,8 +19,25 @@ export const number = class Number extends BasePropDef { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateNumber(value, this.schema, this.path) - buf.pushDoubleLE(val) + if (typeof value !== 'number') { + throw new Error('Invalid type for number ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushDoubleLE(value) } } @@ -112,7 +49,22 @@ export const timestamp = class Timestamp extends number { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number | string { - const ts = validateTimestamp(value, this.schema, this.path) + const ts = convertToTimestamp(value as any) + const prop = this.schema as any + if (prop.min !== undefined && ts < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && ts > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } buf.pushInt64(ts) } } @@ -126,15 +78,28 @@ export const uint8 = class Uint8 extends BasePropDef { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateInteger( - value, - this.schema, - this.path, - 'uint8', - 0, - 255, - ) as number - buf.pushUint8(val) + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for uint8 ' + this.path.join('.')) + } + if (value < 0 || value > 255) { + throw new Error('Value out of range for uint8 ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint8(value) } } @@ -146,15 +111,28 @@ export const int8 = class Int8 extends uint8 { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateInteger( - value, - this.schema, - this.path, - 'int8', - -128, - 127, - ) as number - buf.pushUint8(val) + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for int8 ' + this.path.join('.')) + } + if (value < -128 || value > 127) { + throw new Error('Value out of range for int8 ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint8(value) } } @@ -167,15 +145,28 @@ export const uint16 = class Uint16 extends BasePropDef { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateInteger( - value, - this.schema, - this.path, - 'uint16', - 0, - 65535, - ) as number - buf.pushUint16(val) + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for uint16 ' + this.path.join('.')) + } + if (value < 0 || value > 65535) { + throw new Error('Value out of range for uint16 ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint16(value) } } @@ -187,15 +178,28 @@ export const int16 = class Int16 extends uint16 { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateInteger( - value, - this.schema, - this.path, - 'int16', - -32768, - 32767, - ) as number - buf.pushUint16(val) + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for int16 ' + this.path.join('.')) + } + if (value < -32768 || value > 32767) { + throw new Error('Value out of range for int16 ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint16(value) } } @@ -208,15 +212,28 @@ export const uint32 = class Uint32 extends BasePropDef { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateInteger( - value, - this.schema, - this.path, - 'uint32', - 0, - 4294967295, - ) as number - buf.pushUint32(val) + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for uint32 ' + this.path.join('.')) + } + if (value < 0 || value > 4294967295) { + throw new Error('Value out of range for uint32 ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint32(value) } } @@ -228,15 +245,28 @@ export const int32 = class Int32 extends uint32 { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is number { - const val = validateInteger( - value, - this.schema, - this.path, - 'int32', - -2147483648, - 2147483647, - ) as number - buf.pushUint32(val) + if (typeof value !== 'number' || !Number.isInteger(value)) { + throw new Error('Invalid type for int32 ' + this.path.join('.')) + } + if (value < -2147483648 || value > 2147483647) { + throw new Error('Value out of range for int32 ' + this.path.join('.')) + } + const prop = this.schema as any + if (prop.min !== undefined && value < prop.min) { + throw new Error( + `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value > prop.max) { + throw new Error( + `Value ${value} is larger than max ${prop.max} for ${this.path.join( + '.', + )}`, + ) + } + buf.pushUint32(value) } } @@ -259,7 +289,13 @@ export const enum_ = class Enum extends uint8 { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is EnumItem { - const val = validateEnum(value, this.vals, this.path) + if (typeof value !== 'string' && typeof value !== 'number') { + throw new Error('Invalid type for enum ' + this.path.join('.')) + } + if (!this.vals.has(value)) { + throw new Error(`Invalid enum value ${value} for ${this.path.join('.')}`) + } + const val = this.vals.get(value) ?? 0 buf.pushUint8(val) } } @@ -273,7 +309,10 @@ export const boolean = class Boolean extends BasePropDef { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is boolean { - const val = validateBoolean(value, this.path) + if (typeof value !== 'boolean') { + throw new Error('Invalid type for boolean ' + this.path.join('.')) + } + const val = value ? 1 : 0 buf.pushUint8(val) } } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index bb4e53e910..823c8fa888 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -131,11 +131,18 @@ export const text = class Text extends string { buf.writeUint32(buf.length - start, index) } else if (typeof value === 'object' && value !== null) { for (const key in value) { + if (!(key in LangCode)) { + throw new Error( + `Invalid locale ${key} for text ${this.path.join('.')}`, + ) + } const index = buf.reserveUint32() const start = buf.length super.pushValue(buf, value[key], op, LangCode[key]) buf.writeUint32(buf.length - start, index) } + } else { + throw new Error('Invalid type for text ' + this.path.join('.')) } } override pushSelvaSchema(buf: AutoSizedUint8Array) { diff --git a/test/modify/props/validation.ts b/test/modify/props/validation.ts index f8255c9f9d..81f2e204ad 100644 --- a/test/modify/props/validation.ts +++ b/test/modify/props/validation.ts @@ -92,7 +92,7 @@ await test('modify - validation', async (t) => { 'text value should fail with number', ) await throws( - () => db.create('thing', { myText: { de: 'hello' } as any }), + () => db.create('thing', { myText: { xx: 'hello' } as any }), 'text should fail with invalid locale', ) await db.create('thing', { myText: { en: 'works' } }) diff --git a/test/query/debug_types.ts b/test/query/debug_types.ts new file mode 100644 index 0000000000..f1e005fbfd --- /dev/null +++ b/test/query/debug_types.ts @@ -0,0 +1,44 @@ +import { ResolveSchema } from '../../src/schema/index.js' +import { + InferSchemaOutput, + ResolveInclude, + PickOutput, +} from '../../src/db-client/query2/types.js' + +type MySchemaIn = { + types: { + everything: { + s: 'string' + n: 'number' + nested: { + type: 'object' + props: { + a: 'string' + } + } + } + } +} + +type S = ResolveSchema +type T = 'everything' +type Props = S['types'][T]['props'] +type Keys = keyof Props // Should be 's' | 'n' | 'nested' + +type Output = InferSchemaOutput +type OutputKeys = keyof Output // Should include 's', 'n', 'nested', 'id' + +type Inc1 = ResolveInclude // Should be 'n' +type Inc2 = ResolveInclude // Should be 'n' | 's' + +type Pick1 = PickOutput // Should have 'n' +type Pick2 = PickOutput // Should have 'n' and 's' + +// Test if intersection works +type Inter = ('n' | 's') & OutputKeys // Should be 'n' | 's' + +const p1: Pick1 = {} as any +p1.n +p1.id +// @ts-expect-error +p1.s From 0bc7b613dd40bbe255518c44387f4862a9c9c4a0 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 16:42:05 +0100 Subject: [PATCH 116/449] Remove debug log --- src/protocol/db-read/read.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/protocol/db-read/read.ts b/src/protocol/db-read/read.ts index df69f40204..bca63289b9 100644 --- a/src/protocol/db-read/read.ts +++ b/src/protocol/db-read/read.ts @@ -119,7 +119,6 @@ const readInstruction = ( i: number, item: Item, ): number => { - console.log(result) if (instruction === ReadOp.meta) { return meta(q, result, i, item) } else if (instruction === ReadOp.aggregation) { From 9cbdd4065cc5395aa2b2834184aa0d36cdea1bca Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 3 Feb 2026 12:57:43 -0300 Subject: [PATCH 117/449] ast agg changes --- src/db-query/ast/ast.ts | 19 ++++--- test/aggregate/basic.ts | 115 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 10 deletions(-) diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 252e8579b0..1060ae1efe 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -65,21 +65,20 @@ export type QueryAst = { sort?: { prop: string; order: 'asc' | 'desc' } props?: Record edges?: QueryAst - // ----------- Aggregate stuff count?: { props: string | void } - sum?: { props: string | string[] } - cardinality?: { props: string | string[] } - avg?: { props: string | string[] } - harmonicMean?: { props: string | string[] } - max?: { props: string | string[] } - min?: { props: string | string[] } - stddev?: { props: string | string[]; samplingMode?: 'sample' | 'population' } - var?: { props: string | string[]; samplingMode?: 'sample' | 'population' } + sum?: { props: string[] } + cardinality?: { props: string[] } + avg?: { props: string[] } + harmonicMean?: { props: string[] } + max?: { props: string[] } + min?: { props: string[] } + stddev?: { props: string[]; samplingMode?: 'sample' | 'population' } + var?: { props: string[]; samplingMode?: 'sample' | 'population' } groupBy?: { prop: string step?: number | IntervalString timeZone?: string - display?: Intl.DateTimeFormat + timeFormat?: Intl.DateTimeFormat } } diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 6b5818d084..89a3127c82 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -1086,3 +1086,118 @@ await test('numeric types', async (t) => { // 'range group by references', // ) // }) + +// await test('count props', async (t) => { +// const db = new BasedDb({ +// path: t.tmp, +// maxModifySize: 1e6, +// }) + +// await db.start({ clean: true }) +// t.after(() => db.stop()) + +// await db.setSchema({ +// types: { +// vehicle: { +// props: { +// plate: 'string', +// year: 'unint16', +// }, +// }, +// trip: { +// props: { +// distance: 'number', +// vehicle: { +// ref: 'vehicle', +// prop: 'vehicle', +// }, +// }, +// }, +// }, +// }) + +// const v1 = db.create('vehicle', { +// plate: 'KKK1234', +// year: 2023, +// }) +// db.create('trip', { +// distance: 813.1, +// vehicle: v1, +// }) +// db.create('trip', { +// distance: 1023.1, +// }) +// const v1 = db.create('vehicle', { +// plate: 'LAL0001', +// }) +// await db.query('trip').count().get().inspect() // should count nodes --> count = 2 +// await db.query('trip').count('vehicle').get().inspect() // shoudl count refs --> count = 1 +// await db.query('vehicle').count('year').get().inspect() // should ignore undefined props --> count = 1 +// }) + +// await test('groupBy multiple props', async (t) => { +// const db = new BasedDb({ +// path: t.tmp, +// maxModifySize: 1e6, +// }) + +// await db.start({ clean: true }) +// t.after(() => db.stop()) + +// await db.setSchema({ +// types: { +// vehicle: { +// props: { +// plate: 'string', +// decade: 'uint16', +// brand: 'string', +// }, +// }, +// }, +// }) + +// const v1 = db.create('vehicle', { +// plate: 'KKK1234', +// year: 2020, +// brand: 'Volkswagen', +// }) +// const v2 = db.create('vehicle', { +// plate: 'LAL0001', +// year: 1990, +// brand: 'Volkswagen', +// }) +// const v3 = db.create('vehicle', { +// plate: 'BYD8001', +// year: 2020, +// brand: 'BYD', +// }) + +// await db.query('vehicle').count().groupBy('brand').get().inspect() +/* + { + BYD: { + count: 1 count + }, + Volkswagen: { + count: 2 count + } + } + */ + +// await db.query('vehicle').count().groupBy('decade','brand').get().inspect() +/* +{ + [BYD, 2020]: { + count: 1 count + }, + [BYD, 1990]: { + count: 0 count + }, + [Volkswagen, 1990]: { + count: 2, + }, + [Volkswagen, 2020]: { + count: 0, + } +*/ +// }) From a730277b898c7e7664912e245df2921f156bb8ea Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 17:22:38 +0100 Subject: [PATCH 118/449] Improve alias hashing - Alias can be null - Also hash field and dest --- clibs/lib/selva/db.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 787a9ae5ab..8da54a96b7 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -811,9 +811,14 @@ static void hash_aliases(selva_hash_state_t *hash_state, struct SelvaTypeEntry * struct SelvaAlias find = { .dest = dest, }; + field_t f = i; alias = RB_FIND(SelvaAliasesByDest, &aliases->alias_by_dest, &find); - selva_hash_update(hash_state, alias->name, alias->name_len); + if (alias) { + selva_hash_update(hash_state, &f, sizeof(f)); + selva_hash_update(hash_state, &dest, sizeof(dest)); + selva_hash_update(hash_state, alias->name, alias->name_len); + } } } From b658dc2af57a5172eb48dddbe7a0f1799ab2047c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 17:25:31 +0100 Subject: [PATCH 119/449] Fix fallback --- test/save/save.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/save/save.ts b/test/save/save.ts index 2e7dc9626f..d977c8075b 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -237,7 +237,7 @@ await test('text', async (t) => { await db.setSchema({ locales: { en: {}, - fi: { fallback: 'en' }, + fi: { fallback: ['en'] }, }, types: { article: { From 4a26771f77efb82a940a6852e6a572999a5c46f8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 3 Feb 2026 17:25:44 +0100 Subject: [PATCH 120/449] aliases debug --- native/selva/fields.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/selva/fields.zig b/native/selva/fields.zig index 5b411152ff..d4293e2be6 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -47,6 +47,10 @@ pub fn get( if (propType == t.PropType.alias) { const target = Node.getNodeId(node); const typeAliases = selva.c.selva_get_aliases(typeEntry, fieldSchema.field); + if (typeAliases == null) { + std.log.err("not an alias prop {any}", .{ fieldSchema }); + return @as([*]u8, undefined)[0..0]; + } const alias = selva.c.selva_get_alias_by_dest(typeAliases, target); if (alias == null) { return @as([*]u8, undefined)[0..0]; From 0f4fc528ea46fd1a48791acd329533869d112c96 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 17:28:49 +0100 Subject: [PATCH 121/449] types and validation --- src/db-client/index.ts | 12 +- src/db-client/query2/index.ts | 33 ++-- src/schema/def/typeDef.ts | 3 + src/schema/defs/props/fixed.ts | 278 +++++++++------------------------ src/schema/schema/enum.ts | 2 +- src/schema/schema/prop.ts | 1 + test/query/ast.ts | 194 +++++++++++++++++++++++ test/query/debug_types.ts | 44 ------ test/query/types.ts | 47 +++++- 9 files changed, 348 insertions(+), 266 deletions(-) create mode 100644 test/query/ast.ts delete mode 100644 test/query/debug_types.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index d4c9dd5e7f..8074464e83 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -97,8 +97,16 @@ export class DbClient = SchemaOut> extends DbShared { query2( type: T, - ) { - return new BasedQuery2(type) + ): BasedQuery2 + query2( + type: T, + id: number, + ): BasedQuery2 + query2( + type: T, + id?: number, + ): BasedQuery2 { + return new BasedQuery2(type, id) } create( diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index a0232e4d41..9f4ba89e53 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -4,27 +4,40 @@ export class BasedQuery2< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, K extends (keyof S['types'][T]['props'] | '*' | '**') | 'ALL' = 'ALL', + IsSingle extends boolean = false, > { - constructor(type: T) { + constructor(type: T, id?: number) { this.type = type + if (id !== undefined) { + this.id = id + } } type: T + id?: number async get(): Promise<{ - data: (K extends 'ALL' - ? InferSchemaOutput - : PickOutput< - S, - T, - ResolveInclude> - >)[] + data: IsSingle extends true + ? [K] extends ['ALL'] + ? InferSchemaOutput + : PickOutput< + S, + T, + ResolveInclude> + > + : ([K] extends ['ALL'] + ? InferSchemaOutput + : PickOutput< + S, + T, + ResolveInclude> + >)[] }> { - return { data: [] } + return { data: [] as any } } include( ...fields: F - ): BasedQuery2 { + ): BasedQuery2 { return this as any } } diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index c527309e78..bae3bfc95e 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -280,9 +280,12 @@ const createSchemaTypeDef = ( } if (schemaProp.type === 'enum') { + // @ts-ignore prop.enum = Array.isArray(schemaProp) ? schemaProp : schemaProp.enum prop.reverseEnum = {} + // @ts-ignore for (let i = 0; i < prop.enum.length; i++) { + // @ts-ignore prop.reverseEnum[prop.enum[i]] = i } } else if (schemaProp.type === 'references') { diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 3a741dd913..dfa00409c9 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -1,4 +1,9 @@ -import type { EnumItem, SchemaEnum } from '../../../schema.js' +import type { + EnumItem, + SchemaEnum, + SchemaNumber, + SchemaProp, +} from '../../../schema.js' import { convertToTimestamp } from '../../../utils/timestamp.js' import { PropType, @@ -11,32 +16,41 @@ import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' export const number = class Number extends BasePropDef { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min !== undefined) this.min = schema.min + if (schema.max !== undefined) this.max = schema.max + } override type: PropTypeEnum = PropType.number override size = 8 - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is number { + min = -globalThis.Number.MAX_VALUE + max = globalThis.Number.MAX_VALUE + override validate(value: unknown): asserts value is number { if (typeof value !== 'number') { - throw new Error('Invalid type for number ' + this.path.join('.')) + throw new Error( + `Invalid type for ${this.schema.type} ${this.path.join('.')}`, + ) } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { + if (this.min !== undefined && value < this.min) { throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( + `Value ${value} is smaller than min ${this.min} for ${this.path.join( '.', )}`, ) } - if (prop.max !== undefined && value > prop.max) { + if (this.max !== undefined && value > this.max) { throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( + `Value ${value} is larger than max ${this.max} for ${this.path.join( '.', )}`, ) } + } + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is number { + this.validate(value) buf.pushDoubleLE(value) } } @@ -46,231 +60,85 @@ export const timestamp = class Timestamp extends number { override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is number | string { const ts = convertToTimestamp(value as any) - const prop = this.schema as any - if (prop.min !== undefined && ts < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && ts > prop.max) { + this.validate(ts) + buf.pushInt64(ts) + } +} + +class integer extends number { + override validate(value: unknown): asserts value is number { + super.validate(value) + if (!Number.isInteger(value)) { throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, + `Invalid type for ${this.schema.type} ${this.path.join('.')}`, ) } - buf.pushInt64(ts) } } -export const uint8 = class Uint8 extends BasePropDef { +export const uint8 = class Uint8 extends integer { override type: PropTypeEnum = PropType.uint8 override size = 1 + override min = 0 + override max = 255 override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for uint8 ' + this.path.join('.')) - } - if (value < 0 || value > 255) { - throw new Error('Value out of range for uint8 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } + this.validate(value) buf.pushUint8(value) } } export const int8 = class Int8 extends uint8 { override type = PropType.int8 - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for int8 ' + this.path.join('.')) - } - if (value < -128 || value > 127) { - throw new Error('Value out of range for int8 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint8(value) - } + override min = -128 + override max = 127 } -export const uint16 = class Uint16 extends BasePropDef { +export const uint16 = class Uint16 extends integer { override type: PropTypeEnum = PropType.uint16 override size = 2 + override min = 0 + override max = 65535 override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for uint16 ' + this.path.join('.')) - } - if (value < 0 || value > 65535) { - throw new Error('Value out of range for uint16 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } + this.validate(value) buf.pushUint16(value) } } export const int16 = class Int16 extends uint16 { override type = PropType.int16 - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for int16 ' + this.path.join('.')) - } - if (value < -32768 || value > 32767) { - throw new Error('Value out of range for int16 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint16(value) - } + override min = -32768 + override max = 32767 } -export const uint32 = class Uint32 extends BasePropDef { +export const uint32 = class Uint32 extends integer { override type: PropTypeEnum = PropType.uint32 override size = 4 + override min = 0 + override max = 4294967295 override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for uint32 ' + this.path.join('.')) - } - if (value < 0 || value > 4294967295) { - throw new Error('Value out of range for uint32 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } + this.validate(value) buf.pushUint32(value) } } export const int32 = class Int32 extends uint32 { override type = PropType.int32 - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is number { - if (typeof value !== 'number' || !Number.isInteger(value)) { - throw new Error('Invalid type for int32 ' + this.path.join('.')) - } - if (value < -2147483648 || value > 2147483647) { - throw new Error('Value out of range for int32 ' + this.path.join('.')) - } - const prop = this.schema as any - if (prop.min !== undefined && value < prop.min) { - throw new Error( - `Value ${value} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value > prop.max) { - throw new Error( - `Value ${value} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - buf.pushUint32(value) - } + override min = -2147483648 + override max = 2147483647 } -export const enum_ = class Enum extends uint8 { +export const enum_ = class Enum extends BasePropDef { constructor(prop: SchemaEnum, path: string[], typeDef: TypeDef) { super(prop, path, typeDef) prop.enum.forEach((val, i) => { @@ -279,40 +147,42 @@ export const enum_ = class Enum extends uint8 { this.vals.set(val, byte) }) } + override type = PropType.enum + override size = 1 + enum: Record = {} vals = new Map() - - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is EnumItem { + override validate(value: unknown): asserts value is EnumItem { if (typeof value !== 'string' && typeof value !== 'number') { throw new Error('Invalid type for enum ' + this.path.join('.')) } if (!this.vals.has(value)) { throw new Error(`Invalid enum value ${value} for ${this.path.join('.')}`) } - const val = this.vals.get(value) ?? 0 - buf.pushUint8(val) + } + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is EnumItem { + this.validate(value) + buf.pushUint8(this.vals.get(value) ?? 0) } } export const boolean = class Boolean extends BasePropDef { override type = PropType.boolean override size = 1 + override validate(value: unknown): asserts value is boolean { + if (typeof value !== 'boolean') { + throw new Error('Invalid type for boolean ' + this.path.join('.')) + } + } override pushValue( buf: AutoSizedUint8Array, value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, ): asserts value is boolean { - if (typeof value !== 'boolean') { - throw new Error('Invalid type for boolean ' + this.path.join('.')) - } - const val = value ? 1 : 0 - buf.pushUint8(val) + this.validate(value) + buf.pushUint8(~~value) } } diff --git a/src/schema/schema/enum.ts b/src/schema/schema/enum.ts index 760ed91c97..86369bde49 100644 --- a/src/schema/schema/enum.ts +++ b/src/schema/schema/enum.ts @@ -11,7 +11,7 @@ export type EnumItem = string | number export type SchemaEnum = Base & RequiredIfStrict<{ type: 'enum' }, strict> & { default?: EnumItem - enum: EnumItem[] + enum: EnumItem[] | readonly EnumItem[] } const isEnumItem = (v: unknown): v is EnumItem => diff --git a/src/schema/schema/prop.ts b/src/schema/schema/prop.ts index af2db158a1..ca7649ff28 100644 --- a/src/schema/schema/prop.ts +++ b/src/schema/schema/prop.ts @@ -47,6 +47,7 @@ export type SchemaPropShorthand = | 'cardinality' | NumberType | EnumItem[] + | readonly EnumItem[] export type SchemaProp = | SchemaPropObj diff --git a/test/query/ast.ts b/test/query/ast.ts new file mode 100644 index 0000000000..add46e3bd3 --- /dev/null +++ b/test/query/ast.ts @@ -0,0 +1,194 @@ +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('query ast', async (t) => { + const db = await testDb(t, { + types: { + user: { + isNice: 'boolean', + }, + everything: { + s: 'string', + n: 'number', + i8: 'int8', + u8: 'uint8', + i16: 'int16', + u16: 'uint16', + i32: 'int32', + u32: 'uint32', + b: 'boolean', + txt: 'text', + js: 'json', + ts: 'timestamp', + bin: 'binary', + als: 'alias', + // vec: 'vector', + // col: 'colvec', + card: 'cardinality', + myEnum: ['a', 'b'], + nested: { + type: 'object', + props: { + a: 'string', + }, + }, + myRef: { type: 'reference', ref: 'user', prop: 'backRef' }, + myRefs: { + type: 'references', + items: { ref: 'user', prop: 'backRefs' }, + }, + }, + }, + }) + + await db.create('user', { + isNice: true, + }) + + const query = db.query2('user') + const { data } = await query.get() + + if (data.length > 0) { + const user = data[0] + // Should be strictly boolean, not boolean | null | undefined + const isNice: boolean = user.isNice + const id: number = user.id + // @ts-expect-error + const wrong: string = user.isNice + // @ts-expect-error + const unknown = user.something + } + + const query2 = db.query2('everything') + const res = await query2.get() + const everything = res.data[0] + + if (res.data.length > 0) { + const s: string = everything.s + const n: number = everything.n + const i8: number = everything.i8 + const u8: number = everything.u8 + const i16: number = everything.i16 + const u16: number = everything.u16 + const i32: number = everything.i32 + const u32: number = everything.u32 + const b: boolean = everything.b + const txt: string = everything.txt + const js: any = everything.js + const ts: number = everything.ts + const bin: Uint8Array = everything.bin + const als: string = everything.als + const card: number = everything.card + const myEnum: 'a' | 'b' = everything.myEnum + const nestedA: string = everything.nested.a + const myRef: number = everything.myRef + const myRefs: number[] = everything.myRefs + const id: number = everything.id + + // @ts-expect-error + const wrongEnum: 'c' = everything.myEnum + // @ts-expect-error + const wrongRef: string = everything.myRef + // @ts-expect-error + const wrongRefs: number = everything.myRefs + } + + { + const query = db.query2('everything').include('myEnum') + const { data } = await query.get() + const res = data[0] + const myEnum: 'a' | 'b' = res.myEnum + const id: number = res.id + // @ts-expect-error + const n: number = res.n + } + + { + const query = db.query2('everything').include('*') + const { data } = await query.get() + const res = data[0] + const n: number = res.n + const s: string = res.s + const myEnum: 'a' | 'b' = res.myEnum + // @ts-expect-error + const myRef = res.myRef + } + { + const query = db.query2('everything').include('**') + const { data } = await query.get() + const res = data[0] + + // references + const myRef: number = res.myRef + const myRefs: number[] = res.myRefs + const id: number = res.id + + // Scalars should be missing + // @ts-expect-error + const n: number = res.n + // @ts-expect-error + const s: string = res.s + // @ts-expect-error + const myEnum: 'a' | 'b' = res.myEnum + } + + { + // Combine explicit field + wildcard + const query = db.query2('everything').include('myEnum', '**') + const { data } = await query.get() + const res = data[0] + + const myEnum: 'a' | 'b' = res.myEnum + const myRef: number = res.myRef + const myRefs: number[] = res.myRefs + + // Other scalars missing + // @ts-expect-error + const n: number = res.n + } + + { + // Multiple explicit fields + const query = db.query2('everything').include('n', 's', 'nested') + const { data } = await query.get() + const res = data[0] + + const n: number = res.n + const s: string = res.s + const nestedA: string = res.nested.a + + // Missing + // @ts-expect-error + const myEnum: 'a' | 'b' = res.myEnum + } + + { + // Scalar wildcard + explicit ref + const query = db.query2('everything').include('*', 'myRefs') + const { data } = await query.get() + const res = data[0] + + const n: number = res.n + const myRefs: number[] = res.myRefs + + // Excluded ref + // @ts-expect-error + const myRef: number = res.myRef + } + + { + // Target specific id + const query = db.query2('everything', 1).include('*', 'myRefs') + const { data } = await query.get() + + // Check it's a single item (not array) + const n: number = data.n + const myRefs: number[] = data.myRefs + + // @ts-expect-error + data.map + + // @ts-expect-error + const myRef: number = data.myRef + } +}) diff --git a/test/query/debug_types.ts b/test/query/debug_types.ts deleted file mode 100644 index f1e005fbfd..0000000000 --- a/test/query/debug_types.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ResolveSchema } from '../../src/schema/index.js' -import { - InferSchemaOutput, - ResolveInclude, - PickOutput, -} from '../../src/db-client/query2/types.js' - -type MySchemaIn = { - types: { - everything: { - s: 'string' - n: 'number' - nested: { - type: 'object' - props: { - a: 'string' - } - } - } - } -} - -type S = ResolveSchema -type T = 'everything' -type Props = S['types'][T]['props'] -type Keys = keyof Props // Should be 's' | 'n' | 'nested' - -type Output = InferSchemaOutput -type OutputKeys = keyof Output // Should include 's', 'n', 'nested', 'id' - -type Inc1 = ResolveInclude // Should be 'n' -type Inc2 = ResolveInclude // Should be 'n' | 's' - -type Pick1 = PickOutput // Should have 'n' -type Pick2 = PickOutput // Should have 'n' and 's' - -// Test if intersection works -type Inter = ('n' | 's') & OutputKeys // Should be 'n' | 's' - -const p1: Pick1 = {} as any -p1.n -p1.id -// @ts-expect-error -p1.s diff --git a/test/query/types.ts b/test/query/types.ts index a05c4e411b..66e0cba241 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -1,4 +1,3 @@ -import { deepEqual } from '../shared/assert.js' import { testDb } from '../shared/index.js' import test from '../shared/test.js' @@ -26,15 +25,17 @@ await test('query types', async (t) => { // vec: 'vector', // col: 'colvec', card: 'cardinality', - myEnum: { enum: ['a', 'b'] }, + myEnum: ['a', 'b'], nested: { - type: 'object', props: { a: 'string', }, }, - myRef: { ref: 'user', prop: 'backRef' }, - myRefs: { items: { ref: 'user', prop: 'backRefs' } }, + myRef: { type: 'reference', ref: 'user', prop: 'backRef' }, + myRefs: { + type: 'references', + items: { ref: 'user', prop: 'backRefs' }, + }, }, }, }) @@ -173,4 +174,40 @@ await test('query types', async (t) => { // @ts-expect-error const myRef: number = res.myRef } + + { + // Target specific id + const query = db.query2('everything', 1).include('*', 'myRefs') + const { data } = await query.get() + + // Check it's a single item (not array) + const n: number = data.n + const myRefs: number[] = data.myRefs + + // @ts-expect-error + data.map + + // @ts-expect-error + const myRef: number = data.myRef + } +}) + +await test('query types', async (t) => { + const db = await testDb(t, { + types: { + user: { + isNice: 'boolean', + }, + }, + }) + + const id = await db.create('user', { + isNice: true, + }) + + const { + data: { name, isNice }, + } = await db.query2('user', id).get() + + console.log({ name, isNice }) }) From 1464e9053fa6caab565489c0f8916b4509364a82 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 17:43:20 +0100 Subject: [PATCH 122/449] query typings --- test/query/types.ts | 6 ++++++ test/save/save.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/query/types.ts b/test/query/types.ts index 66e0cba241..12379f8eb4 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -210,4 +210,10 @@ await test('query types', async (t) => { } = await db.query2('user', id).get() console.log({ name, isNice }) + + const { data } = await db.query2('user').get() + + for (const { name, isNice } of data) { + console.log({ name, isNice }) + } }) diff --git a/test/save/save.ts b/test/save/save.ts index d977c8075b..836ad3371d 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -1,4 +1,4 @@ -import { BasedDb, filter } from '../../src/index.js' +import { BasedDb } from '../../src/index.js' import { deepEqual, equal } from '../shared/assert.js' import test from '../shared/test.js' import { setTimeout } from 'node:timers/promises' From 895112ad57c0a0e4ca76dfe3c8f009d4e9031e2f Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 18:11:20 +0100 Subject: [PATCH 123/449] testing query2 --- src/db-client/index.ts | 8 +- src/db-client/query2/index.ts | 32 ++---- test/query/ast.ts | 194 ---------------------------------- test/query/types.ts | 38 ++++--- 4 files changed, 31 insertions(+), 241 deletions(-) delete mode 100644 test/query/ast.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 8074464e83..c0e061a735 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -97,16 +97,16 @@ export class DbClient = SchemaOut> extends DbShared { query2( type: T, - ): BasedQuery2 + ): BasedQuery2 query2( type: T, id: number, - ): BasedQuery2 + ): BasedQuery2 query2( type: T, id?: number, - ): BasedQuery2 { - return new BasedQuery2(type, id) + ): BasedQuery2 { + return new BasedQuery2(type, id) } create( diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 9f4ba89e53..209c4d8203 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -3,41 +3,27 @@ import type { InferSchemaOutput, PickOutput, ResolveInclude } from './types.js' export class BasedQuery2< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, - K extends (keyof S['types'][T]['props'] | '*' | '**') | 'ALL' = 'ALL', + K extends keyof S['types'][T]['props'] | '*' | '**' = '*', IsSingle extends boolean = false, > { constructor(type: T, id?: number) { this.type = type - if (id !== undefined) { - this.id = id - } + this.id = id } type: T id?: number - async get(): Promise<{ - data: IsSingle extends true - ? [K] extends ['ALL'] - ? InferSchemaOutput - : PickOutput< - S, - T, - ResolveInclude> - > - : ([K] extends ['ALL'] - ? InferSchemaOutput - : PickOutput< - S, - T, - ResolveInclude> - >)[] - }> { - return { data: [] as any } + async get(): Promise< + IsSingle extends true + ? PickOutput> + : PickOutput>[] + > { + return [] as any } include( ...fields: F - ): BasedQuery2 { + ): BasedQuery2 { return this as any } } diff --git a/test/query/ast.ts b/test/query/ast.ts deleted file mode 100644 index add46e3bd3..0000000000 --- a/test/query/ast.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' - -await test('query ast', async (t) => { - const db = await testDb(t, { - types: { - user: { - isNice: 'boolean', - }, - everything: { - s: 'string', - n: 'number', - i8: 'int8', - u8: 'uint8', - i16: 'int16', - u16: 'uint16', - i32: 'int32', - u32: 'uint32', - b: 'boolean', - txt: 'text', - js: 'json', - ts: 'timestamp', - bin: 'binary', - als: 'alias', - // vec: 'vector', - // col: 'colvec', - card: 'cardinality', - myEnum: ['a', 'b'], - nested: { - type: 'object', - props: { - a: 'string', - }, - }, - myRef: { type: 'reference', ref: 'user', prop: 'backRef' }, - myRefs: { - type: 'references', - items: { ref: 'user', prop: 'backRefs' }, - }, - }, - }, - }) - - await db.create('user', { - isNice: true, - }) - - const query = db.query2('user') - const { data } = await query.get() - - if (data.length > 0) { - const user = data[0] - // Should be strictly boolean, not boolean | null | undefined - const isNice: boolean = user.isNice - const id: number = user.id - // @ts-expect-error - const wrong: string = user.isNice - // @ts-expect-error - const unknown = user.something - } - - const query2 = db.query2('everything') - const res = await query2.get() - const everything = res.data[0] - - if (res.data.length > 0) { - const s: string = everything.s - const n: number = everything.n - const i8: number = everything.i8 - const u8: number = everything.u8 - const i16: number = everything.i16 - const u16: number = everything.u16 - const i32: number = everything.i32 - const u32: number = everything.u32 - const b: boolean = everything.b - const txt: string = everything.txt - const js: any = everything.js - const ts: number = everything.ts - const bin: Uint8Array = everything.bin - const als: string = everything.als - const card: number = everything.card - const myEnum: 'a' | 'b' = everything.myEnum - const nestedA: string = everything.nested.a - const myRef: number = everything.myRef - const myRefs: number[] = everything.myRefs - const id: number = everything.id - - // @ts-expect-error - const wrongEnum: 'c' = everything.myEnum - // @ts-expect-error - const wrongRef: string = everything.myRef - // @ts-expect-error - const wrongRefs: number = everything.myRefs - } - - { - const query = db.query2('everything').include('myEnum') - const { data } = await query.get() - const res = data[0] - const myEnum: 'a' | 'b' = res.myEnum - const id: number = res.id - // @ts-expect-error - const n: number = res.n - } - - { - const query = db.query2('everything').include('*') - const { data } = await query.get() - const res = data[0] - const n: number = res.n - const s: string = res.s - const myEnum: 'a' | 'b' = res.myEnum - // @ts-expect-error - const myRef = res.myRef - } - { - const query = db.query2('everything').include('**') - const { data } = await query.get() - const res = data[0] - - // references - const myRef: number = res.myRef - const myRefs: number[] = res.myRefs - const id: number = res.id - - // Scalars should be missing - // @ts-expect-error - const n: number = res.n - // @ts-expect-error - const s: string = res.s - // @ts-expect-error - const myEnum: 'a' | 'b' = res.myEnum - } - - { - // Combine explicit field + wildcard - const query = db.query2('everything').include('myEnum', '**') - const { data } = await query.get() - const res = data[0] - - const myEnum: 'a' | 'b' = res.myEnum - const myRef: number = res.myRef - const myRefs: number[] = res.myRefs - - // Other scalars missing - // @ts-expect-error - const n: number = res.n - } - - { - // Multiple explicit fields - const query = db.query2('everything').include('n', 's', 'nested') - const { data } = await query.get() - const res = data[0] - - const n: number = res.n - const s: string = res.s - const nestedA: string = res.nested.a - - // Missing - // @ts-expect-error - const myEnum: 'a' | 'b' = res.myEnum - } - - { - // Scalar wildcard + explicit ref - const query = db.query2('everything').include('*', 'myRefs') - const { data } = await query.get() - const res = data[0] - - const n: number = res.n - const myRefs: number[] = res.myRefs - - // Excluded ref - // @ts-expect-error - const myRef: number = res.myRef - } - - { - // Target specific id - const query = db.query2('everything', 1).include('*', 'myRefs') - const { data } = await query.get() - - // Check it's a single item (not array) - const n: number = data.n - const myRefs: number[] = data.myRefs - - // @ts-expect-error - data.map - - // @ts-expect-error - const myRef: number = data.myRef - } -}) diff --git a/test/query/types.ts b/test/query/types.ts index 12379f8eb4..7f04c2db9b 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -45,7 +45,7 @@ await test('query types', async (t) => { }) const query = db.query2('user') - const { data } = await query.get() + const data = await query.get() if (data.length > 0) { const user = data[0] @@ -60,9 +60,9 @@ await test('query types', async (t) => { const query2 = db.query2('everything') const res = await query2.get() - const everything = res.data[0] + const everything = res[0] - if (res.data.length > 0) { + if (res.length > 0) { const s: string = everything.s const n: number = everything.n const i8: number = everything.i8 @@ -80,8 +80,6 @@ await test('query types', async (t) => { const card: number = everything.card const myEnum: 'a' | 'b' = everything.myEnum const nestedA: string = everything.nested.a - const myRef: number = everything.myRef - const myRefs: number[] = everything.myRefs const id: number = everything.id // @ts-expect-error @@ -94,7 +92,7 @@ await test('query types', async (t) => { { const query = db.query2('everything').include('myEnum') - const { data } = await query.get() + const data = await query.get() const res = data[0] const myEnum: 'a' | 'b' = res.myEnum const id: number = res.id @@ -104,7 +102,7 @@ await test('query types', async (t) => { { const query = db.query2('everything').include('*') - const { data } = await query.get() + const data = await query.get() const res = data[0] const n: number = res.n const s: string = res.s @@ -114,7 +112,7 @@ await test('query types', async (t) => { } { const query = db.query2('everything').include('**') - const { data } = await query.get() + const data = await query.get() const res = data[0] // references @@ -134,7 +132,7 @@ await test('query types', async (t) => { { // Combine explicit field + wildcard const query = db.query2('everything').include('myEnum', '**') - const { data } = await query.get() + const data = await query.get() const res = data[0] const myEnum: 'a' | 'b' = res.myEnum @@ -149,7 +147,7 @@ await test('query types', async (t) => { { // Multiple explicit fields const query = db.query2('everything').include('n', 's', 'nested') - const { data } = await query.get() + const data = await query.get() const res = data[0] const n: number = res.n @@ -164,7 +162,7 @@ await test('query types', async (t) => { { // Scalar wildcard + explicit ref const query = db.query2('everything').include('*', 'myRefs') - const { data } = await query.get() + const data = await query.get() const res = data[0] const n: number = res.n @@ -178,7 +176,7 @@ await test('query types', async (t) => { { // Target specific id const query = db.query2('everything', 1).include('*', 'myRefs') - const { data } = await query.get() + const data = await query.get() // Check it's a single item (not array) const n: number = data.n @@ -205,15 +203,15 @@ await test('query types', async (t) => { isNice: true, }) - const { - data: { name, isNice }, - } = await db.query2('user', id).get() + // const { + // data: { name, isNice }, + // } = await db.query2('user', id).get() - console.log({ name, isNice }) + // console.log({ name, isNice }) - const { data } = await db.query2('user').get() + // const data = await db.query2('user').get() - for (const { name, isNice } of data) { - console.log({ name, isNice }) - } + // for (const { name, isNice } of data) { + // console.log({ name, isNice }) + // } }) From 5fe369e3b6e779869dc18c315aee9868feb04699 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 19:28:45 +0100 Subject: [PATCH 124/449] update query2 --- src/db-client/query2/index.ts | 22 +++++++++++++++------- test/query/ast.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 test/query/ast.ts diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 209c4d8203..95eb68d412 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,3 +1,4 @@ +import type { QueryAst } from '../../db-query/ast/ast.js' import type { InferSchemaOutput, PickOutput, ResolveInclude } from './types.js' export class BasedQuery2< @@ -6,13 +7,11 @@ export class BasedQuery2< K extends keyof S['types'][T]['props'] | '*' | '**' = '*', IsSingle extends boolean = false, > { - constructor(type: T, id?: number) { - this.type = type - this.id = id + constructor(type: T, target?: number) { + this.ast.type = type as string + this.ast.target = target } - type: T - id?: number - + ast: QueryAst = {} async get(): Promise< IsSingle extends true ? PickOutput> @@ -22,8 +21,17 @@ export class BasedQuery2< } include( - ...fields: F + ...props: F ): BasedQuery2 { + for (const prop of props) { + const path = (prop as string).split('.') + let target = this.ast + for (const key of path) { + target.props ??= {} + target = target.props[key] = {} + } + target.include = {} + } return this as any } } diff --git a/test/query/ast.ts b/test/query/ast.ts new file mode 100644 index 0000000000..779f46fde4 --- /dev/null +++ b/test/query/ast.ts @@ -0,0 +1,30 @@ +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('query types', async (t) => { + const db = await testDb(t, { + types: { + user: { + name: 'string', + isNice: 'boolean', + otherUsers: { + items: { + ref: 'user', + prop: 'otherUsers', + }, + }, + }, + }, + }) + + await db.create('user', { + isNice: true, + }) + + const query = db + .query2('user') + .include('isNice', 'name') + .include((select) => select('otherUsers').include('name')) + + const result = query.get() +}) From e32267c690168a6c162fadc68ffcaaa375fcc6a6 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 21:01:44 +0100 Subject: [PATCH 125/449] typemania --- src/db-client/index.ts | 12 +-- src/db-client/query2/index.ts | 61 ++++++++++++- src/db-client/query2/types.ts | 67 ++++++++++---- src/schema/schema/schema.ts | 65 ++++++++++++- test/query/ast.ts | 4 +- test/query/types.ts | 166 +++++++++++++++++++++++----------- test/schema/parse/types.ts | 38 ++++++++ 7 files changed, 327 insertions(+), 86 deletions(-) create mode 100644 test/schema/parse/types.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index c0e061a735..b8c4781416 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -40,7 +40,7 @@ export type ModifyOpts = { locale?: keyof typeof LangCode } -export class DbClient = SchemaOut> extends DbShared { +export class DbClient extends DbShared { constructor({ hooks, maxModifySize = 100 * 1e3 * 1e3, @@ -75,24 +75,24 @@ export class DbClient = SchemaOut> extends DbShared { } } - async setSchema( + async setSchema( schema: T, transformFns?: SchemaMigrateFns, ): Promise>> { - const strictSchema = parse(schema as unknown as SchemaIn).schema + const strictSchema = parse(schema).schema await this.drain() const schemaChecksum = await this.hooks.setSchema( strictSchema as SchemaOut, transformFns, ) if (this.stopped) { - return this as unknown as DbClient> + return this as DbClient> } if (schemaChecksum !== this.schema?.hash) { await this.once('schema') - return this as unknown as DbClient> + return this as DbClient> } - return this as unknown as DbClient> + return this as DbClient> } query2( diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 95eb68d412..a62199280c 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,29 +1,56 @@ import type { QueryAst } from '../../db-query/ast/ast.js' import type { InferSchemaOutput, PickOutput, ResolveInclude } from './types.js' +import type { ResolvedProps } from '../../schema/index.js' export class BasedQuery2< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, - K extends keyof S['types'][T]['props'] | '*' | '**' = '*', + K extends + | keyof ResolvedProps + | '*' + | '**' + | { field: any; select: any } = '*', IsSingle extends boolean = false, + SourceField extends string | number | symbol | undefined = undefined, > { constructor(type: T, target?: number) { this.ast.type = type as string this.ast.target = target } + ast: QueryAst = {} async get(): Promise< IsSingle extends true - ? PickOutput> - : PickOutput>[] + ? PickOutput, K>> + : PickOutput, K>>[] > { return [] as any } - include( + include< + F extends ( + | keyof ResolvedProps + | '*' + | '**' + | ((q: SelectFn) => BasedQuery2) + )[], + >( ...props: F - ): BasedQuery2 { + ): BasedQuery2< + S, + T, + (K extends '*' ? never : K) | ResolveIncludeArgs, + IsSingle, + SourceField + > { for (const prop of props) { + if (typeof prop === 'function') { + const nestedQuery = (prop as any)( + (field: any) => new BasedQuery2(field), + ) + // TODO: Merge nested AST + continue + } const path = (prop as string).split('.') let target = this.ast for (const key of path) { @@ -35,3 +62,27 @@ export class BasedQuery2< return this as any } } + +type SelectFn = < + P extends keyof ResolvedProps, +>( + field: P, +) => BasedQuery2< + S, + ResolvedProps[P] extends { ref: infer R extends string } + ? R + : ResolvedProps[P] extends { + items: { ref: infer R extends string } + } + ? R + : never, + '*', + false, + P +> + +export type ResolveIncludeArgs = T extends ( + q: any, +) => BasedQuery2 + ? { field: SourceField; select: K } + : T diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 3fa49bb759..ad837ae47e 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -7,6 +7,16 @@ type TypedArray = | Int32Array | Uint32Array | Float64Array +import type { ResolvedProps } from '../../schema/index.js' + +export type InferSchemaOutput< + S extends { types: any; locales?: any }, + T extends keyof S['types'], +> = InferType< + ResolvedProps, + S['types'], + S['locales'] extends Record ? S['locales'] : {} +> & { id: number } type TypeMap = { string: string @@ -28,10 +38,14 @@ type TypeMap = { cardinality: number } +// Helper to check if Selection is provided (not never/any/unknown default behavior) +type IsSelected = [T] extends [never] ? false : true + type InferProp< Prop, Types, Locales extends Record = Record, + Selection = never, > = Prop extends { type: 'text' } ? string : Prop extends { type: 'object'; props: infer P } @@ -40,10 +54,18 @@ type InferProp< ? TypeMap[T] : Prop extends { enum: infer E extends readonly any[] } ? E[number] - : Prop extends { ref: string } - ? number // ID - : Prop extends { items: { ref: string } } - ? number[] // IDs + : Prop extends { ref: infer R extends string } + ? IsSelected extends true + ? R extends keyof Types + ? PickOutput<{ types: Types; locales: Locales }, R, Selection> + : never + : number // ID + : Prop extends { items: { ref: infer R extends string } } + ? IsSelected extends true + ? R extends keyof Types + ? PickOutput<{ types: Types; locales: Locales }, R, Selection>[] + : never + : number[] // IDs : unknown type InferType< @@ -54,15 +76,6 @@ type InferType< [K in keyof Props]: InferProp } -export type InferSchemaOutput< - S extends { types: any; locales?: any }, - T extends keyof S['types'], -> = InferType< - S['types'][T]['props'], - S['types'], - S['locales'] extends Record ? S['locales'] : {} -> & { id: number } - // Helpers for include type IsRefProp

= P extends { type: 'reference' } | { type: 'references' } ? true @@ -82,7 +95,7 @@ export type RefKeys = { export type ResolveInclude< Props, - K extends keyof Props | '*' | '**', + K extends keyof Props | '*' | '**' | { field: any; select: any }, > = K extends any ? K extends '*' ? NonRefKeys @@ -94,14 +107,28 @@ export type ResolveInclude< export type IncludeSelection< S extends { types: any; locales?: any }, T extends keyof S['types'], - K extends keyof S['types'][T]['props'] | '*', -> = ResolveInclude + K extends keyof ResolvedProps | '*', +> = ResolveInclude, K> export type PickOutput< S extends { types: any; locales?: any }, T extends keyof S['types'], K, -> = Pick< - InferSchemaOutput, - Extract> | 'id' -> +> = { + [P in + | Extract> + | 'id']: P extends keyof ResolvedProps + ? IsRefProp[P]> extends true + ? ResolvedProps[P] extends { items: any } + ? { id: number }[] + : { id: number } + : InferSchemaOutput[P] + : InferSchemaOutput[P] +} & { + [Item in Extract as Item['field']]: InferProp< + ResolvedProps[Item['field']], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + Item['select'] + > +} diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index a936f4ee0f..ff3ab05a6e 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -58,6 +58,60 @@ type NormalizeProp = T extends string ? T & { type: 'enum' } : T +// Utility to convert a Union to an Intersection +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( + k: infer I, +) => void + ? I + : never + +// Helper to find Props in other types that reference TName with a specific 'prop' field +type GetBackRefs = UnionToIntersection< + { + [K in keyof Types]: ( + Types[K] extends { props: infer P } ? P : Types[K] + ) extends infer Props + ? { + [P in keyof Props as Props[P] extends { + ref: TName + prop: infer BackProp extends string + } + ? BackProp + : Props[P] extends { + items: { ref: TName; prop: infer BackProp extends string } + } + ? BackProp + : never]: { + type: 'references' + items: { + type: 'reference' + ref: K & string + prop: P & string + } + } + } + : never + }[keyof Types] +> + +import type { SchemaProp } from './prop.js' + +// ResolvedProps combines explicit props with inferred back-reference props +export type ResolvedProps< + Types, + TName extends keyof Types, + Props = NormalizeType extends { props: infer P } ? P : {}, + BackRefs = GetBackRefs, +> = { + [K in keyof (Props & + ([BackRefs] extends [never] ? {} : Omit)) as Extract< + K, + string + >]: (Props & + ([BackRefs] extends [never] ? {} : Omit))[K] & + SchemaProp +} + type NormalizeType = T extends { props: infer P } ? Omit & { props: { [K in keyof P]: NormalizeProp } } : { props: { [K in keyof T]: NormalizeProp } } @@ -69,7 +123,9 @@ export type ResolveSchema = Omit< 'types' | 'locales' > & { types: { - [K in keyof S['types']]: NormalizeType + [K in keyof S['types']]: Omit, 'props'> & { + props: ResolvedProps + } } locales: S extends { locales: infer L } ? L extends readonly (infer K extends LangName)[] @@ -136,7 +192,9 @@ const track =

>(input: P): P => { /* This returns a "public" parsed schema, suitable for external users */ -export const parseSchema = (input: S): ResolveSchema => { +export const parseSchema = ( + input: S, +): ResolveSchema => { const v: unknown = track(input) assert(isRecord(v), 'Schema should be record') try { @@ -169,6 +227,7 @@ export const parseSchema = (input: S): ResolveSchema => { defaultTimezone: v.defaultTimezone, migrations: v.migrations, types, + hash: v.hash, }) as SchemaOut assertExpectedProps(result, v) @@ -183,7 +242,7 @@ export const parseSchema = (input: S): ResolveSchema => { // TODO we can remove hash from here after we finish new schema defs (internal schema) result.hash = hash(result) - return result as ResolveSchema + return result as unknown as ResolveSchema } catch (e) { if (tracking) { e = Error(`${path.join('.')}: ${inspect(value)} - ${e}`, { cause: e }) diff --git a/test/query/ast.ts b/test/query/ast.ts index 779f46fde4..b92fac906a 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -26,5 +26,7 @@ await test('query types', async (t) => { .include('isNice', 'name') .include((select) => select('otherUsers').include('name')) - const result = query.get() + const result = await query.get() + + result[0].otherUsers }) diff --git a/test/query/types.ts b/test/query/types.ts index 7f04c2db9b..f3aa1425b7 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -3,6 +3,7 @@ import test from '../shared/test.js' await test('query types', async (t) => { const db = await testDb(t, { + locales: { en: true }, types: { user: { isNice: 'boolean', @@ -40,10 +41,38 @@ await test('query types', async (t) => { }, }) - await db.create('user', { + const id1 = await db.create('everything', {}) + + const id = await db.create('user', { isNice: true, }) + const everythingId = await db.create('everything', { + s: 'some string', + n: 123, + i8: 1, + u8: 1, + i16: 1, + u16: 1, + i32: 1, + u32: 1, + b: true, + txt: { en: 'some text' }, + js: { a: 1 }, + ts: Date.now(), + bin: new Uint8Array(1), + als: 'alias', + myEnum: 'a', + nested: { + a: 'nested string', + }, + myRef: id, + myRefs: [id], + }) + + // Wait for consistency + await new Promise((resolve) => setTimeout(resolve, 100)) + const query = db.query2('user') const data = await query.get() @@ -93,84 +122,96 @@ await test('query types', async (t) => { { const query = db.query2('everything').include('myEnum') const data = await query.get() - const res = data[0] - const myEnum: 'a' | 'b' = res.myEnum - const id: number = res.id - // @ts-expect-error - const n: number = res.n + if (data.length > 0) { + const res = data[0] + const myEnum: 'a' | 'b' = res.myEnum + const id: number = res.id + // @ts-expect-error + const n: number = res.n + } } { const query = db.query2('everything').include('*') const data = await query.get() - const res = data[0] - const n: number = res.n - const s: string = res.s - const myEnum: 'a' | 'b' = res.myEnum - // @ts-expect-error - const myRef = res.myRef + if (data.length > 0) { + const res = data[0] + const n: number = res.n + const s: string = res.s + const myEnum: 'a' | 'b' = res.myEnum + // @ts-expect-error + const myRef = res.myRef + } } { const query = db.query2('everything').include('**') const data = await query.get() - const res = data[0] + if (data.length > 0) { + const res = data[0] - // references - const myRef: number = res.myRef - const myRefs: number[] = res.myRefs - const id: number = res.id + // references + const myRef: { id: number } = res.myRef + const myRefs: { id: number }[] = res.myRefs + const id: number = res.id - // Scalars should be missing - // @ts-expect-error - const n: number = res.n - // @ts-expect-error - const s: string = res.s - // @ts-expect-error - const myEnum: 'a' | 'b' = res.myEnum + // Scalars should be missing + // @ts-expect-error + const n: number = res.n + // @ts-expect-error + const s: string = res.s + // @ts-expect-error + const myEnum: 'a' | 'b' = res.myEnum + } } { // Combine explicit field + wildcard const query = db.query2('everything').include('myEnum', '**') const data = await query.get() - const res = data[0] + if (data.length > 0) { + const res = data[0] - const myEnum: 'a' | 'b' = res.myEnum - const myRef: number = res.myRef - const myRefs: number[] = res.myRefs + const myEnum: 'a' | 'b' = res.myEnum + const myRef: { id: number } = res.myRef + const myRefs: { id: number }[] = res.myRefs - // Other scalars missing - // @ts-expect-error - const n: number = res.n + // Other scalars missing + // @ts-expect-error + const n: number = res.n + } } { // Multiple explicit fields const query = db.query2('everything').include('n', 's', 'nested') const data = await query.get() - const res = data[0] + if (data.length > 0) { + const res = data[0] - const n: number = res.n - const s: string = res.s - const nestedA: string = res.nested.a + const n: number = res.n + const s: string = res.s + const nestedA: string = res.nested.a - // Missing - // @ts-expect-error - const myEnum: 'a' | 'b' = res.myEnum + // Missing + // @ts-expect-error + const myEnum: 'a' | 'b' = res.myEnum + } } { // Scalar wildcard + explicit ref const query = db.query2('everything').include('*', 'myRefs') const data = await query.get() - const res = data[0] + if (data.length > 0) { + const res = data[0] - const n: number = res.n - const myRefs: number[] = res.myRefs + const n: number = res.n + const myRefs: { id: number }[] = res.myRefs - // Excluded ref - // @ts-expect-error - const myRef: number = res.myRef + // Excluded ref + // @ts-expect-error + const myRef: number = res.myRef + } } { @@ -178,15 +219,38 @@ await test('query types', async (t) => { const query = db.query2('everything', 1).include('*', 'myRefs') const data = await query.get() - // Check it's a single item (not array) - const n: number = data.n - const myRefs: number[] = data.myRefs + if ('n' in data) { + // Check it's a single item (not array) + const n: number = data.n + const myRefs: { id: number }[] = data.myRefs - // @ts-expect-error - data.map + // @ts-expect-error + data.map - // @ts-expect-error - const myRef: number = data.myRef + // @ts-expect-error + const myRef: number = data.myRef + } + } + + { + const query = db + .query2('everything', 1) + .include((select) => select('myRefs').include('isNice')) + const data = await query.get() + if ('myRefs' in data) { + for (const item of data.myRefs) { + const isNice: boolean = item.isNice + } + } + } + + { + const query = db.query2('user', 1).include('backRefs') + const data = await query.get() + + if ('backRefs' in data) { + const backRefs: { id: number }[] = data.backRefs + } } }) diff --git a/test/schema/parse/types.ts b/test/schema/parse/types.ts new file mode 100644 index 0000000000..fe17558416 --- /dev/null +++ b/test/schema/parse/types.ts @@ -0,0 +1,38 @@ +import { + parseSchema, + type SchemaOut, + type SchemaReferences, +} from '../../../src/schema.js' +import { test } from '../../shared/index.js' + +await test('types', async () => { + const schemaOut: SchemaOut = parseSchema({ + hash: 0, + locales: { + nl: true, + }, + types: { + coolUser: { + props: { + name: 'string', + myObj: { + type: 'object', + props: { + dicky: 'string', + }, + }, + }, + }, + coolType: { + myUser: { + ref: 'coolUser', + prop: 'myType', + }, + }, + }, + }) + + const myType: SchemaReferences = schemaOut.types.coolUser.props + .myType as SchemaReferences + console.log({ myType }) +}) From e930a5ae64406c9ea5dfb3d8b9490e349065d30d Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 21:16:51 +0100 Subject: [PATCH 126/449] heard you like typescript --- test/query/types.ts | 28 +++++++++++++++++++++++++--- test/schema/parse/types.ts | 4 ---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/test/query/types.ts b/test/query/types.ts index f3aa1425b7..f5bb356c98 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -245,11 +245,13 @@ await test('query types', async (t) => { } { - const query = db.query2('user', 1).include('backRefs') + const query = db + .query2('user', 1) + .include((select) => select('backRefs').include('myEnum')) const data = await query.get() - if ('backRefs' in data) { - const backRefs: { id: number }[] = data.backRefs + for (const { id, myEnum, nonExistent } of data.backRefs) { + console.log({ id, myEnum, nonExistent }) } } }) @@ -260,13 +262,33 @@ await test('query types', async (t) => { user: { isNice: 'boolean', }, + article: { + title: 'string', + readers: { + ref: 'user', + prop: 'readArticles', + }, + }, }, }) const id = await db.create('user', { isNice: true, + readArticles: [1], }) + const users = await db + .query2('user') + .include('isNice') + .include((select) => select('readArticles').include('title')) + .get() + + for (const { isNice, readArticles } of users) { + for (const article of readArticles) { + const { title, wrongField } = article + } + } + // const { // data: { name, isNice }, // } = await db.query2('user', id).get() diff --git a/test/schema/parse/types.ts b/test/schema/parse/types.ts index fe17558416..d8f39f1e06 100644 --- a/test/schema/parse/types.ts +++ b/test/schema/parse/types.ts @@ -31,8 +31,4 @@ await test('types', async () => { }, }, }) - - const myType: SchemaReferences = schemaOut.types.coolUser.props - .myType as SchemaReferences - console.log({ myType }) }) From 04d66eea2968a7b8b28957bd4f1bac28adbee5a7 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 3 Feb 2026 21:34:18 +0100 Subject: [PATCH 127/449] typecheck inverse prop --- src/schema/schema/schema.ts | 74 +++++++++++++++++++++++++++++++++++++ test/query/types.ts | 46 +++++------------------ test/shared/index.ts | 10 +++-- 3 files changed, 90 insertions(+), 40 deletions(-) diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index ff3ab05a6e..0d73fb0538 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -116,6 +116,80 @@ type NormalizeType = T extends { props: infer P } ? Omit & { props: { [K in keyof P]: NormalizeProp } } : { props: { [K in keyof T]: NormalizeProp } } +// Helper to extract props from a type definition (explicit or shorthand) +type GetProps = T extends { props: infer P } ? P : T + +// Helper to find "Incoming Claims" - properties on TargetRef that explicitly point to MyType.MyProp +type GetIncomingClaims = { + [K in keyof GetProps]: GetProps< + Types[TargetRef] + >[K] extends infer TargetProp + ? TargetProp extends { ref: MyType; prop: MyProp } + ? K + : TargetProp extends { items: { ref: MyType; prop: MyProp } } + ? K + : never + : never +}[keyof GetProps] + +type ValidateProp = Prop extends { + ref: infer Ref extends string + prop: infer BackProp extends string +} + ? Ref extends keyof Types + ? GetIncomingClaims extends infer Claims + ? [Claims] extends [never] + ? Prop + : BackProp extends Claims + ? Prop + : { ref: Ref; prop: Claims } & Omit + : never + : Prop + : Prop extends { + items: { + ref: infer Ref extends string + prop: infer BackProp extends string + } + } + ? Ref extends keyof Types + ? GetIncomingClaims extends infer Claims + ? [Claims] extends [never] + ? Prop + : BackProp extends Claims + ? Prop + : Prop extends { items: infer I } + ? { + items: { ref: Ref; prop: Claims } & Omit + } & Omit + : Prop + : never + : Prop + : Prop + +type ValidateProps = { + [K in keyof Props]: ValidateProp +} + +export type ValidateSchema = Omit & { + types: { + [K in keyof S['types']]: S['types'][K] extends { props: infer P } + ? { props: ValidateProps } & Omit< + S['types'][K], + 'props' + > + : { + [P in keyof S['types'][K]]: P extends + | 'hooks' + | 'blockCapacity' + | 'insertOnly' + | 'capped' + | 'partial' + ? S['types'][K][P] + : ValidateProp + } + } +} + import { type LangName, type SchemaLocale } from './locales.js' export type ResolveSchema = Omit< diff --git a/test/query/types.ts b/test/query/types.ts index f5bb356c98..5157c57e14 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -250,9 +250,9 @@ await test('query types', async (t) => { .include((select) => select('backRefs').include('myEnum')) const data = await query.get() - for (const { id, myEnum, nonExistent } of data.backRefs) { - console.log({ id, myEnum, nonExistent }) - } + // for (const { id, myEnum, nonExistent } of data.backRefs) { + // console.log({ id, myEnum, nonExistent }) + // } } }) @@ -261,43 +261,15 @@ await test('query types', async (t) => { types: { user: { isNice: 'boolean', - }, - article: { - title: 'string', - readers: { - ref: 'user', - prop: 'readArticles', + name: { + type: 'string', + required: true, }, }, }, }) - const id = await db.create('user', { - isNice: true, - readArticles: [1], - }) - - const users = await db - .query2('user') - .include('isNice') - .include((select) => select('readArticles').include('title')) - .get() - - for (const { isNice, readArticles } of users) { - for (const article of readArticles) { - const { title, wrongField } = article - } - } - - // const { - // data: { name, isNice }, - // } = await db.query2('user', id).get() - - // console.log({ name, isNice }) - - // const data = await db.query2('user').get() - - // for (const { name, isNice } of data) { - // console.log({ name, isNice }) - // } + // const id = await db.create('user', { + // isNice: true, + // }) }) diff --git a/test/shared/index.ts b/test/shared/index.ts index 3b56d7be19..46ef490c75 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,4 +1,8 @@ -import type { ResolveSchema, SchemaIn } from '../../src/schema.js' +import type { + ResolveSchema, + SchemaIn, + ValidateSchema, +} from '../../src/schema.js' import { BasedDb, type DbClient } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' @@ -35,10 +39,10 @@ export function logMemoryUsage() { export const testDb = async ( t, - schema: S, + schema: S & ValidateSchema, ): Promise>> => { const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) t.after(() => db.destroy()) - return db.setSchema(schema) + return db.setSchema(schema) as unknown as Promise>> } From bf294a41d052e099a05c6eb93b6d3646a0d92480 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 3 Feb 2026 19:39:44 -0300 Subject: [PATCH 128/449] agg AST to query Buffer --- src/db-query/ast/aggregates.ts | 182 +++++++++++++++++++++++++++++++++ src/db-query/ast/multiple.ts | 7 ++ test/query-ast/aggregate.ts | 25 ----- test/query-ast/aggregates.ts | 62 +++++++++++ 4 files changed, 251 insertions(+), 25 deletions(-) create mode 100644 src/db-query/ast/aggregates.ts delete mode 100644 test/query-ast/aggregate.ts create mode 100644 test/query-ast/aggregates.ts diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts new file mode 100644 index 0000000000..3a357fb66a --- /dev/null +++ b/src/db-query/ast/aggregates.ts @@ -0,0 +1,182 @@ +import { TypeDef } from '../../schema/defs/index.js' +import { + QueryType, + QueryIteratorType, + AggFunction, + AggHeaderByteSize, + createAggHeader, + createAggProp, + AggPropByteSize, +} from '../../zigTsExports.js' +import { Ctx, QueryAst } from './ast.js' +import { filter } from './filter/filter.js' +import { aggregateTypeMap } from '../../db-client/query/aggregates/types.js' + +export const pushAggregatesQuery = ( + ast: QueryAst, + ctx: Ctx, + typeDef: TypeDef, +) => { + const headerStartPos = ctx.query.length + ctx.query.length += AggHeaderByteSize + + let filterSize = 0 + if (ast.filter) { + filterSize = filter(ast.filter, ctx, typeDef) + } + + const sizes = { + result: 0, + accumulator: 0, + } + + const hasGroupBy = false // TODO : later + + pushAggregates(ast, ctx, typeDef, sizes) + + const headerBuffer = buildAggregateHeader( + ast, + typeDef, + filterSize, + hasGroupBy, + sizes, + ) + ctx.query.data.set(headerBuffer, headerStartPos) +} + +const isRootCountOnly = (ast: QueryAst) => { + return !!( + ast.count && + !ast.sum && + !ast.avg && + !ast.min && + !ast.max && + !ast.stddev && + !ast.var && + !ast.harmonicMean && + !ast.cardinality + ) +} + +const buildAggregateHeader = ( + ast: QueryAst, + typeDef: TypeDef, + filterSize: number, + hasGroupBy: boolean, + sizes: { result: number; accumulator: number }, +) => { + const rangeStart = ast.range?.start || 0 + + const commonHeader = { + offset: rangeStart, + filterSize, + hasGroupBy, + resultsSize: sizes.result, + accumulatorSize: sizes.accumulator, + isSamplingSet: true, // TODO : later + } + + const isCountOnly = isRootCountOnly(ast) + const op = isCountOnly ? QueryType.aggregatesCount : QueryType.aggregates + + let headerBuffer: Uint8Array + + // TODO: references + + headerBuffer = createAggHeader({ + ...commonHeader, + op, + typeId: typeDef.id, + limit: (ast.range?.end || 1000) + rangeStart, + iteratorType: + filterSize === 0 + ? QueryIteratorType.aggregate + : QueryIteratorType.aggregateFilter, // TODO : later + }) + return headerBuffer +} + +const pushAggregates = ( + ast: QueryAst, + ctx: Ctx, + typeDef: TypeDef, + sizes: { result: number; accumulator: number }, +) => { + // this for loop may be temporary + // need to support repeated funcs or keep it very strict + // adding a validation to force only distinct funcs with props[] + const aggs = [ + { key: 'count', fn: AggFunction.count }, + { key: 'sum', fn: AggFunction.sum }, + { key: 'avg', fn: AggFunction.avg }, + { key: 'min', fn: AggFunction.min }, + { key: 'max', fn: AggFunction.max }, + { key: 'cardinality', fn: AggFunction.cardinality }, + { key: 'stddev', fn: AggFunction.stddev }, + { key: 'var', fn: AggFunction.variance }, + { key: 'harmonicMean', fn: AggFunction.hmean }, + ] + + for (const { key, fn } of aggs) { + const data = ast[key] + if (!data) continue + + const props = Array.isArray(data.props) + ? data.props + : data.props + ? [data.props] + : [] + + if (key === 'count' && props.length === 0) { + continue + } + + for (const propName of props) { + const propDef = typeDef.props.get(propName) + if (!propDef) { + throw new Error(`Aggregate property '${propName}' not found`) + } + + let resSize = 0 + let accSize = 0 + const specificSizes = aggregateTypeMap.get(fn) + if (specificSizes) { + resSize += specificSizes.resultsSize + accSize += specificSizes.accumulatorSize + } else { + resSize += 8 + accSize += 8 + } + + const buffer = createAggProp({ + propId: propDef.id, + propType: propDef.type || 0, + propDefStart: propDef.start || 0, + aggFunction: fn, + resultPos: sizes.result, + accumulatorPos: sizes.accumulator, + }) + + ctx.query.data.set(buffer, ctx.query.length) + ctx.query.length += AggPropByteSize + + sizes.result += resSize + sizes.accumulator += accSize + } + } +} + +export const isAggregateAst = (ast: QueryAst) => { + return !!( + ast.groupBy || + ast.count || + ast.sum || + ast.avg || + ast.min || + ast.max || + ast.stddev || + ast.var || + ast.harmonicMean || + ast.cardinality + ) +} diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index f786fa0837..9d036ba7a3 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -13,9 +13,16 @@ import { filter } from './filter/filter.js' import { include } from './include.js' import { getIteratorType } from './iteratorType.js' import { readPropDef, readSchema } from './readSchema.js' +import { isAggregateAst, pushAggregatesQuery } from './aggregates.js' export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { const rangeStart = ast.range?.start || 0 + + if (isAggregateAst(ast)) { + pushAggregatesQuery(ast, ctx, typeDef) + return + } + const headerIndex = pushQueryHeader(ctx.query, { op: QueryType.default, prop: ID_PROP, diff --git a/test/query-ast/aggregate.ts b/test/query-ast/aggregate.ts deleted file mode 100644 index 33c39f0591..0000000000 --- a/test/query-ast/aggregate.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { QueryAst } from '../../src/db-query/ast/ast.js' -import { testDb } from '../shared/index.js' - -import test from '../shared/test.js' - -await test('aggregate', async (t) => { - const client = await testDb(t, { - types: { - user: { - age: 'uint8', - }, - }, - }) - - const a = client.create('user', { - age: 18, - }) - - const ast: QueryAst = { - type: 'user', - // aggregate: { - // props: {}, - // }, - } -}) diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts new file mode 100644 index 0000000000..b5847aec30 --- /dev/null +++ b/test/query-ast/aggregates.ts @@ -0,0 +1,62 @@ +import { QueryAst } from '../../src/db-query/ast/ast.js' +import { testDb } from '../shared/index.js' +import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' +import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' +import { BasedDb, debugBuffer } from '../../src/sdk.js' +import { + resultToObject, + serializeReaderSchema, +} from '../../src/protocol/index.js' + +import test from '../shared/test.js' + +await test('aggregate', async (t) => { + const db = new BasedDb({ path: t.tmp }) + await db.start({ clean: true }) + t.after(() => db.destroy()) + + // const client = await testDb(t, { + const client = await db.setSchema({ + types: { + user: { + age: 'uint8', + }, + }, + }) + + const a = client.create('user', { + age: 18, + }) + const back = client.create('user', { + age: 30, + }) + + const ast: QueryAst = { + type: 'user', + // props: { + // age: { include: {} }, + // }, + sum: { + props: ['age'], + }, + } + const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + const result = await db.server.getQueryBuf(ctx.query) + debugBuffer(result) + + const readSchemaBuf = serializeReaderSchema(ctx.readSchema) + + const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + console.dir(obj, { depth: 10 }) + + console.log( + JSON.stringify(obj).length, + readSchemaBuf.byteLength + result.byteLength, + ) + + console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + const r = await db.query('user').sum('age').get() + r.debug() + r.inspect(10) +}) From 44d80789abd46a79bad89fdc4530af0edf624c70 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 08:09:21 +0100 Subject: [PATCH 129/449] upsert insert --- src/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 02b4ee8385..b854834810 100644 --- a/src/index.ts +++ b/src/index.ts @@ -72,13 +72,13 @@ export class BasedDb extends Emitter { return this.client.update.apply(this.client, arguments) } - // upsert: DbClient['upsert'] = function (this: BasedDb) { - // return this.client.upsert.apply(this.client, arguments) - // } + upsert: DbClient['upsert'] = function (this: BasedDb) { + return this.client.upsert.apply(this.client, arguments) + } - // insert: DbClient['insert'] = function (this: BasedDb) { - // return this.client.insert.apply(this.client, arguments) - // } + insert: DbClient['insert'] = function (this: BasedDb) { + return this.client.insert.apply(this.client, arguments) + } delete: DbClient['delete'] = function (this: BasedDb) { return this.client.delete.apply(this.client, arguments) From 3868cb8a34572abc7ceed65974173917d54bb9fe Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 08:32:20 +0100 Subject: [PATCH 130/449] Fix upsert save test --- test/save/save.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/save/save.ts b/test/save/save.ts index 836ad3371d..54aac5e09e 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -437,10 +437,7 @@ await test('upsert', async (t) => { }) await db.drain() await db.save() - await db.upsert('person', { - alias: 'boss', - age: 42, - }) + await db.upsert('person', { alias: 'boss' }, { age: 42 }) await db.drain() await db.save() @@ -588,10 +585,7 @@ await test('simulated periodic save', async (t) => { await db.save() // change a node using an alias - db.upsert('person', { - alias: 'slim', - name: 'Shady', - }) + db.upsert('person', { alias: 'slim' }, { name: 'Shady' }) await db.drain() await db.save() From aad1c58e066915792bc3a09cb51122038d2eccba Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 08:32:38 +0100 Subject: [PATCH 131/449] Some prettier --- test/save/saveRange.ts | 62 ++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/test/save/saveRange.ts b/test/save/saveRange.ts index c7349e217b..8fc382382b 100644 --- a/test/save/saveRange.ts +++ b/test/save/saveRange.ts @@ -1,4 +1,4 @@ -import { createHash } from 'node:crypto'; +import { createHash } from 'node:crypto' import { readdir } from 'node:fs/promises' import { BasedDb, DbServer } from '../../src/index.js' import test from '../shared/test.js' @@ -6,18 +6,27 @@ import { italy } from '../shared/examples.js' import { deepEqual, equal, notEqual } from '../shared/assert.js' import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' -const getActiveBlocks = async (db: DbServer, tc: number): Promise> => (await getBlockStatuses(db, tc)).reduce((acc, cur, i) => { - if (cur) { - acc.push(i) - } - return acc -}, [] as Array) -const block2start = (block: number, capacity: number): number => block * capacity + 1; +const getActiveBlocks = async ( + db: DbServer, + tc: number, +): Promise> => + (await getBlockStatuses(db, tc)).reduce((acc, cur, i) => { + if (cur) { + acc.push(i) + } + return acc + }, [] as Array) +const block2start = (block: number, capacity: number): number => + block * capacity + 1 const hashType = async (db: DbServer, typeName: string): Promise => { const tc = db.schemaTypesParsed[typeName].id const capacity = db.schemaTypesParsed[typeName].blockCapacity const hash = createHash('sha256') - const bhs = await Promise.all((await getActiveBlocks(db, tc)).map((block) => getBlockHash(db, tc, block2start(block, capacity)))) + const bhs = await Promise.all( + (await getActiveBlocks(db, tc)).map((block) => + getBlockHash(db, tc, block2start(block, capacity)), + ), + ) for (const bh of bhs) { hash.update(bh) } @@ -190,7 +199,10 @@ async function countDirtyBlocks(server: DbServer) { let n = 0 for (const t of Object.keys(server.schemaTypesParsedById)) { - n += (await getBlockStatuses(server, Number(t))).reduce((acc, cur) => acc + ~~!!(cur & 0x4), 0) + n += (await getBlockStatuses(server, Number(t))).reduce( + (acc, cur) => acc + ~~!!(cur & 0x4), + 0, + ) } return n @@ -227,14 +239,22 @@ await test('reference changes', async (t) => { }), ) await db.drain() - equal(await countDirtyBlocks(db.server), 1, 'creating new users creates a dirty range') + equal( + await countDirtyBlocks(db.server), + 1, + 'creating new users creates a dirty range', + ) db.create('doc', { title: 'The Wonders of AI', creator: users[0], }) await db.drain() - equal(await countDirtyBlocks(db.server), 2, 'creating nodes in two types makes both dirty') + equal( + await countDirtyBlocks(db.server), + 2, + 'creating nodes in two types makes both dirty', + ) await db.save() equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') @@ -246,21 +266,33 @@ await test('reference changes', async (t) => { title: 'The Hype of AI', }) await db.drain() - equal(await countDirtyBlocks(db.server), 1, 'creating docs makes the range dirty') + equal( + await countDirtyBlocks(db.server), + 1, + 'creating docs makes the range dirty', + ) await db.save() equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') // Link user -> doc db.update('user', users[1], { docs: [doc2] }) await db.drain() - equal(await countDirtyBlocks(db.server), 2, 'Linking a user to doc makes both dirty') + equal( + await countDirtyBlocks(db.server), + 2, + 'Linking a user to doc makes both dirty', + ) await db.save() equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') // Link doc -> user db.update('doc', doc3, { creator: users[2] }) await db.drain() - equal(await countDirtyBlocks(db.server), 2, 'Linking a doc to user makes both dirty') + equal( + await countDirtyBlocks(db.server), + 2, + 'Linking a doc to user makes both dirty', + ) await db.save() equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') }) From 8f4db6202816839d6fe080a96427a66789f9ba92 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 08:59:19 +0100 Subject: [PATCH 132/449] add fixed string --- src/db-client/query2/index.ts | 23 ++++---- src/schema/defs/props/separate.ts | 88 ++++++++++++++++++++++--------- src/schema/schema/schema.ts | 2 +- test/modify/props/default.ts | 2 +- test/query/ast.ts | 4 +- test/shared/index.ts | 2 +- 6 files changed, 79 insertions(+), 42 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index a62199280c..63975e613a 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,5 +1,5 @@ import type { QueryAst } from '../../db-query/ast/ast.js' -import type { InferSchemaOutput, PickOutput, ResolveInclude } from './types.js' +import type { PickOutput, ResolveInclude } from './types.js' import type { ResolvedProps } from '../../schema/index.js' export class BasedQuery2< @@ -44,20 +44,19 @@ export class BasedQuery2< SourceField > { for (const prop of props) { - if (typeof prop === 'function') { - const nestedQuery = (prop as any)( - (field: any) => new BasedQuery2(field), - ) - // TODO: Merge nested AST - continue - } - const path = (prop as string).split('.') let target = this.ast - for (const key of path) { + if (typeof prop === 'function') { + const { ast } = (prop as any)((field: any) => new BasedQuery2(field)) target.props ??= {} - target = target.props[key] = {} + target.props[ast.type] = ast + } else { + const path = (prop as string).split('.') + for (const key of path) { + target.props ??= {} + target = target.props[key] = {} + } + target.include = {} } - target.include = {} } return this as any } diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 823c8fa888..9998ba476d 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -43,58 +43,94 @@ const validateMaxBytes = ( } } +function validateString( + value: unknown, + prop: { min?: number; max?: number }, + path: string[], +): asserts value is string { + if (typeof value !== 'string') { + throw new Error('Invalid type for string ' + path.join('.')) + } + if (prop.min !== undefined && value.length < prop.min) { + throw new Error( + `Length ${value.length} is smaller than min ${prop.min} for ${path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value.length > prop.max) { + throw new Error( + `Length ${value.length} is larger than max ${prop.max} for ${path.join( + '.', + )}`, + ) + } +} + export const string = class String extends BasePropDef { constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { super(prop, path, typeDef) - // TODO! - // if (prop.maxBytes && prop.maxBytes < 61) { - // this.size = prop.maxBytes + 1 - // } else if (prop.max && prop.max < 31) { - // this.size = prop.max * 2 + 1 - // } - // if (this.size) { - // this.type = PropType.stringFixed - // this.pushValue = this.pushFixedValue as any - // } + if (prop.maxBytes && prop.maxBytes < 61) { + this.size = prop.maxBytes + 1 + } else if (prop.max && prop.max < 31) { + this.size = prop.max * 2 + 1 + } + if (this.size) { + this.type = PropType.stringFixed + this.pushValue = this.pushFixedValue + } } declare schema: SchemaString override type: PropTypeEnum = PropType.string - override pushValue( - buf: AutoSizedUint8Array, - val: unknown, - _op?: ModifyEnum, - lang: LangCodeEnum = LangCode.none, - ): asserts val is string { - if (typeof val !== 'string') { + override validate(value: unknown): asserts value is string { + if (typeof value !== 'string') { throw new Error('Invalid type for string ' + this.path.join('.')) } - const prop = this.schema - if (prop.min !== undefined && val.length < prop.min) { + if (prop.min !== undefined && value.length < prop.min) { throw new Error( - `Length ${val.length} is smaller than min ${prop.min} for ${this.path.join( + `Length ${value.length} is smaller than min ${prop.min} for ${this.path.join( '.', )}`, ) } - if (prop.max !== undefined && val.length > prop.max) { + if (prop.max !== undefined && value.length > prop.max) { throw new Error( - `Length ${val.length} is larger than max ${prop.max} for ${this.path.join( + `Length ${value.length} is larger than max ${prop.max} for ${this.path.join( '.', )}`, ) } - - const normalized = val.normalize('NFKD') + } + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + _op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, + ): asserts value is string { + validateString(value, this.schema, this.path) + const normalized = value.normalize('NFKD') buf.pushUint8(lang) buf.pushUint8(NOT_COMPRESSED) const written = buf.pushString(normalized) - validateMaxBytes(written, prop, this.path) + validateMaxBytes(written, this.schema, this.path) const crc = native.crc32(buf.subarray(buf.length - written)) buf.pushUint32(crc) } - // pushFixedValue(buf: AutoSizedUint8Array, val: string, lang: LangCodeEnum) {} + pushFixedValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is string { + validateString(value, this.schema, this.path) + const size = native.stringByteLength(value) + validateMaxBytes(size, this.schema, this.path) + buf.pushUint8(size) + buf.pushString(value) + if (size < this.size) { + buf.fill(0, buf.length, buf.length + this.size - size) + } + } override pushSelvaSchema(buf: AutoSizedUint8Array) { const index = pushSelvaSchemaString(buf, { diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index 0d73fb0538..f12e53a6f0 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -316,7 +316,7 @@ export const parseSchema = ( // TODO we can remove hash from here after we finish new schema defs (internal schema) result.hash = hash(result) - return result as unknown as ResolveSchema + return result as ResolveSchema } catch (e) { if (tracking) { e = Error(`${path.join('.')}: ${inspect(value)} - ${e}`, { cause: e }) diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index 11e8ddedc0..83e9f9bc0d 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -13,7 +13,7 @@ await test('modify - default values basic', async (t) => { myEnum: { enum: ['a', 'b'], default: 'a' }, myJson: { type: 'json', default: { foo: 'bar' } }, myText: { type: 'text', default: { en: 'Hello' } }, - myTs: { type: 'timestamp', default: 1000 }, + myTs: { type: 'timestamp', default: '1000' }, }, }, }) diff --git a/test/query/ast.ts b/test/query/ast.ts index b92fac906a..9da482491a 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -28,5 +28,7 @@ await test('query types', async (t) => { const result = await query.get() - result[0].otherUsers + // result[0].otherUsers + + console.dir(query.ast, { depth: null }) }) diff --git a/test/shared/index.ts b/test/shared/index.ts index 46ef490c75..76244d1b56 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -44,5 +44,5 @@ export const testDb = async ( const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) t.after(() => db.destroy()) - return db.setSchema(schema) as unknown as Promise>> + return db.setSchema(schema) as unknown as DbClient> } From 5ec47b05d2a23cc595aff67cf7a41de0b121d0e1 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 09:07:42 +0100 Subject: [PATCH 133/449] added fixed binary --- src/schema/defs/props/separate.ts | 56 ++++++++++++++----------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 9998ba476d..f2ae242e51 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -82,26 +82,6 @@ export const string = class String extends BasePropDef { } declare schema: SchemaString override type: PropTypeEnum = PropType.string - override validate(value: unknown): asserts value is string { - if (typeof value !== 'string') { - throw new Error('Invalid type for string ' + this.path.join('.')) - } - const prop = this.schema - if (prop.min !== undefined && value.length < prop.min) { - throw new Error( - `Length ${value.length} is smaller than min ${prop.min} for ${this.path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value.length > prop.max) { - throw new Error( - `Length ${value.length} is larger than max ${prop.max} for ${this.path.join( - '.', - )}`, - ) - } - } override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -205,32 +185,48 @@ export const json = class Json extends string { op?: ModifyEnum, lang: LangCodeEnum = LangCode.none, ) { - if (value === undefined) { - throw new Error('Invalid undefined value for json ' + this.path.join('.')) - } super.pushValue(buf, JSON.stringify(value), op, lang) } } export const binary = class Binary extends BasePropDef { - override type = PropType.binary + constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) + if (prop.maxBytes && prop.maxBytes < 61) { + this.size = prop.maxBytes + 1 + } + if (this.size) { + this.type = PropType.binaryFixed + this.pushValue = this.pushFixedValue + } + } + override type: PropTypeEnum = PropType.binary declare schema: SchemaBinary - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - ): asserts value is Uint8Array { + override validate(value: unknown): asserts value is Uint8Array { if (!(value instanceof Uint8Array)) { throw new Error('Invalid type for binary ' + this.path.join('.')) } - validateMaxBytes(value.byteLength, this.schema, this.path) - + } + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is Uint8Array { + this.validate(value) const crc = native.crc32(value) buf.pushUint8(LangCode.none) buf.pushUint8(NOT_COMPRESSED) buf.set(value, buf.length) buf.pushUint32(crc) } + pushFixedValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is Uint8Array { + this.validate(value) + buf.pushUint8(value.byteLength) + buf.set(value, buf.length) + } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, From fac9284bc8bf342389fea59bfb03a5702080ea3c Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 10:36:17 +0100 Subject: [PATCH 134/449] fixed string and query2 --- native/modify/modify.zig | 6 +-- native/types.zig | 3 +- src/db-client/modify/props.ts | 1 + src/db-client/query2/index.ts | 84 ++++++++++++++++++++++--------- src/schema/defs/props/separate.ts | 5 +- src/zigTsExports.ts | 35 ++++++++----- test/modify/props/default.ts | 2 +- test/modify/props/string.ts | 62 +++++++++++++++++++++++ test/modify/props/timestamp.ts | 2 +- test/modify/upsert.ts | 13 +++++ test/save/save.ts | 1 - 11 files changed, 167 insertions(+), 47 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index f69715dac3..dd9a7bed46 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -51,12 +51,12 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 while (j < data.len) { const propId = data[j]; const propSchema = try Schema.getFieldSchema(typeEntry, propId); + if (propId == 0) { // main handling const main = utils.readNext(t.ModifyMainHeader, data, &j); const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); - const size = main.type.size(); - const value = data[j .. j + size]; + const value = data[j .. j + main.size]; if (main.increment != .none) { switch (main.type) { .number => applyInc(f64, current, value, main.start, main.increment), @@ -68,7 +68,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 } } utils.copy(u8, current, value, main.start); - j += size; + j += main.size; } else { // separate handling const prop = utils.readNext(t.ModifyPropHeader, data, &j); diff --git a/native/types.zig b/native/types.zig index 3853560e94..22feadcd8e 100644 --- a/native/types.zig +++ b/native/types.zig @@ -124,8 +124,9 @@ pub const ModifyIncrement = enum(u8) { pub const ModifyMainHeader = packed struct { id: u8, type: PropType, - start: u16, increment: ModifyIncrement, + size: u8, + start: u16, }; pub const ModifyPropHeader = packed struct { diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index cda901c88e..fc72e7b4cb 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -41,6 +41,7 @@ export const serializeProps = ( id: 0, start: prop.start, type: prop.type, + size: prop.size, increment: increment ? increment < 0 ? ModifyIncrement.decrement diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 63975e613a..488978f559 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -2,7 +2,7 @@ import type { QueryAst } from '../../db-query/ast/ast.js' import type { PickOutput, ResolveInclude } from './types.js' import type { ResolvedProps } from '../../schema/index.js' -export class BasedQuery2< +class QueryBranch< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, K extends @@ -12,41 +12,40 @@ export class BasedQuery2< | { field: any; select: any } = '*', IsSingle extends boolean = false, SourceField extends string | number | symbol | undefined = undefined, + IsRoot extends boolean = false, > { - constructor(type: T, target?: number) { - this.ast.type = type as string - this.ast.target = target - } - - ast: QueryAst = {} - async get(): Promise< - IsSingle extends true - ? PickOutput, K>> - : PickOutput, K>>[] - > { - return [] as any + constructor(ast: QueryAst) { + this.ast = ast } - + ast: QueryAst include< F extends ( | keyof ResolvedProps | '*' | '**' - | ((q: SelectFn) => BasedQuery2) + | ((q: SelectFn) => QueryBranch) )[], >( ...props: F - ): BasedQuery2< - S, - T, - (K extends '*' ? never : K) | ResolveIncludeArgs, - IsSingle, - SourceField - > { + ): IsRoot extends true + ? BasedQuery2< + S, + T, + (K extends '*' ? never : K) | ResolveIncludeArgs, + IsSingle + > + : QueryBranch< + S, + T, + (K extends '*' ? never : K) | ResolveIncludeArgs, + IsSingle, + SourceField, + IsRoot + > { for (const prop of props) { let target = this.ast if (typeof prop === 'function') { - const { ast } = (prop as any)((field: any) => new BasedQuery2(field)) + const { ast } = (prop as any)((field: any) => new QueryBranch(field)) target.props ??= {} target.props[ast.type] = ast } else { @@ -60,13 +59,41 @@ export class BasedQuery2< } return this as any } + + filter(...args: any[]): this { + return this + } +} + +export class BasedQuery2< + S extends { types: any } = { types: any }, + T extends keyof S['types'] = any, + K extends + | keyof ResolvedProps + | '*' + | '**' + | { field: any; select: any } = '*', + IsSingle extends boolean = false, +> extends QueryBranch { + constructor(type: T, target?: number) { + super({}) + this.ast.type = type as string + this.ast.target = target + } + async get(): Promise< + IsSingle extends true + ? PickOutput, K>> + : PickOutput, K>>[] + > { + return [] as any + } } type SelectFn = < P extends keyof ResolvedProps, >( field: P, -) => BasedQuery2< +) => QueryBranch< S, ResolvedProps[P] extends { ref: infer R extends string } ? R @@ -82,6 +109,13 @@ type SelectFn = < export type ResolveIncludeArgs = T extends ( q: any, -) => BasedQuery2 +) => QueryBranch< + infer S, + infer T, + infer K, + infer Single, + infer SourceField, + any +> ? { field: SourceField; select: K } : T diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index f2ae242e51..0fed1dc7e0 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -107,8 +107,9 @@ export const string = class String extends BasePropDef { validateMaxBytes(size, this.schema, this.path) buf.pushUint8(size) buf.pushString(value) - if (size < this.size) { - buf.fill(0, buf.length, buf.length + this.size - size) + const padEnd = this.size - size - 1 + if (padEnd) { + buf.fill(0, buf.length, buf.length + padEnd) } } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index f6f0110e20..54239d1665 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -571,11 +571,12 @@ export type ModifyIncrementEnum = (typeof ModifyIncrement)[keyof typeof ModifyIn export type ModifyMainHeader = { id: number type: PropTypeEnum - start: number increment: ModifyIncrementEnum + size: number + start: number } -export const ModifyMainHeaderByteSize = 5 +export const ModifyMainHeaderByteSize = 6 export const ModifyMainHeaderAlignOf = 8 @@ -588,10 +589,12 @@ export const writeModifyMainHeader = ( offset += 1 buf[offset] = Number(header.type) offset += 1 - writeUint16(buf, Number(header.start), offset) - offset += 2 buf[offset] = Number(header.increment) offset += 1 + buf[offset] = Number(header.size) + offset += 1 + writeUint16(buf, Number(header.start), offset) + offset += 2 return offset } @@ -602,11 +605,14 @@ export const writeModifyMainHeaderProps = { type: (buf: Uint8Array, value: PropTypeEnum, offset: number) => { buf[offset + 1] = Number(value) }, - start: (buf: Uint8Array, value: number, offset: number) => { - writeUint16(buf, Number(value), offset + 2) - }, increment: (buf: Uint8Array, value: ModifyIncrementEnum, offset: number) => { - buf[offset + 4] = Number(value) + buf[offset + 2] = Number(value) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 3] = Number(value) + }, + start: (buf: Uint8Array, value: number, offset: number) => { + writeUint16(buf, Number(value), offset + 4) }, } @@ -617,8 +623,9 @@ export const readModifyMainHeader = ( const value: ModifyMainHeader = { id: buf[offset], type: (buf[offset + 1]) as PropTypeEnum, - start: readUint16(buf, offset + 2), - increment: (buf[offset + 4]) as ModifyIncrementEnum, + increment: (buf[offset + 2]) as ModifyIncrementEnum, + size: buf[offset + 3], + start: readUint16(buf, offset + 4), } return value } @@ -626,8 +633,9 @@ export const readModifyMainHeader = ( export const readModifyMainHeaderProps = { id: (buf: Uint8Array, offset: number) => buf[offset], type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), - increment: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyIncrementEnum, + increment: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as ModifyIncrementEnum, + size: (buf: Uint8Array, offset: number) => buf[offset + 3], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), } export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { @@ -643,8 +651,9 @@ export const pushModifyMainHeader = ( const index = buf.length buf.pushUint8(Number(header.id)) buf.pushUint8(Number(header.type)) - buf.pushUint16(Number(header.start)) buf.pushUint8(Number(header.increment)) + buf.pushUint8(Number(header.size)) + buf.pushUint16(Number(header.start)) return index } diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index 83e9f9bc0d..11e8ddedc0 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -13,7 +13,7 @@ await test('modify - default values basic', async (t) => { myEnum: { enum: ['a', 'b'], default: 'a' }, myJson: { type: 'json', default: { foo: 'bar' } }, myText: { type: 'text', default: { en: 'Hello' } }, - myTs: { type: 'timestamp', default: '1000' }, + myTs: { type: 'timestamp', default: 1000 }, }, }, }) diff --git a/test/modify/props/string.ts b/test/modify/props/string.ts index 0ffa531870..ff83361232 100644 --- a/test/modify/props/string.ts +++ b/test/modify/props/string.ts @@ -191,3 +191,65 @@ await test('modify string on edge', async (t) => { .toObject() deepEqual(res6.toThing?.$edgeName, s6) }) + +await test('modify fixed string', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: { + type: 'string', + max: 16, + }, + }, + }, + }) + + // Basic string + const s1 = 'hello' + const id1 = await db.create('thing', { + name: s1, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s1, + }) + + // Update to another string + const s2 = 'world' + await db.update('thing', id1, { + name: s2, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s2, + }) + + // String with spaces + const s3 = 'foo bar' + await db.update('thing', id1, { + name: s3, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s3, + }) + + // Max length string + const s4 = 'a'.repeat(16) + await db.update('thing', id1, { + name: s4, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s4, + }) + + // Delete + await db.update('thing', id1, { + name: null, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: '', + }) +}) diff --git a/test/modify/props/timestamp.ts b/test/modify/props/timestamp.ts index 7dd604735f..4cd46930a5 100644 --- a/test/modify/props/timestamp.ts +++ b/test/modify/props/timestamp.ts @@ -97,7 +97,7 @@ await test('modify timestamp', async (t) => { // Delete await db.update('event', id1, { ts: null }) - deepEqual((await db.query('event', id1).get()).ts, undefined) + deepEqual((await db.query('event', id1).get().toObject()).ts, undefined) }) await test('modify timestamp on edge', async (t) => { diff --git a/test/modify/upsert.ts b/test/modify/upsert.ts index 1a38c6bc37..89d4191e5d 100644 --- a/test/modify/upsert.ts +++ b/test/modify/upsert.ts @@ -72,4 +72,17 @@ await test('upsert', async (t) => { email: 'bla@bla.com', isNice: false, }) + + const id5 = await db.create('user', { + email: 'newkid@block.com', + isNice: true, + }) + + const res5 = await db.upsert( + 'user', + { email: 'newkid@block.com' }, + { isNice: false }, + ) + + deepEqual(id5, res5) }) diff --git a/test/save/save.ts b/test/save/save.ts index 54aac5e09e..1313adadba 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -430,7 +430,6 @@ await test('upsert', async (t) => { }, }, }) - const joe = db.create('person', { name: 'Joe', alias: 'boss', From 85b0bd1db8449d73393377368955e60d74473db9 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 10:47:13 +0100 Subject: [PATCH 135/449] add mixed props test --- test/modify/props/mixed.ts | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 test/modify/props/mixed.ts diff --git a/test/modify/props/mixed.ts b/test/modify/props/mixed.ts new file mode 100644 index 0000000000..4bf37b6774 --- /dev/null +++ b/test/modify/props/mixed.ts @@ -0,0 +1,72 @@ +import { deepEqual } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('mixed props', async (t) => { + const db = await testDb(t, { + types: { + user: { + props: { + name: { type: 'string' }, + email: { type: 'string' }, + age: { type: 'uint32' }, + story: { type: 'string' }, + alias: { type: 'alias' }, + }, + }, + typeTest: { + props: { + q: { type: 'reference', ref: 'user', prop: 'test' }, + r: { type: 'enum', enum: ['a', 'b', 'c'] }, + }, + }, + }, + }) + + const u = await db.create('user', { + name: 'T', + email: 't@t.com', + age: 33, + story: 'hello', + alias: 't', + }) + + const t1 = await db.create('typeTest', { + q: u, + r: 'a', + }) + + const typeTest = await db + .query('typeTest') + .include('*', '**') + .get() + .toObject() + const user = await db.query('user').include('*', '**').get().toObject() + + deepEqual(typeTest, [ + { + id: 1, + r: 'a', + q: { + id: 1, + age: 33, + name: 'T', + email: 't@t.com', + story: 'hello', + alias: 't', + }, + }, + ]) + + deepEqual(user, [ + { + id: 1, + age: 33, + name: 'T', + email: 't@t.com', + story: 'hello', + alias: 't', + test: [{ id: 1, r: 'a' }], + }, + ]) +}) From a6c6cec698544711332319d001318fac76343f37 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 10:05:19 +0100 Subject: [PATCH 136/449] Inlcude typeName in TypeDef --- src/db-server/schema.ts | 11 ++++++----- src/schema/defs/getTypeDefs.ts | 2 ++ src/schema/defs/index.ts | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index fe80708ea0..2dd85213b4 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -22,6 +22,7 @@ import { import { SCHEMA_FILE } from '../index.js' import { getTypeDefs, propIndexOffset } from '../schema/defs/getTypeDefs.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' +import { TypeDef } from '../schema/defs/index.js' const schemaOpId = idGenerator() @@ -65,20 +66,20 @@ export const writeSchemaFile = async (server: DbServer, schema: SchemaOut) => { export async function createSelvaType( server: DbServer, - typeId: number, + typeDef: TypeDef, schema: Uint8Array, ): Promise { const msg = new Uint8Array(5 + schema.byteLength) - writeUint32(msg, typeId, 0) + writeUint32(msg, typeDef.id, 0) msg[4] = OpType.createType msg.set(schema, 5) return new Promise((resolve, reject) => { - server.addOpOnceListener(OpType.createType, typeId, (buf: Uint8Array) => { + server.addOpOnceListener(OpType.createType, typeDef.id, (buf: Uint8Array) => { const err = readUint32(buf, 0) if (err) { - const errMsg = `Create type ${typeId} failed: ${native.selvaStrerror(err)}` + const errMsg = `Create type '${typeDef.name}' (${typeDef.id}) failed: ${native.selvaStrerror(err)}` server.emit('error', errMsg) reject(new Error(errMsg)) } else { @@ -151,7 +152,7 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { prop.pushSelvaSchema(buf) } - return createSelvaType(server, typeDef.id, buf.view) + return createSelvaType(server, typeDef, buf.view) }), ) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index b0ff788b3a..56777977a5 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -69,6 +69,7 @@ const getTypeDef = (schema: SchemaType): TypeDef => { const { props } = schema const typeDef: TypeDef = { id: 0, + name: '', separate: [], props: new Map(), main: [], @@ -116,6 +117,7 @@ export const getTypeDefs = (schema: SchemaOut): Map => { let typeId = 1 for (const [typeName, typeDef] of typeDefs) { typeDef.id = typeId++ + typeDef.name = typeName for (const [propPath, def] of typeDef.props) { const prop = def.schema.type === 'references' ? def.schema.items : def.schema diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 2482d7dbc9..149d8f762d 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -13,6 +13,7 @@ export type PropTree = Map export type TypeDef = { id: number + name: string main: PropDef[] separate: PropDef[] props: Map From 8e6ad961a43b9d200ccc8d6a155668affceaa497 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 10:54:28 +0100 Subject: [PATCH 137/449] lang fixy --- test/save/save.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/save/save.ts b/test/save/save.ts index 1313adadba..dc510424b0 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -12,20 +12,20 @@ await test('simple', async (t) => { await db.setSchema({ locales: { - en: { required: true }, - fr: { required: true }, - nl: { required: true }, - el: { required: true }, - he: { required: true }, - it: { required: true }, - lv: { required: true }, - lb: { required: true }, - ro: { required: true }, - sl: { required: true }, - es: { required: true }, - de: { required: true }, - cs: { required: true }, - et: { required: true }, + en: {}, + fr: {}, + nl: {}, + el: {}, + he: {}, + it: {}, + lv: {}, + lb: {}, + ro: {}, + sl: {}, + es: {}, + de: {}, + cs: {}, + et: {}, }, types: { user: { From d5a58459d2671a1127d88a5b45667c2c80cda18d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 10:54:37 +0100 Subject: [PATCH 138/449] broky --- test/save/save.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/save/save.ts b/test/save/save.ts index dc510424b0..79513cd17b 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -34,31 +34,31 @@ await test('simple', async (t) => { email: { type: 'string' }, age: { type: 'uint32' }, story: { type: 'string' }, - test: { ref: 'typeTest', prop: 'q' }, + //test: { ref: 'typeTest', prop: 'q' }, alias: { type: 'alias' }, }, }, typeTest: { props: { - a: { type: 'string' }, - b: { type: 'number' }, - c: { type: 'boolean' }, + //a: { type: 'string' }, + //b: { type: 'number' }, + //c: { type: 'boolean' }, //d: { type: 'object' }, - e: { type: 'timestamp' }, - f: { type: 'binary' }, - g: { type: 'alias' }, - h: { type: 'text' }, - i: { type: 'json' }, - j: { type: 'cardinality' }, - k: { type: 'int8' }, - l: { type: 'int16' }, - m: { type: 'uint16' }, - n: { type: 'int32' }, - o: { type: 'uint32' }, + //e: { type: 'timestamp' }, + //f: { type: 'binary' }, + //g: { type: 'alias' }, + //h: { type: 'text' }, + //i: { type: 'json' }, + //j: { type: 'cardinality' }, + //k: { type: 'int8' }, + //l: { type: 'int16' }, + //m: { type: 'uint16' }, + //n: { type: 'int32' }, + //o: { type: 'uint32' }, //p: { type: 'references', ref: 'typeTest', prop: 'reference' }, q: { type: 'reference', ref: 'user', prop: 'test' }, r: { type: 'enum', enum: ['a', 'b', 'c'] }, - s: { type: 'vector', size: 1, baseType: 'float32' }, + //s: { type: 'vector', size: 1, baseType: 'float32' }, //t: { type: 'set' }, }, }, From b4f09ed2387e141fbca62cf388c1746ca72095d0 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 11:06:47 +0100 Subject: [PATCH 139/449] fix enum in deserialize schema --- src/schema/serialize.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/schema/serialize.ts b/src/schema/serialize.ts index f6a5e62ac7..01d3ed84da 100644 --- a/src/schema/serialize.ts +++ b/src/schema/serialize.ts @@ -468,10 +468,7 @@ export const deSerializeInner = ( if (isSchemaProp) { const type = buf[i] - const parsedType = PropTypeInverse[type] - if (type !== PropType.enum) { - obj.type = parsedType - } + obj.type = PropTypeInverse[type] i += 1 } From 937a9373b3b34dba305240340a1f81a9e718ae04 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 11:07:57 +0100 Subject: [PATCH 140/449] This is an Error --- src/schema/defs/getTypeDefs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 56777977a5..0acc43c275 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -53,7 +53,7 @@ const addPropDef = ( ) => { const Def = defs[prop.type] if (!Def) { - throw 'unknown def' + throw new Error('Unknown def') } const def: PropDef = new Def(prop, path, typeDef) From 1612214cfebcd832a8d44c11259000fd07786fde Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 11:08:30 +0100 Subject: [PATCH 141/449] Revert "broky" This reverts commit d5a58459d2671a1127d88a5b45667c2c80cda18d. --- test/save/save.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/save/save.ts b/test/save/save.ts index 79513cd17b..dc510424b0 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -34,31 +34,31 @@ await test('simple', async (t) => { email: { type: 'string' }, age: { type: 'uint32' }, story: { type: 'string' }, - //test: { ref: 'typeTest', prop: 'q' }, + test: { ref: 'typeTest', prop: 'q' }, alias: { type: 'alias' }, }, }, typeTest: { props: { - //a: { type: 'string' }, - //b: { type: 'number' }, - //c: { type: 'boolean' }, + a: { type: 'string' }, + b: { type: 'number' }, + c: { type: 'boolean' }, //d: { type: 'object' }, - //e: { type: 'timestamp' }, - //f: { type: 'binary' }, - //g: { type: 'alias' }, - //h: { type: 'text' }, - //i: { type: 'json' }, - //j: { type: 'cardinality' }, - //k: { type: 'int8' }, - //l: { type: 'int16' }, - //m: { type: 'uint16' }, - //n: { type: 'int32' }, - //o: { type: 'uint32' }, + e: { type: 'timestamp' }, + f: { type: 'binary' }, + g: { type: 'alias' }, + h: { type: 'text' }, + i: { type: 'json' }, + j: { type: 'cardinality' }, + k: { type: 'int8' }, + l: { type: 'int16' }, + m: { type: 'uint16' }, + n: { type: 'int32' }, + o: { type: 'uint32' }, //p: { type: 'references', ref: 'typeTest', prop: 'reference' }, q: { type: 'reference', ref: 'user', prop: 'test' }, r: { type: 'enum', enum: ['a', 'b', 'c'] }, - //s: { type: 'vector', size: 1, baseType: 'float32' }, + s: { type: 'vector', size: 1, baseType: 'float32' }, //t: { type: 'set' }, }, }, From 4b744106c37cbafefedbce2e73882de397522db3 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 11:36:50 +0100 Subject: [PATCH 142/449] Make some block test utils shared --- test/save/saveRange.ts | 45 ++--------------------------------------- test/shared/index.ts | 46 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/test/save/saveRange.ts b/test/save/saveRange.ts index 8fc382382b..78c979d8b3 100644 --- a/test/save/saveRange.ts +++ b/test/save/saveRange.ts @@ -1,37 +1,9 @@ -import { createHash } from 'node:crypto' import { readdir } from 'node:fs/promises' -import { BasedDb, DbServer } from '../../src/index.js' +import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { italy } from '../shared/examples.js' import { deepEqual, equal, notEqual } from '../shared/assert.js' -import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' - -const getActiveBlocks = async ( - db: DbServer, - tc: number, -): Promise> => - (await getBlockStatuses(db, tc)).reduce((acc, cur, i) => { - if (cur) { - acc.push(i) - } - return acc - }, [] as Array) -const block2start = (block: number, capacity: number): number => - block * capacity + 1 -const hashType = async (db: DbServer, typeName: string): Promise => { - const tc = db.schemaTypesParsed[typeName].id - const capacity = db.schemaTypesParsed[typeName].blockCapacity - const hash = createHash('sha256') - const bhs = await Promise.all( - (await getActiveBlocks(db, tc)).map((block) => - getBlockHash(db, tc, block2start(block, capacity)), - ), - ) - for (const bh of bhs) { - hash.update(bh) - } - return hash.digest('hex') -} +import { countDirtyBlocks, hashType } from '../shared/index.js' await test('save simple range', async (t) => { const db = new BasedDb({ @@ -195,19 +167,6 @@ await test('save simple range', async (t) => { ) }) -async function countDirtyBlocks(server: DbServer) { - let n = 0 - - for (const t of Object.keys(server.schemaTypesParsedById)) { - n += (await getBlockStatuses(server, Number(t))).reduce( - (acc, cur) => acc + ~~!!(cur & 0x4), - 0, - ) - } - - return n -} - await test('reference changes', async (t) => { const db = new BasedDb({ path: t.tmp, diff --git a/test/shared/index.ts b/test/shared/index.ts index 76244d1b56..e1394657a1 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,9 +1,11 @@ +import { createHash } from 'node:crypto' +import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' import type { ResolveSchema, SchemaIn, ValidateSchema, } from '../../src/schema.js' -import { BasedDb, type DbClient } from '../../src/sdk.js' +import { BasedDb, DbServer, type DbClient } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' export * from './examples.js' @@ -46,3 +48,45 @@ export const testDb = async ( t.after(() => db.destroy()) return db.setSchema(schema) as unknown as DbClient> } + +export async function countDirtyBlocks(server: DbServer) { + let n = 0 + + for (const t of Object.keys(server.schemaTypesParsedById)) { + n += (await getBlockStatuses(server, Number(t))).reduce( + (acc, cur) => acc + ~~!!(cur & 0x4), + 0, + ) + } + + return n +} + +export const getActiveBlocks = async ( + db: DbServer, + tc: number, +): Promise> => + (await getBlockStatuses(db, tc)).reduce((acc, cur, i) => { + if (cur) { + acc.push(i) + } + return acc + }, [] as Array) + +const block2start = (block: number, capacity: number): number => + block * capacity + 1 + +export const hashType = async (db: DbServer, typeName: string): Promise => { + const tc = db.schemaTypesParsed[typeName].id + const capacity = db.schemaTypesParsed[typeName].blockCapacity + const hash = createHash('sha256') + const bhs = await Promise.all( + (await getActiveBlocks(db, tc)).map((block) => + getBlockHash(db, tc, block2start(block, capacity)), + ), + ) + for (const bh of bhs) { + hash.update(bh) + } + return hash.digest('hex') +} From e1d08d5ab24e1a234ba9577b8ee77afcd1b6e2eb Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 11:40:46 +0100 Subject: [PATCH 143/449] Add back dirty blocks check to edgeType test FDN-1850 --- test/edges/edgeType.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/edges/edgeType.ts b/test/edges/edgeType.ts index 54e1cfc6e9..8172354034 100644 --- a/test/edges/edgeType.ts +++ b/test/edges/edgeType.ts @@ -1,6 +1,7 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual, equal } from '../shared/assert.js' +import { countDirtyBlocks } from '../shared/index.js' await test('single reference', async (t) => { const db = new BasedDb({ @@ -49,6 +50,8 @@ await test('single reference', async (t) => { $note: 'funny', }, }) + + deepEqual(await countDirtyBlocks(db.server), 3) }) await test('json type edge', async (t) => { From d9fb398932aa3172f96581ff168d8d74ef1ff63e Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Wed, 4 Feb 2026 10:40:30 -0300 Subject: [PATCH 144/449] adding to agg ast to readerSchema --- src/db-query/ast/aggregates.ts | 14 ++++++++++++++ test/query-ast/aggregates.ts | 9 ++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index 3a357fb66a..d794244c61 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -11,6 +11,7 @@ import { import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' import { aggregateTypeMap } from '../../db-client/query/aggregates/types.js' +import { readPropDef } from './readSchema.js' export const pushAggregatesQuery = ( ast: QueryAst, @@ -19,6 +20,10 @@ export const pushAggregatesQuery = ( ) => { const headerStartPos = ctx.query.length ctx.query.length += AggHeaderByteSize + ctx.readSchema.aggregate = { + aggregates: [], + totalResultsSize: 0, + } let filterSize = 0 if (ast.filter) { @@ -131,6 +136,7 @@ const pushAggregates = ( continue } + let i = 0 for (const propName of props) { const propDef = typeDef.props.get(propName) if (!propDef) { @@ -156,6 +162,14 @@ const pushAggregates = ( resultPos: sizes.result, accumulatorPos: sizes.accumulator, }) + ctx.readSchema.aggregate?.aggregates.push({ + path: propDef.path!, + type: fn, + resultPos: sizes.result, + }) + ctx.readSchema.main.props[i] = readPropDef(propDef, ctx.locales) + ctx.readSchema.main.len += propDef.size + i += propDef.size ctx.query.data.set(buffer, ctx.query.length) ctx.query.length += AggPropByteSize diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index b5847aec30..cb049daaea 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -31,6 +31,7 @@ await test('aggregate', async (t) => { age: 30, }) + await db.drain() const ast: QueryAst = { type: 'user', // props: { @@ -44,18 +45,16 @@ await test('aggregate', async (t) => { const result = await db.server.getQueryBuf(ctx.query) debugBuffer(result) - const readSchemaBuf = serializeReaderSchema(ctx.readSchema) + const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) console.dir(obj, { depth: 10 }) - console.log( - JSON.stringify(obj).length, - readSchemaBuf.byteLength + result.byteLength, - ) + console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + const r = await db.query('user').sum('age').get() r.debug() r.inspect(10) From 51aa7d09791ee3b3860151d5958f74c900a33c7b Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 15:04:35 +0100 Subject: [PATCH 145/449] Also test deleting node 0 --- test/delete.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/delete.ts b/test/delete.ts index af9dcf583e..623b0565bc 100644 --- a/test/delete.ts +++ b/test/delete.ts @@ -170,13 +170,10 @@ await test('non existing 2', async (t) => { }, ]) - // this can be handled in js throws(() => db.delete('nurp', 213123123)) - throws(() => db.delete('user', simple)) - - // this has to be ignored in C throws(() => db.delete('user', simple)) + throws(() => db.delete('user', 0)) }) await test('save', async (t) => { From 840d81f7119037871317e1e8726b63a2b458e727 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 16:21:28 +0100 Subject: [PATCH 146/449] add string compression --- src/db-client/query2/index.ts | 55 ++++++++++++++++---- src/db-client/query2/types.ts | 84 ++++++++++++++++++++++++++++--- src/db-query/ast/ast.ts | 10 ++-- src/schema/defs/props/separate.ts | 22 +++++++- src/utils/AutoSizedUint8Array.ts | 2 +- test/modify/props/string.ts | 44 ++++++++++++++++ test/query/ast.ts | 7 ++- 7 files changed, 197 insertions(+), 27 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 488978f559..61b7716107 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,5 +1,14 @@ -import type { QueryAst } from '../../db-query/ast/ast.js' -import type { PickOutput, ResolveInclude } from './types.js' +import type { FilterLeaf, QueryAst } from '../../db-query/ast/ast.js' +import type { + PickOutput, + ResolveInclude, + InferProp, + Path, + FilterOpts, // FilterOpts + Operator, + ResolveDotPath, + InferPathType, +} from './types.js' import type { ResolvedProps } from '../../schema/index.js' class QueryBranch< @@ -9,7 +18,8 @@ class QueryBranch< | keyof ResolvedProps | '*' | '**' - | { field: any; select: any } = '*', + | { field: any; select: any } + | string = '*', // Allow string for potential dot paths IsSingle extends boolean = false, SourceField extends string | number | symbol | undefined = undefined, IsRoot extends boolean = false, @@ -21,6 +31,7 @@ class QueryBranch< include< F extends ( | keyof ResolvedProps + | Path | '*' | '**' | ((q: SelectFn) => QueryBranch) @@ -42,14 +53,19 @@ class QueryBranch< SourceField, IsRoot > { - for (const prop of props) { + for (const prop of props as (string | Function)[]) { let target = this.ast if (typeof prop === 'function') { - const { ast } = (prop as any)((field: any) => new QueryBranch(field)) - target.props ??= {} - target.props[ast.type] = ast + prop((prop: string) => { + const path = prop.split('.') + for (const key of path) { + target.props ??= {} + target = target.props[key] = {} + } + return new QueryBranch(target) + }) } else { - const path = (prop as string).split('.') + const path = prop.split('.') for (const key of path) { target.props ??= {} target = target.props[key] = {} @@ -60,7 +76,20 @@ class QueryBranch< return this as any } - filter(...args: any[]): this { + filter

| Path>( + prop: P, + op: Operator, + val: InferPathType, + opts?: FilterOpts, + ): this { + let target: FilterLeaf = (this.ast.filter ??= {}) + const path = (prop as string).split('.') + for (const key of path) { + target.props ??= {} + target = target.props[key] = {} + } + target.ops ??= [] + target.ops.push({ op, val }) return this } } @@ -72,7 +101,8 @@ export class BasedQuery2< | keyof ResolvedProps | '*' | '**' - | { field: any; select: any } = '*', + | { field: any; select: any } + | string = '*', IsSingle extends boolean = false, > extends QueryBranch { constructor(type: T, target?: number) { @@ -107,6 +137,7 @@ type SelectFn = < P > +// ResolveIncludeArgs needs to stay here because it refers to QueryBranch export type ResolveIncludeArgs = T extends ( q: any, ) => QueryBranch< @@ -118,4 +149,6 @@ export type ResolveIncludeArgs = T extends ( any > ? { field: SourceField; select: K } - : T + : T extends string + ? ResolveDotPath + : T diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index ad837ae47e..76c5a390f0 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -41,7 +41,7 @@ type TypeMap = { // Helper to check if Selection is provided (not never/any/unknown default behavior) type IsSelected = [T] extends [never] ? false : true -type InferProp< +export type InferProp< Prop, Types, Locales extends Record = Record, @@ -93,10 +93,7 @@ export type RefKeys = { [K in keyof Props]: IsRefProp extends true ? K : never }[keyof Props] -export type ResolveInclude< - Props, - K extends keyof Props | '*' | '**' | { field: any; select: any }, -> = K extends any +export type ResolveInclude = K extends any ? K extends '*' ? NonRefKeys : K extends '**' @@ -107,7 +104,7 @@ export type ResolveInclude< export type IncludeSelection< S extends { types: any; locales?: any }, T extends keyof S['types'], - K extends keyof ResolvedProps | '*', + K, > = ResolveInclude, K> export type PickOutput< @@ -125,10 +122,81 @@ export type PickOutput< : InferSchemaOutput[P] : InferSchemaOutput[P] } & { - [Item in Extract as Item['field']]: InferProp< - ResolvedProps[Item['field']], + [Item in Extract as Item['field'] & + keyof ResolvedProps]: InferProp< + ResolvedProps[Item['field'] & + keyof ResolvedProps], S['types'], S['locales'] extends Record ? S['locales'] : {}, Item['select'] > } + +export type FilterOpts = { + lowerCase?: boolean + fn?: + | 'dotProduct' + | 'manhattanDistance' + | 'cosineSimilarity' + | 'euclideanDistance' + score?: number +} + +export type Operator = + | '=' + | '<' + | '>' + | '!=' + | '>=' + | '<=' + | '..' + | '!..' + | 'exists' + | '!exists' + | 'like' + | '!like' + | 'includes' + | '!includes' + +type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +export type Path = [ + Depth, +] extends [never] + ? never + : { + [K in keyof ResolvedProps & string]: + | K + | (ResolvedProps[K] extends { ref: infer R extends string } + ? `${K}.${Path}` + : ResolvedProps[K] extends { + items: { ref: infer R extends string } + } + ? `${K}.${Path}` + : never) + }[keyof ResolvedProps & string] + +export type ResolveDotPath = + T extends `${infer Head}.${infer Tail}` + ? { field: Head; select: ResolveDotPath } + : T + +export type InferPathType< + S extends { types: any }, + T extends keyof S['types'], + P, +> = P extends keyof ResolvedProps + ? InferProp[P], S['types']> + : P extends `${infer Head}.${infer Tail}` + ? Head extends keyof ResolvedProps + ? ResolvedProps[Head] extends { + ref: infer R extends string + } + ? InferPathType + : ResolvedProps[Head] extends { + items: { ref: infer R extends string } + } + ? InferPathType + : never + : never + : never diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 1060ae1efe..6c53f395c2 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -34,12 +34,14 @@ export type FilterOp = { opts?: FilterOpts } +export type FilterLeaf = FilterAst & { + ops?: FilterOp[] + select?: { start: number; end: number } +} + export type FilterAst = { props?: { - [key: string]: FilterAst & { - ops?: FilterOp[] - select?: { start: number; end: number } - } + [key: string]: FilterLeaf } or?: FilterAst and?: FilterAst diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 0fed1dc7e0..22c09158c2 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -1,5 +1,5 @@ import native from '../../../native.js' -import { NOT_COMPRESSED } from '../../../protocol/index.js' +import { COMPRESSED, NOT_COMPRESSED } from '../../../protocol/index.js' import type { SchemaBinary, SchemaCardinality, @@ -78,8 +78,11 @@ export const string = class String extends BasePropDef { if (this.size) { this.type = PropType.stringFixed this.pushValue = this.pushFixedValue + } else if (prop.compression === 'none') { + this.deflate = false } } + deflate = true declare schema: SchemaString override type: PropTypeEnum = PropType.string override pushValue( @@ -91,6 +94,23 @@ export const string = class String extends BasePropDef { validateString(value, this.schema, this.path) const normalized = value.normalize('NFKD') buf.pushUint8(lang) + if (this.deflate && normalized.length > 200) { + buf.pushUint8(COMPRESSED) + const sizePos = buf.reserveUint32() + const stringPos = buf.length + const written = buf.pushString(normalized) + buf.pushString(normalized) + const size = native.compress(buf.data, stringPos, written) + if (size !== 0) { + buf.writeUint32(written, sizePos) + buf.length = stringPos + size + validateMaxBytes(size, this.schema, this.path) + const crc = native.crc32(buf.subarray(stringPos)) + buf.pushUint32(crc) + return + } + buf.length = sizePos - 1 + } buf.pushUint8(NOT_COMPRESSED) const written = buf.pushString(normalized) validateMaxBytes(written, this.schema, this.path) diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 480457b703..2de7362d1e 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -50,7 +50,7 @@ export class AutoSizedUint8Array { this.maxLength = maxLength } - private ensure(requiredLength: number): void { + ensure(requiredLength: number): void { const currentLength = this.data.byteLength if (currentLength >= requiredLength) return if (requiredLength > this.maxLength) { diff --git a/test/modify/props/string.ts b/test/modify/props/string.ts index ff83361232..342e5c170b 100644 --- a/test/modify/props/string.ts +++ b/test/modify/props/string.ts @@ -253,3 +253,47 @@ await test('modify fixed string', async (t) => { name: '', }) }) + +await test('modify long string', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: 'string', + }, + longCompressedString: { + name: { + type: 'string', + }, + }, + }, + }) + + // String > 200 chars (triggers compression if enabled) + const s1 = 'a'.repeat(250) + const id1 = await db.create('thing', { + name: s1, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s1, + }) + + // Mixed content string > 200 chars + const s2 = 'abcdefghijklmnopqrstuvwxyz'.repeat(10) // 260 chars + await db.update('thing', id1, { + name: s2, + }) + deepEqual(await db.query('thing', id1).get(), { + id: id1, + name: s2, + }) + + // Test no compression option + const id2 = await db.create('longCompressedString', { + name: s1, + }) + deepEqual(await db.query('longCompressedString', id2).get(), { + id: id2, + name: s1, + }) +}) diff --git a/test/query/ast.ts b/test/query/ast.ts index 9da482491a..98a2196c3c 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -23,8 +23,11 @@ await test('query types', async (t) => { const query = db .query2('user') - .include('isNice', 'name') - .include((select) => select('otherUsers').include('name')) + .include('isNice', 'name', 'otherUsers.name') + .include((select) => + select('otherUsers').include('name').filter('name', '=', 'youzi'), + ) + .filter('otherUsers.name', '=', 'youzi') const result = await query.get() From 5c370f38e333fc8745732255cfe8661b42c227c8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 15:11:21 +0100 Subject: [PATCH 147/449] Fix upserts --- test/references/references.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/references/references.ts b/test/references/references.ts index 9ce87c9d74..42f424238d 100644 --- a/test/references/references.ts +++ b/test/references/references.ts @@ -875,7 +875,7 @@ await test('single ref save and load', async (t) => { const users = [{ email: '1@saulx.com' }, { email: '2@saulx.com' }] for (const user of users) { - await db.upsert('user', user) + await db.upsert('user', { email: user.email }, {}) } await db.stop() @@ -1026,7 +1026,7 @@ await test('reference to a non-existing node', async (t) => { articles: [], }) - const article = await db.create('article') + const article = await db.create('article', {}) deepEqual(article, 1) deepEqual(await db.query('user', mrSnurp).include('**').get(), { From 45ae7bd55d41b2486c2fa7408a6be5b2a513ad35 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 4 Feb 2026 16:25:36 +0100 Subject: [PATCH 148/449] Fix null ref query include response In case the reference is null the query response must still contain the opCode for attempted include but without any data. Otherwise the response looks like include wasn't even attempted, i.e. no null value is shown by the js client. Ref FDN-1869 --- native/query/single.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index 97457836c0..43cc8def56 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -79,20 +79,20 @@ pub fn reference( if (References.getReference(from, fs)) |ref| { const typeEntry = try Node.getType(ctx.db, header.typeId); const n = Node.getNode(typeEntry, ref.dst); + try ctx.thread.query.append(t.ReadOp.reference); + try ctx.thread.query.append(header.prop); + const resultByteSizeIndex = try ctx.thread.query.reserve(4); + const startIndex = ctx.thread.query.index; if (n) |node| { - try ctx.thread.query.append(t.ReadOp.reference); - try ctx.thread.query.append(header.prop); - const resultByteSizeIndex = try ctx.thread.query.reserve(4); - const startIndex = ctx.thread.query.index; try ctx.thread.query.append(ref.dst); const nestedQuery = q[i.* .. i.* + header.includeSize]; try Include.include(node, ctx, nestedQuery, typeEntry); - ctx.thread.query.writeAs( - u32, - @truncate(ctx.thread.query.index - startIndex), - resultByteSizeIndex, - ); } + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - startIndex), + resultByteSizeIndex, + ); } i.* += header.includeSize; } From 7780ca8e8b848b3c41e8f18293eb98338d7c14f3 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 4 Feb 2026 16:31:18 +0100 Subject: [PATCH 149/449] update string compression --- src/schema/defs/props/separate.ts | 3 ++- src/utils/AutoSizedUint8Array.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts index 22c09158c2..311a9285b0 100644 --- a/src/schema/defs/props/separate.ts +++ b/src/schema/defs/props/separate.ts @@ -99,7 +99,8 @@ export const string = class String extends BasePropDef { const sizePos = buf.reserveUint32() const stringPos = buf.length const written = buf.pushString(normalized) - buf.pushString(normalized) + buf.ensure(buf.length + written) + buf.data.copyWithin(buf.length, buf.length - written, buf.length) const size = native.compress(buf.data, stringPos, written) if (size !== 0) { buf.writeUint32(written, sizePos) diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 2de7362d1e..fcce56c07f 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -40,7 +40,7 @@ export class AutoSizedUint8Array { initialCapacity: number = 256, maxLength: number = 1024 * 1024 * 1024, data: Uint8Array = new Uint8Array( - new (ArrayBuffer as any)(initialCapacity, { + new ArrayBuffer(initialCapacity, { maxByteLength: maxLength, }), ), From 5c53a9ea7ae730f3d6126e0d60b2c7af3c7360d0 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Wed, 4 Feb 2026 13:22:06 -0300 Subject: [PATCH 150/449] ast top level count ongoing --- src/db-query/ast/aggregates.ts | 17 +++++++++++- src/db-query/ast/ast.ts | 2 +- test/query-ast/aggregates.ts | 47 +++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index d794244c61..4a15d7a852 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -12,6 +12,7 @@ import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' import { aggregateTypeMap } from '../../db-client/query/aggregates/types.js' import { readPropDef } from './readSchema.js' +import { truncate } from 'node:fs' export const pushAggregatesQuery = ( ast: QueryAst, @@ -78,7 +79,7 @@ const buildAggregateHeader = ( hasGroupBy, resultsSize: sizes.result, accumulatorSize: sizes.accumulator, - isSamplingSet: true, // TODO : later + isSamplingSet: checkSamplingMode(ast), } const isCountOnly = isRootCountOnly(ast) @@ -133,6 +134,11 @@ const pushAggregates = ( : [] if (key === 'count' && props.length === 0) { + ctx.readSchema.aggregate?.aggregates.push({ + path: ['count'], + type: fn, + resultPos: sizes.result, + }) continue } @@ -194,3 +200,12 @@ export const isAggregateAst = (ast: QueryAst) => { ast.cardinality ) } + +const checkSamplingMode = (ast: QueryAst): boolean => { + if ( + ast['stddev']?.samplingMode === 'population' || + ast['var']?.samplingMode === 'population' + ) + return false + else return true +} diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 6c53f395c2..191e5e95f4 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -67,7 +67,7 @@ export type QueryAst = { sort?: { prop: string; order: 'asc' | 'desc' } props?: Record edges?: QueryAst - count?: { props: string | void } + count?: { props?: string } sum?: { props: string[] } cardinality?: { props: string[] } avg?: { props: string[] } diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index cb049daaea..5912234bb1 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -1,5 +1,4 @@ import { QueryAst } from '../../src/db-query/ast/ast.js' -import { testDb } from '../shared/index.js' import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' import { BasedDb, debugBuffer } from '../../src/sdk.js' @@ -7,6 +6,7 @@ import { resultToObject, serializeReaderSchema, } from '../../src/protocol/index.js' +import { deepEqual } from 'assert' import test from '../shared/test.js' @@ -20,26 +20,40 @@ await test('aggregate', async (t) => { types: { user: { age: 'uint8', + balance: 'number', }, }, }) const a = client.create('user', { age: 18, + balance: -130.2, }) - const back = client.create('user', { + const b = client.create('user', { age: 30, + balance: 0, + }) + const c = client.create('user', { + age: 41, + balance: 1500.5, }) await db.drain() const ast: QueryAst = { type: 'user', - // props: { - // age: { include: {} }, + // sum: { + // props: ['age', 'balance'], // }, - sum: { - props: ['age'], - }, + // stddev: { + // props: ['age'], + // samplingMode: 'population', + // }, + // var: { + // props: ['age'], + // }, + // count: { props: 'age' }, + // count: { props: 'balance' }, + count: {}, } const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) const result = await db.server.getQueryBuf(ctx.query) @@ -49,13 +63,22 @@ await test('aggregate', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + // deepEqual( + // obj, + // { + // age: { sum: 89, stddev: 9.392668535736911, variance: 88.22222222222217 }, + // balance: { sum: 1370.3 }, + // }, + // 'basic accum, no groupby, no refs', + // ) + console.dir(obj, { depth: 10 }) - console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) + // console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) - console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') - const r = await db.query('user').sum('age').get() - r.debug() - r.inspect(10) + // const r = await db.query('user').count().get() + // r.debug() + // r.inspect(10) }) From 902cc8d504d04142af9295d8e2ea00fac1b80607 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Wed, 4 Feb 2026 17:40:24 -0300 Subject: [PATCH 151/449] top level count ast to buffer fix --- native/query/aggregates/aggregates.zig | 1 + src/db-query/ast/aggregates.ts | 18 +++++++++++++----- test/query-ast/aggregates.ts | 11 ++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 06677bde8a..5fa4946a3b 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -51,6 +51,7 @@ pub inline fn aggregateProps( var i: usize = 0; while (i < aggDefs.len) { + utils.debugPrint("aggDefs: {any}\n", .{aggDefs}); const currentAggDef = utils.readNext(t.AggProp, aggDefs, &i); utils.debugPrint("currentAggDef: {any}\n", .{currentAggDef}); utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index 4a15d7a852..c8413cc9da 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -1,4 +1,4 @@ -import { TypeDef } from '../../schema/defs/index.js' +import { TypeDef, PropDef } from '../../schema/defs/index.js' import { QueryType, QueryIteratorType, @@ -127,24 +127,32 @@ const pushAggregates = ( const data = ast[key] if (!data) continue - const props = Array.isArray(data.props) + let props = Array.isArray(data.props) ? data.props : data.props ? [data.props] : [] + let i = 0 if (key === 'count' && props.length === 0) { ctx.readSchema.aggregate?.aggregates.push({ path: ['count'], type: fn, resultPos: sizes.result, }) - continue + props.push('count') } - let i = 0 for (const propName of props) { - const propDef = typeDef.props.get(propName) + let propDef: PropDef | any = typeDef.props.get(propName) + if (propName === 'count' && fn === AggFunction.count) { + propDef = { + id: 255, + path: [propName], + start: 0, + type: 1, + } + } if (!propDef) { throw new Error(`Aggregate property '${propName}' not found`) } diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index 5912234bb1..a6ad4466df 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -53,6 +53,7 @@ await test('aggregate', async (t) => { // }, // count: { props: 'age' }, // count: { props: 'balance' }, + sum: { props: ['age'] }, count: {}, } const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) @@ -74,11 +75,11 @@ await test('aggregate', async (t) => { console.dir(obj, { depth: 10 }) - // console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) + console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) - // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') - // const r = await db.query('user').count().get() - // r.debug() - // r.inspect(10) + const r = await db.query('user').count().sum('age').get() + r.debug() + r.inspect(10) }) From 134ef36df940da1ad0296c5720e718c83de0e993 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Wed, 4 Feb 2026 18:06:50 -0300 Subject: [PATCH 152/449] to plevel count test --- native/query/aggregates/aggregates.zig | 1 - test/query-ast/aggregates.ts | 51 +++++++++++++------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 5fa4946a3b..06677bde8a 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -51,7 +51,6 @@ pub inline fn aggregateProps( var i: usize = 0; while (i < aggDefs.len) { - utils.debugPrint("aggDefs: {any}\n", .{aggDefs}); const currentAggDef = utils.readNext(t.AggProp, aggDefs, &i); utils.debugPrint("currentAggDef: {any}\n", .{currentAggDef}); utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index a6ad4466df..f10dde6d66 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -10,7 +10,7 @@ import { deepEqual } from 'assert' import test from '../shared/test.js' -await test('aggregate', async (t) => { +await test('basic', async (t) => { const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) t.after(() => db.destroy()) @@ -41,19 +41,17 @@ await test('aggregate', async (t) => { await db.drain() const ast: QueryAst = { type: 'user', - // sum: { - // props: ['age', 'balance'], - // }, - // stddev: { - // props: ['age'], - // samplingMode: 'population', - // }, - // var: { - // props: ['age'], - // }, - // count: { props: 'age' }, - // count: { props: 'balance' }, - sum: { props: ['age'] }, + sum: { + props: ['age', 'balance'], + }, + stddev: { + props: ['age'], + samplingMode: 'population', + }, + var: { + props: ['age'], + }, + // count: { props: 'age' }, // not implementd yet count: {}, } const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) @@ -64,22 +62,23 @@ await test('aggregate', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - // deepEqual( - // obj, - // { - // age: { sum: 89, stddev: 9.392668535736911, variance: 88.22222222222217 }, - // balance: { sum: 1370.3 }, - // }, - // 'basic accum, no groupby, no refs', - // ) + deepEqual( + obj, + { + age: { sum: 89, stddev: 9.392668535736911, variance: 88.22222222222217 }, + balance: { sum: 1370.3 }, + count: 3, + }, + 'basic accum, no groupby, no refs', + ) console.dir(obj, { depth: 10 }) console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) - console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') - const r = await db.query('user').count().sum('age').get() - r.debug() - r.inspect(10) + // const r = await db.query('user').count().sum('age').get() + // r.debug() + // r.inspect(10) }) From 6d29aaf5608af496f99ce4041502e14c7499e5d8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 08:25:52 +0100 Subject: [PATCH 153/449] unused import --- test/mem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mem.ts b/test/mem.ts index 37344a3630..f102b1f0b0 100644 --- a/test/mem.ts +++ b/test/mem.ts @@ -1,4 +1,4 @@ -import { fastPrng, wait } from '../src/utils/index.js' +import { fastPrng } from '../src/utils/index.js' import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { equal } from './shared/assert.js' From 2c6883135d3eae7a2de648cd851a8afe01941c77 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 09:42:00 +0100 Subject: [PATCH 154/449] update --- test/query/ast.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/query/ast.ts b/test/query/ast.ts index 98a2196c3c..f8c0c84dd6 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -17,10 +17,6 @@ await test('query types', async (t) => { }, }) - await db.create('user', { - isNice: true, - }) - const query = db .query2('user') .include('isNice', 'name', 'otherUsers.name') @@ -29,9 +25,5 @@ await test('query types', async (t) => { ) .filter('otherUsers.name', '=', 'youzi') - const result = await query.get() - - // result[0].otherUsers - console.dir(query.ast, { depth: null }) }) From 835cd78cd53a7adfcc878656a89086b8ebf5fe9a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 10:53:41 +0100 Subject: [PATCH 155/449] Remove debug log --- native/query/multiple.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 0ff793c073..dfb380dd0e 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -219,7 +219,6 @@ pub fn references( switch (header.iteratorType) { .edgeInclude => { - std.debug.print("hello start IT => \n", .{}); var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); }, From 754e7458cd1d46791f1b871b1d65d1657b0453ca Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 10:53:56 +0100 Subject: [PATCH 156/449] Test that it was not removed --- test/insertOnly.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/insertOnly.ts b/test/insertOnly.ts index 2cc9b897ec..2c4b646fe7 100644 --- a/test/insertOnly.ts +++ b/test/insertOnly.ts @@ -1,5 +1,5 @@ import { BasedDb } from '../src/index.js' -import { throws } from './shared/assert.js' +import { deepEqual, throws } from './shared/assert.js' import test from './shared/test.js' await test('insert only => no delete', async (t) => { @@ -24,6 +24,7 @@ await test('insert only => no delete', async (t) => { const a = await db.create('audit', { v: 100 }) await db.create('audit', { v: 100 }) await throws(() => db.delete('audit', a)) + deepEqual(await db.query('audit', a).get(), { id: 1, v: 100 }) }) await test('colvec requires insertOnly', async (t) => { From 8ba4947540083f95f147feeb522672eef1c11130 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 11:09:31 +0100 Subject: [PATCH 157/449] added insertOnly --- package-lock.json | 4 ---- src/schema/schema/type.ts | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 20b4d7c1f2..71baa502af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,10 +174,6 @@ "@based/locale-x86-64-gnu": "*" } }, - "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { - "dev": true, - "optional": true - }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", diff --git a/src/schema/schema/type.ts b/src/schema/schema/type.ts index 4f763ffe84..a193026253 100644 --- a/src/schema/schema/type.ts +++ b/src/schema/schema/type.ts @@ -58,10 +58,15 @@ export const parseType = ( type.partial === undefined || isBoolean(type.partial), 'Should be boolean', ) + assert( + type.insertOnly === undefined || isBoolean(type.insertOnly), + 'Should be boolean', + ) assert(isRecord(type.props), 'Should be record') const result = { hooks: type.hooks, blockCapacity: type.blockCapacity, + insertOnly: type.insertOnly, capped: type.capped, partial: type.partial, props: parseProps(type.props, locales), From 7057f072f5f7e56fef54a9ac44a79268b5459f3f Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 11:19:31 +0100 Subject: [PATCH 158/449] fix blocker batch --- src/db-client/modify/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 58b491d1e4..99c1884706 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -134,7 +134,6 @@ export class BasedModify any = ModifySerializer> this._id = readUint32(this._batch.result, this._index! * 5) const errCode = this._batch.result[this._index! * 5 + 4] if (errCode) this._error = new Error('ModifyError: ' + errCode) - this._batch = undefined } } get id(): number | undefined { From 3818eee879bb2a4e76c24d06233edefc69deb245 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 11:47:43 +0100 Subject: [PATCH 159/449] remove limit selva schema buffer --- src/db-server/index.ts | 6 +- src/db-server/schema.ts | 28 +- src/schema/defs/index.ts | 12 +- src/schema/defs/props/alias.ts | 19 ++ src/schema/defs/props/binary.ts | 61 +++++ src/schema/defs/props/cardinality.ts | 52 ++++ src/schema/defs/props/separate.ts | 387 --------------------------- src/schema/defs/props/strings.ts | 186 +++++++++++++ src/schema/defs/props/utils.ts | 15 ++ src/schema/defs/props/vector.ts | 85 ++++++ src/utils/AutoSizedUint8Array.ts | 2 +- 11 files changed, 450 insertions(+), 403 deletions(-) create mode 100644 src/schema/defs/props/alias.ts create mode 100644 src/schema/defs/props/binary.ts create mode 100644 src/schema/defs/props/cardinality.ts delete mode 100644 src/schema/defs/props/separate.ts create mode 100644 src/schema/defs/props/strings.ts create mode 100644 src/schema/defs/props/utils.ts create mode 100644 src/schema/defs/props/vector.ts diff --git a/src/db-server/index.ts b/src/db-server/index.ts index 937cd65de9..a0f02db554 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -203,6 +203,7 @@ export class DbServer extends DbShared { schema: SchemaOut, transformFns?: SchemaMigrateFns, ): Promise { + console.log('????') if (this.stopped || !this.dbCtxExternal) { throw new Error('Db is stopped') } @@ -220,10 +221,13 @@ export class DbServer extends DbShared { await migrate(this, this.schema, schema, transformFns) return this.schema.hash } - + console.log('????1') setSchemaOnServer(this, schema) + console.log('????2') await setNativeSchema(this, schema) + console.log('????3') await writeSchemaFile(this, schema) + console.log('????4') process.nextTick(() => { this.emit('schema', this.schema!) diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index 2dd85213b4..edb92a79e6 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -76,17 +76,21 @@ export async function createSelvaType( msg[4] = OpType.createType msg.set(schema, 5) return new Promise((resolve, reject) => { - server.addOpOnceListener(OpType.createType, typeDef.id, (buf: Uint8Array) => { - const err = readUint32(buf, 0) - if (err) { - const errMsg = `Create type '${typeDef.name}' (${typeDef.id}) failed: ${native.selvaStrerror(err)}` - server.emit('error', errMsg) - reject(new Error(errMsg)) - } else { - resolve() - } - server.keepRefAliveTillThisPoint(msg) - }) + server.addOpOnceListener( + OpType.createType, + typeDef.id, + (buf: Uint8Array) => { + const err = readUint32(buf, 0) + if (err) { + const errMsg = `Create type '${typeDef.name}' (${typeDef.id}) failed: ${native.selvaStrerror(err)}` + server.emit('error', errMsg) + reject(new Error(errMsg)) + } else { + resolve() + } + server.keepRefAliveTillThisPoint(msg) + }, + ) native.modify(msg, server.dbCtxExternal) }) } @@ -109,7 +113,7 @@ export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { let maxTypeId = 0 await Promise.all( typeDefs.values().map((typeDef) => { - const buf = new AutoSizedUint8Array(4, 65536) + const buf = new AutoSizedUint8Array() maxTypeId = Math.max(maxTypeId, typeDef.id) let nrFixedFields = 1 let nrVirtualFields = 0 diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 149d8f762d..c0536438ae 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -7,7 +7,11 @@ import type { import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import * as references from './props/references.js' import * as fixed from './props/fixed.js' -import * as vars from './props/separate.js' +import * as alias from './props/alias.js' +import * as binary from './props/binary.js' +import * as cardinality from './props/cardinality.js' +import * as strings from './props/strings.js' +import * as vector from './props/vector.js' export type PropTree = Map @@ -66,6 +70,10 @@ export const defs: Record< > = { ...references, ...fixed, - ...vars, + ...alias, + ...binary, + ...cardinality, + ...strings, + ...vector, enum: fixed.enum_, } diff --git a/src/schema/defs/props/alias.ts b/src/schema/defs/props/alias.ts new file mode 100644 index 0000000000..c544269efc --- /dev/null +++ b/src/schema/defs/props/alias.ts @@ -0,0 +1,19 @@ +import { PropType, PropTypeSelva } from '../../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' + +export const alias = class Alias extends BasePropDef { + override type = PropType.alias + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is string { + if (typeof value !== 'string') { + throw new Error('Invalid type for alias ' + this.path.join('.')) + } + buf.pushString(value) + } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + buf.pushUint8(PropTypeSelva.alias) + } +} diff --git a/src/schema/defs/props/binary.ts b/src/schema/defs/props/binary.ts new file mode 100644 index 0000000000..78d6d2aa69 --- /dev/null +++ b/src/schema/defs/props/binary.ts @@ -0,0 +1,61 @@ +import native from '../../../native.js' +import { NOT_COMPRESSED } from '../../../protocol/index.js' +import type { SchemaBinary, SchemaString } from '../../../schema.js' +import { + PropType, + type PropTypeEnum, + PropTypeSelva, + pushSelvaSchemaString, + LangCode, +} from '../../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' +import type { TypeDef } from '../index.js' +import { validateMaxBytes } from './utils.js' + +export const binary = class Binary extends BasePropDef { + constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) + if (prop.maxBytes && prop.maxBytes < 61) { + this.size = prop.maxBytes + 1 + } + if (this.size) { + this.type = PropType.binaryFixed + this.pushValue = this.pushFixedValue + } + } + override type: PropTypeEnum = PropType.binary + declare schema: SchemaBinary + override validate(value: unknown): asserts value is Uint8Array { + if (!(value instanceof Uint8Array)) { + throw new Error('Invalid type for binary ' + this.path.join('.')) + } + validateMaxBytes(value.byteLength, this.schema, this.path) + } + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is Uint8Array { + this.validate(value) + const crc = native.crc32(value) + buf.pushUint8(LangCode.none) + buf.pushUint8(NOT_COMPRESSED) + buf.set(value, buf.length) + buf.pushUint32(crc) + } + pushFixedValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is Uint8Array { + this.validate(value) + buf.pushUint8(value.byteLength) + buf.set(value, buf.length) + } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + } +} diff --git a/src/schema/defs/props/cardinality.ts b/src/schema/defs/props/cardinality.ts new file mode 100644 index 0000000000..26e0f9b549 --- /dev/null +++ b/src/schema/defs/props/cardinality.ts @@ -0,0 +1,52 @@ +import type { SchemaCardinality } from '../../../schema.js' +import { ENCODER } from '../../../utils/uint8.js' +import { + pushModifyCardinalityHeader, + PropType, + PropTypeSelva, + pushSelvaSchemaString, +} from '../../../zigTsExports.js' +import { xxHash64 } from '../../../db-client/xxHash64.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' +import type { TypeDef } from '../index.js' + +export const cardinality = class Cardinality extends BasePropDef { + constructor(prop: SchemaCardinality, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) + this.sparse = prop.mode === 'sparse' + this.precision = prop.precision ?? 8 + } + sparse: boolean + precision: number + override type = PropType.cardinality + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is any { + if (!Array.isArray(value)) { + value = [value] + } + + const items = value as any[] + if (items.length === 0) return + pushModifyCardinalityHeader(buf, this) + for (const item of items) { + if (typeof item === 'string') { + buf.reserveUint64() + xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) + } else if (item instanceof Uint8Array && item.byteLength === 8) { + buf.set(item, buf.length) + } else { + throw new Error('Invalid value for cardinality ' + this.path.join('.')) + } + } + } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + } +} diff --git a/src/schema/defs/props/separate.ts b/src/schema/defs/props/separate.ts deleted file mode 100644 index 311a9285b0..0000000000 --- a/src/schema/defs/props/separate.ts +++ /dev/null @@ -1,387 +0,0 @@ -import native from '../../../native.js' -import { COMPRESSED, NOT_COMPRESSED } from '../../../protocol/index.js' -import type { - SchemaBinary, - SchemaCardinality, - SchemaString, - SchemaText, - SchemaVector, -} from '../../../schema.js' -import { ENCODER } from '../../../utils/uint8.js' -import { - pushModifyCardinalityHeader, - PropType, - type LangCodeEnum, - type PropTypeEnum, - type ModifyEnum, - PropTypeSelva, - pushSelvaSchemaColvec, - pushSelvaSchemaMicroBuffer, - pushSelvaSchemaString, - pushSelvaSchemaText, - LangCode, - writeSelvaSchemaStringProps, -} from '../../../zigTsExports.js' -import { xxHash64 } from '../../../db-client/xxHash64.js' -import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' -import { BasePropDef } from './base.js' -import type { TypeDef } from '../index.js' - -const validateMaxBytes = ( - bytes: number, - prop: { maxBytes?: number }, - path: string[], -) => { - if (prop.maxBytes !== undefined) { - if (bytes > prop.maxBytes) { - throw new Error( - `Byte length ${bytes} is larger than maxBytes ${ - prop.maxBytes - } for ${path.join('.')}`, - ) - } - } -} - -function validateString( - value: unknown, - prop: { min?: number; max?: number }, - path: string[], -): asserts value is string { - if (typeof value !== 'string') { - throw new Error('Invalid type for string ' + path.join('.')) - } - if (prop.min !== undefined && value.length < prop.min) { - throw new Error( - `Length ${value.length} is smaller than min ${prop.min} for ${path.join( - '.', - )}`, - ) - } - if (prop.max !== undefined && value.length > prop.max) { - throw new Error( - `Length ${value.length} is larger than max ${prop.max} for ${path.join( - '.', - )}`, - ) - } -} - -export const string = class String extends BasePropDef { - constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { - super(prop, path, typeDef) - if (prop.maxBytes && prop.maxBytes < 61) { - this.size = prop.maxBytes + 1 - } else if (prop.max && prop.max < 31) { - this.size = prop.max * 2 + 1 - } - if (this.size) { - this.type = PropType.stringFixed - this.pushValue = this.pushFixedValue - } else if (prop.compression === 'none') { - this.deflate = false - } - } - deflate = true - declare schema: SchemaString - override type: PropTypeEnum = PropType.string - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - lang: LangCodeEnum = LangCode.none, - ): asserts value is string { - validateString(value, this.schema, this.path) - const normalized = value.normalize('NFKD') - buf.pushUint8(lang) - if (this.deflate && normalized.length > 200) { - buf.pushUint8(COMPRESSED) - const sizePos = buf.reserveUint32() - const stringPos = buf.length - const written = buf.pushString(normalized) - buf.ensure(buf.length + written) - buf.data.copyWithin(buf.length, buf.length - written, buf.length) - const size = native.compress(buf.data, stringPos, written) - if (size !== 0) { - buf.writeUint32(written, sizePos) - buf.length = stringPos + size - validateMaxBytes(size, this.schema, this.path) - const crc = native.crc32(buf.subarray(stringPos)) - buf.pushUint32(crc) - return - } - buf.length = sizePos - 1 - } - buf.pushUint8(NOT_COMPRESSED) - const written = buf.pushString(normalized) - validateMaxBytes(written, this.schema, this.path) - const crc = native.crc32(buf.subarray(buf.length - written)) - buf.pushUint32(crc) - } - - pushFixedValue( - buf: AutoSizedUint8Array, - value: unknown, - ): asserts value is string { - validateString(value, this.schema, this.path) - const size = native.stringByteLength(value) - validateMaxBytes(size, this.schema, this.path) - buf.pushUint8(size) - buf.pushString(value) - const padEnd = this.size - size - 1 - if (padEnd) { - buf.fill(0, buf.length, buf.length + padEnd) - } - } - - override pushSelvaSchema(buf: AutoSizedUint8Array) { - const index = pushSelvaSchemaString(buf, { - type: PropTypeSelva.string, - fixedLen: 0, - defaultLen: 0, - }) - if (this.schema.default) { - const start = buf.length - this.pushValue(buf, this.schema.default) - writeSelvaSchemaStringProps.defaultLen( - buf.data, - buf.length - start, - index, - ) - } - } -} - -export const text = class Text extends string { - override type = PropType.text - // @ts-ignore - declare schema: SchemaText - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - op?: ModifyEnum, - lang: LangCodeEnum = LangCode.none, - ) { - if (typeof value === 'string') { - const index = buf.reserveUint32() - const start = buf.length - super.pushValue(buf, value, op, lang) - buf.writeUint32(buf.length - start, index) - } else if (typeof value === 'object' && value !== null) { - for (const key in value) { - if (!(key in LangCode)) { - throw new Error( - `Invalid locale ${key} for text ${this.path.join('.')}`, - ) - } - const index = buf.reserveUint32() - const start = buf.length - super.pushValue(buf, value[key], op, LangCode[key]) - buf.writeUint32(buf.length - start, index) - } - } else { - throw new Error('Invalid type for text ' + this.path.join('.')) - } - } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - if (this.schema.default) { - pushSelvaSchemaText(buf, { - type: PropTypeSelva.text, - nrDefaults: Object.keys(this.schema.default).length, - }) - this.pushValue(buf, this.schema.default) - } else { - pushSelvaSchemaText(buf, { - type: PropTypeSelva.text, - nrDefaults: 0, - }) - } - } -} - -export const json = class Json extends string { - override type = PropType.json - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - op?: ModifyEnum, - lang: LangCodeEnum = LangCode.none, - ) { - super.pushValue(buf, JSON.stringify(value), op, lang) - } -} - -export const binary = class Binary extends BasePropDef { - constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { - super(prop, path, typeDef) - if (prop.maxBytes && prop.maxBytes < 61) { - this.size = prop.maxBytes + 1 - } - if (this.size) { - this.type = PropType.binaryFixed - this.pushValue = this.pushFixedValue - } - } - override type: PropTypeEnum = PropType.binary - declare schema: SchemaBinary - override validate(value: unknown): asserts value is Uint8Array { - if (!(value instanceof Uint8Array)) { - throw new Error('Invalid type for binary ' + this.path.join('.')) - } - validateMaxBytes(value.byteLength, this.schema, this.path) - } - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - ): asserts value is Uint8Array { - this.validate(value) - const crc = native.crc32(value) - buf.pushUint8(LangCode.none) - buf.pushUint8(NOT_COMPRESSED) - buf.set(value, buf.length) - buf.pushUint32(crc) - } - pushFixedValue( - buf: AutoSizedUint8Array, - value: unknown, - ): asserts value is Uint8Array { - this.validate(value) - buf.pushUint8(value.byteLength) - buf.set(value, buf.length) - } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaString(buf, { - type: PropTypeSelva.string, - fixedLen: 0, - defaultLen: 0, - }) - } -} - -export const alias = class Alias extends BasePropDef { - override type = PropType.alias - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - ): asserts value is string { - if (typeof value !== 'string') { - throw new Error('Invalid type for alias ' + this.path.join('.')) - } - buf.pushString(value) - } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - buf.pushUint8(PropTypeSelva.alias) - } -} - -export const cardinality = class Cardinality extends BasePropDef { - constructor(prop: SchemaCardinality, path: string[], typeDef: TypeDef) { - super(prop, path, typeDef) - this.sparse = prop.mode === 'sparse' - this.precision = prop.precision ?? 8 - } - sparse: boolean - precision: number - override type = PropType.cardinality - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - ): asserts value is any { - if (!Array.isArray(value)) { - value = [value] - } - - const items = value as any[] - if (items.length === 0) return - pushModifyCardinalityHeader(buf, this) - for (const item of items) { - if (typeof item === 'string') { - buf.reserveUint64() - xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) - } else if (item instanceof Uint8Array && item.byteLength === 8) { - buf.set(item, buf.length) - } else { - throw new Error('Invalid value for cardinality ' + this.path.join('.')) - } - } - } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaString(buf, { - type: PropTypeSelva.string, - fixedLen: 0, - defaultLen: 0, - }) - } -} - -export const vector = class Vector extends BasePropDef { - constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { - super(schema, path, typeDef) - this.vectorSize = schema.size * 4 - } - vectorSize: number - override type: PropTypeEnum = PropType.vector - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op?: ModifyEnum, - _lang?: LangCodeEnum, - ): asserts value is any { - throw new Error('Serialize vector not implemented') - } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaMicroBuffer(buf, { - type: PropTypeSelva.colVec, - len: this.vectorSize, - hasDefault: 0, // TODO default - }) - } -} - -// This will become similair to Main BUFFER -// and it can use it if there is an option used like "appendOnly: true" on the type -// then we can switch to colvec for all main buffer props -// if there are no var props we can iterate straight trough the colvec list using another iterator -export const colvec = class ColVec extends BasePropDef { - constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { - super(schema, path, typeDef) - this.compSize = getByteSize(schema.baseType) - this.vecLen = schema.size * this.compSize - } - compSize: number - vecLen: number - override type = PropType.colVec - override pushValue( - buf: AutoSizedUint8Array, - value: unknown, - _op: ModifyEnum, - _lang: LangCodeEnum, - ): asserts value is any { - throw new Error('Serialize colvec not implemented') - } - override pushSelvaSchema(buf: AutoSizedUint8Array) { - pushSelvaSchemaColvec(buf, { - type: PropTypeSelva.colVec, - vecLen: this.vecLen, - compSize: this.compSize, - hasDefault: 0, - }) - } -} - -function getByteSize(str?: string) { - switch (str) { - case 'int8': - case 'uint8': - return 1 - case 'int16': - case 'uint16': - return 2 - case 'int32': - case 'uint32': - case 'float32': - return 4 - default: - return 8 - } -} diff --git a/src/schema/defs/props/strings.ts b/src/schema/defs/props/strings.ts new file mode 100644 index 0000000000..d7d8da1dd2 --- /dev/null +++ b/src/schema/defs/props/strings.ts @@ -0,0 +1,186 @@ +import native from '../../../native.js' +import { COMPRESSED, NOT_COMPRESSED } from '../../../protocol/index.js' +import type { SchemaString, SchemaText } from '../../../schema.js' +import { + PropType, + type LangCodeEnum, + type PropTypeEnum, + type ModifyEnum, + PropTypeSelva, + pushSelvaSchemaString, + pushSelvaSchemaText, + LangCode, + writeSelvaSchemaStringProps, +} from '../../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' +import type { TypeDef } from '../index.js' +import { validateMaxBytes } from './utils.js' + +function validateString( + value: unknown, + prop: { min?: number; max?: number }, + path: string[], +): asserts value is string { + if (typeof value !== 'string') { + throw new Error('Invalid type for string ' + path.join('.')) + } + if (prop.min !== undefined && value.length < prop.min) { + throw new Error( + `Length ${value.length} is smaller than min ${prop.min} for ${path.join( + '.', + )}`, + ) + } + if (prop.max !== undefined && value.length > prop.max) { + throw new Error( + `Length ${value.length} is larger than max ${prop.max} for ${path.join( + '.', + )}`, + ) + } +} + +export const string = class String extends BasePropDef { + constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { + super(prop, path, typeDef) + if (prop.maxBytes && prop.maxBytes < 61) { + this.size = prop.maxBytes + 1 + } else if (prop.max && prop.max < 31) { + this.size = prop.max * 2 + 1 + } + if (this.size) { + this.type = PropType.stringFixed + this.pushValue = this.pushFixedValue + } else if (prop.compression === 'none') { + this.deflate = false + } + } + deflate = true + declare schema: SchemaString + override type: PropTypeEnum = PropType.string + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + _op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, + ): asserts value is string { + validateString(value, this.schema, this.path) + const normalized = value.normalize('NFKD') + buf.pushUint8(lang) + if (this.deflate && normalized.length > 200) { + buf.pushUint8(COMPRESSED) + const sizePos = buf.reserveUint32() + const stringPos = buf.length + const written = buf.pushString(normalized) + buf.ensure(buf.length + written) + buf.data.copyWithin(buf.length, buf.length - written, buf.length) + const size = native.compress(buf.data, stringPos, written) + if (size !== 0) { + buf.writeUint32(written, sizePos) + buf.length = stringPos + size + validateMaxBytes(size, this.schema, this.path) + const crc = native.crc32(buf.subarray(stringPos)) + buf.pushUint32(crc) + return + } + buf.length = sizePos - 1 + } + buf.pushUint8(NOT_COMPRESSED) + const written = buf.pushString(normalized) + validateMaxBytes(written, this.schema, this.path) + const crc = native.crc32(buf.subarray(buf.length - written)) + buf.pushUint32(crc) + } + + pushFixedValue( + buf: AutoSizedUint8Array, + value: unknown, + ): asserts value is string { + validateString(value, this.schema, this.path) + const size = native.stringByteLength(value) + validateMaxBytes(size, this.schema, this.path) + buf.pushUint8(size) + buf.pushString(value) + const padEnd = this.size - size - 1 + if (padEnd) { + buf.fill(0, buf.length, buf.length + padEnd) + } + } + + override pushSelvaSchema(buf: AutoSizedUint8Array) { + const index = pushSelvaSchemaString(buf, { + type: PropTypeSelva.string, + fixedLen: 0, + defaultLen: 0, + }) + if (this.schema.default) { + const start = buf.length + this.pushValue(buf, this.schema.default) + writeSelvaSchemaStringProps.defaultLen( + buf.data, + buf.length - start, + index, + ) + } + } +} + +export const text = class Text extends string { + override type = PropType.text + // @ts-ignore + declare schema: SchemaText + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, + ) { + if (typeof value === 'string') { + const index = buf.reserveUint32() + const start = buf.length + super.pushValue(buf, value, op, lang) + buf.writeUint32(buf.length - start, index) + } else if (typeof value === 'object' && value !== null) { + for (const key in value) { + if (!(key in LangCode)) { + throw new Error( + `Invalid locale ${key} for text ${this.path.join('.')}`, + ) + } + const index = buf.reserveUint32() + const start = buf.length + super.pushValue(buf, value[key], op, LangCode[key]) + buf.writeUint32(buf.length - start, index) + } + } else { + throw new Error('Invalid type for text ' + this.path.join('.')) + } + } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + if (this.schema.default) { + pushSelvaSchemaText(buf, { + type: PropTypeSelva.text, + nrDefaults: Object.keys(this.schema.default).length, + }) + this.pushValue(buf, this.schema.default) + } else { + pushSelvaSchemaText(buf, { + type: PropTypeSelva.text, + nrDefaults: 0, + }) + } + } +} + +export const json = class Json extends string { + override type = PropType.json + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + op?: ModifyEnum, + lang: LangCodeEnum = LangCode.none, + ) { + super.pushValue(buf, JSON.stringify(value), op, lang) + } +} diff --git a/src/schema/defs/props/utils.ts b/src/schema/defs/props/utils.ts new file mode 100644 index 0000000000..6daf82778f --- /dev/null +++ b/src/schema/defs/props/utils.ts @@ -0,0 +1,15 @@ +export const validateMaxBytes = ( + bytes: number, + prop: { maxBytes?: number }, + path: string[], +) => { + if (prop.maxBytes !== undefined) { + if (bytes > prop.maxBytes) { + throw new Error( + `Byte length ${bytes} is larger than maxBytes ${ + prop.maxBytes + } for ${path.join('.')}`, + ) + } + } +} diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts new file mode 100644 index 0000000000..1bd6dc3afb --- /dev/null +++ b/src/schema/defs/props/vector.ts @@ -0,0 +1,85 @@ +import type { SchemaVector } from '../../../schema.js' +import { + PropType, + type LangCodeEnum, + type PropTypeEnum, + type ModifyEnum, + PropTypeSelva, + pushSelvaSchemaColvec, + pushSelvaSchemaMicroBuffer, +} from '../../../zigTsExports.js' +import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' +import { BasePropDef } from './base.js' +import type { TypeDef } from '../index.js' + +export const vector = class Vector extends BasePropDef { + constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + this.vectorSize = schema.size * 4 + } + vectorSize: number + override type: PropTypeEnum = PropType.vector + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + _op?: ModifyEnum, + _lang?: LangCodeEnum, + ): asserts value is any { + throw new Error('Serialize vector not implemented') + } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaMicroBuffer(buf, { + type: PropTypeSelva.colVec, + len: this.vectorSize, + hasDefault: 0, // TODO default + }) + } +} + +// This will become similair to Main BUFFER +// and it can use it if there is an option used like "appendOnly: true" on the type +// then we can switch to colvec for all main buffer props +// if there are no var props we can iterate straight trough the colvec list using another iterator +export const colvec = class ColVec extends BasePropDef { + constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + this.compSize = getByteSize(schema.baseType) + this.vecLen = schema.size * this.compSize + } + compSize: number + vecLen: number + override type = PropType.colVec + override pushValue( + buf: AutoSizedUint8Array, + value: unknown, + _op: ModifyEnum, + _lang: LangCodeEnum, + ): asserts value is any { + throw new Error('Serialize colvec not implemented') + } + override pushSelvaSchema(buf: AutoSizedUint8Array) { + pushSelvaSchemaColvec(buf, { + type: PropTypeSelva.colVec, + vecLen: this.vecLen, + compSize: this.compSize, + hasDefault: 0, + }) + } +} + +function getByteSize(str?: string) { + switch (str) { + case 'int8': + case 'uint8': + return 1 + case 'int16': + case 'uint16': + return 2 + case 'int32': + case 'uint32': + case 'float32': + return 4 + default: + return 8 + } +} diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index fcce56c07f..0c8011742a 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -30,7 +30,7 @@ const delegateMethods = [ ] as const export class AutoSizedUint8Array { - static readonly ERR_OVERFLOW = 1 + static readonly ERR_OVERFLOW = Error('ERR_OVERFLOW') data: Uint8Array length: number From 68576766d4aa03cacee3cb55063ce7c7ff953748 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 11:52:51 +0100 Subject: [PATCH 160/449] accept uint8 for string --- src/db-server/index.ts | 5 --- src/schema/defs/props/strings.ts | 55 +++++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/db-server/index.ts b/src/db-server/index.ts index a0f02db554..0dd287b29c 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -203,7 +203,6 @@ export class DbServer extends DbShared { schema: SchemaOut, transformFns?: SchemaMigrateFns, ): Promise { - console.log('????') if (this.stopped || !this.dbCtxExternal) { throw new Error('Db is stopped') } @@ -221,13 +220,9 @@ export class DbServer extends DbShared { await migrate(this, this.schema, schema, transformFns) return this.schema.hash } - console.log('????1') setSchemaOnServer(this, schema) - console.log('????2') await setNativeSchema(this, schema) - console.log('????3') await writeSchemaFile(this, schema) - console.log('????4') process.nextTick(() => { this.emit('schema', this.schema!) diff --git a/src/schema/defs/props/strings.ts b/src/schema/defs/props/strings.ts index d7d8da1dd2..986e615ef7 100644 --- a/src/schema/defs/props/strings.ts +++ b/src/schema/defs/props/strings.ts @@ -64,33 +64,38 @@ export const string = class String extends BasePropDef { value: unknown, _op?: ModifyEnum, lang: LangCodeEnum = LangCode.none, - ): asserts value is string { - validateString(value, this.schema, this.path) - const normalized = value.normalize('NFKD') - buf.pushUint8(lang) - if (this.deflate && normalized.length > 200) { - buf.pushUint8(COMPRESSED) - const sizePos = buf.reserveUint32() - const stringPos = buf.length - const written = buf.pushString(normalized) - buf.ensure(buf.length + written) - buf.data.copyWithin(buf.length, buf.length - written, buf.length) - const size = native.compress(buf.data, stringPos, written) - if (size !== 0) { - buf.writeUint32(written, sizePos) - buf.length = stringPos + size - validateMaxBytes(size, this.schema, this.path) - const crc = native.crc32(buf.subarray(stringPos)) - buf.pushUint32(crc) - return + ): asserts value is string | Uint8Array { + if (value instanceof Uint8Array) { + buf.pushUint32(value.byteLength) + buf.set(value, buf.length) + } else { + validateString(value, this.schema, this.path) + const normalized = value.normalize('NFKD') + buf.pushUint8(lang) + if (this.deflate && normalized.length > 200) { + buf.pushUint8(COMPRESSED) + const sizePos = buf.reserveUint32() + const stringPos = buf.length + const written = buf.pushString(normalized) + buf.ensure(buf.length + written) + buf.data.copyWithin(buf.length, buf.length - written, buf.length) + const size = native.compress(buf.data, stringPos, written) + if (size !== 0) { + buf.writeUint32(written, sizePos) + buf.length = stringPos + size + validateMaxBytes(size, this.schema, this.path) + const crc = native.crc32(buf.subarray(stringPos)) + buf.pushUint32(crc) + return + } + buf.length = sizePos - 1 } - buf.length = sizePos - 1 + buf.pushUint8(NOT_COMPRESSED) + const written = buf.pushString(normalized) + validateMaxBytes(written, this.schema, this.path) + const crc = native.crc32(buf.subarray(buf.length - written)) + buf.pushUint32(crc) } - buf.pushUint8(NOT_COMPRESSED) - const written = buf.pushString(normalized) - validateMaxBytes(written, this.schema, this.path) - const crc = native.crc32(buf.subarray(buf.length - written)) - buf.pushUint32(crc) } pushFixedValue( From 07f7aa12ced12208afc824c4b86552f7b4b94fd5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 12:30:15 +0100 Subject: [PATCH 161/449] Throw on delete if type is insertOnly --- src/db-client/modify/delete.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/db-client/modify/delete.ts b/src/db-client/modify/delete.ts index 380765b057..914aae86bc 100644 --- a/src/db-client/modify/delete.ts +++ b/src/db-client/modify/delete.ts @@ -13,6 +13,9 @@ export const serializeDelete = < buf: AutoSizedUint8Array, ) => { const typeDef = getTypeDef(schema, type) + if (typeDef.schema.insertOnly) { + throw new Error('This type is insertOnly') + } const header = assignTarget(item, { op: Modify.delete, type: typeDef.id, From cb3182df614080d07ab39c6189919cc11c330641 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 13:31:06 +0100 Subject: [PATCH 162/449] fix mismatch defs ids --- src/schema/def/typeDef.ts | 11 +++++++++++ src/schema/defs/getTypeDefs.ts | 3 ++- test/filter/string.ts | 23 ++++++++++++++++++++--- test/modify/props/mixed.ts | 5 +++-- test/modify/props/references.ts | 1 + test/modify/props/timestamp.ts | 2 +- 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index bae3bfc95e..e1c4e848c7 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -30,6 +30,7 @@ import { fillEmptyMain, isZeroes } from './fillEmptyMain.js' import type { SchemaType } from '../schema/type.js' import { PropType } from '../../zigTsExports.js' import type { SchemaLocales } from '../schema/locales.js' +import { getTypeDefs } from '../defs/getTypeDefs.js' export const updateTypeDefs = (schema: SchemaOut) => { const schemaTypesParsed: { [key: string]: SchemaTypeDef } = {} @@ -126,6 +127,16 @@ export const updateTypeDefs = (schema: SchemaOut) => { } } + // A hack to sync the prop ids: + const newDefs = getTypeDefs(schema) + for (const [type, typeDef] of newDefs) { + const oldTypeDef = schemaTypesParsed[type] + oldTypeDef.id = typeDef.id + for (const path in oldTypeDef.props) { + oldTypeDef.props[path].prop = typeDef.props.get(path)!.id + } + } + return { schemaTypesParsed, schemaTypesParsedById } } diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 0acc43c275..7fc78c17f5 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -44,7 +44,8 @@ export const propIndexOffset = (prop: PropDef): number => { } } -const separateSorter = (a, b) => propIndexOffset(a) - propIndexOffset(b) +const separateSorter = (a: PropDef, b: PropDef) => + propIndexOffset(a) - propIndexOffset(b) const addPropDef = ( prop: SchemaProp, diff --git a/test/filter/string.ts b/test/filter/string.ts index aa9f08ea64..3a3dfbdcf3 100644 --- a/test/filter/string.ts +++ b/test/filter/string.ts @@ -1,10 +1,28 @@ -import { BasedDb, stringCompress as compress } from '../../src/index.js' +import { BasedDb } from '../../src/index.js' import { ENCODER } from '../../src/utils/uint8.js' import test from '../shared/test.js' import { equal, deepEqual } from '../shared/assert.js' import { italy, sentence, readBible } from '../shared/examples.js' import { decompress } from '../../src/protocol/index.js' - +import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' +import { defs } from '../../src/schema/defs/index.js' +import { Modify } from '../../src/zigTsExports.js' + +const buf = new AutoSizedUint8Array() +const s = new defs.string({ type: 'string' }, [], { + id: 0, + name: '', + main: [], + separate: [], + props: new Map(), + tree: new Map(), + schema: { props: {} }, +}) +const compress = (str: string) => { + buf.length = 0 + s.pushValue(buf, str, Modify.create) + return buf.view.slice() +} const bible = readBible() const capitals = @@ -43,7 +61,6 @@ await test('variable size (string/binary)', async (t) => { equal(decompress(compressedSentence), sentence, 'compress / decompress api') const compressedItaly = compress(italy) equal(decompress(compressedItaly), italy, 'compress / decompress api (large)') - for (let i = 0; i < 1000; i++) { const str = 'en' db.create('article', { diff --git a/test/modify/props/mixed.ts b/test/modify/props/mixed.ts index 4bf37b6774..900aaa2706 100644 --- a/test/modify/props/mixed.ts +++ b/test/modify/props/mixed.ts @@ -1,3 +1,4 @@ +import { ENCODER } from '../../../src/utils/index.js' import { deepEqual } from '../../shared/assert.js' import { testDb } from '../../shared/index.js' import test from '../../shared/test.js' @@ -49,9 +50,9 @@ await test('mixed props', async (t) => { r: 'a', q: { id: 1, - age: 33, name: 'T', email: 't@t.com', + age: 33, story: 'hello', alias: 't', }, @@ -61,9 +62,9 @@ await test('mixed props', async (t) => { deepEqual(user, [ { id: 1, - age: 33, name: 'T', email: 't@t.com', + age: 33, story: 'hello', alias: 't', test: [{ id: 1, r: 'a' }], diff --git a/test/modify/props/references.ts b/test/modify/props/references.ts index cf52b3a178..6eaccb16af 100644 --- a/test/modify/props/references.ts +++ b/test/modify/props/references.ts @@ -56,6 +56,7 @@ await test('modify single reference', async (t) => { await db.update('holder', h1, { dest: null }) deepEqual(await db.query('holder', h1).include('dest').get(), { id: h1, + dest: null, }) }) diff --git a/test/modify/props/timestamp.ts b/test/modify/props/timestamp.ts index 4cd46930a5..91f929c0ff 100644 --- a/test/modify/props/timestamp.ts +++ b/test/modify/props/timestamp.ts @@ -97,7 +97,7 @@ await test('modify timestamp', async (t) => { // Delete await db.update('event', id1, { ts: null }) - deepEqual((await db.query('event', id1).get().toObject()).ts, undefined) + deepEqual((await db.query('event', id1).get().toObject()).ts, 0) }) await test('modify timestamp on edge', async (t) => { From 69c14d37555bc70500b8f1bdb75634e86f414a4b Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 13:32:10 +0100 Subject: [PATCH 163/449] update comment --- src/schema/def/typeDef.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index e1c4e848c7..0101472ee9 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -127,7 +127,7 @@ export const updateTypeDefs = (schema: SchemaOut) => { } } - // A hack to sync the prop ids: + // A hack to sync the type and prop ids: const newDefs = getTypeDefs(schema) for (const [type, typeDef] of newDefs) { const oldTypeDef = schemaTypesParsed[type] From 4690e545fa891f673ed5d1b2c31d565c5f4fab79 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 13:45:16 +0100 Subject: [PATCH 164/449] vector type is a microBuffer --- src/schema/defs/props/vector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 1bd6dc3afb..b6a558af5b 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -29,7 +29,7 @@ export const vector = class Vector extends BasePropDef { } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaMicroBuffer(buf, { - type: PropTypeSelva.colVec, + type: PropTypeSelva.microBuffer, len: this.vectorSize, hasDefault: 0, // TODO default }) From d579dd29576a3af7ccccf6bd9926ea242ee0c930 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 13:54:06 +0100 Subject: [PATCH 165/449] fix the fix --- src/schema/def/typeDef.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index 0101472ee9..09152b64ad 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -131,9 +131,10 @@ export const updateTypeDefs = (schema: SchemaOut) => { const newDefs = getTypeDefs(schema) for (const [type, typeDef] of newDefs) { const oldTypeDef = schemaTypesParsed[type] - oldTypeDef.id = typeDef.id - for (const path in oldTypeDef.props) { - oldTypeDef.props[path].prop = typeDef.props.get(path)!.id + if (oldTypeDef) { + for (const path in oldTypeDef.props) { + oldTypeDef.props[path].prop = typeDef.props.get(path)!.id + } } } From a2a9939e69e4bf687d67418265d4a0e954b59f6d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 14:44:30 +0100 Subject: [PATCH 166/449] Implement push for vectors --- src/schema/defs/props/vector.ts | 48 ++++++++++++++++++--------------- src/schema/schema/vector.ts | 2 +- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index b6a558af5b..272a8ee6f3 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -11,11 +11,24 @@ import { import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' +import {isTypedArray} from 'util/types' + +const baseTypeSize: { [K in SchemaVector['baseType']]: number } = { + 'number': 8, + int8: 1, + uint8: 1, + int16: 2, + uint16: 2, + int32: 4, + uint32: 4, + float32: 8, + float64: 8, +} export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) - this.vectorSize = schema.size * 4 + this.vectorSize = schema.size * baseTypeSize[schema.baseType] } vectorSize: number override type: PropTypeEnum = PropType.vector @@ -25,7 +38,11 @@ export const vector = class Vector extends BasePropDef { _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is any { - throw new Error('Serialize vector not implemented') + if (!isTypedArray(value)) { + throw new Error('Not a typed array') + } + const v = new Uint8Array(value.buffer).subarray(0, Math.min(value.byteLength, this.vectorSize)) + buf.set(v, buf.length) } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaMicroBuffer(buf, { @@ -36,14 +53,14 @@ export const vector = class Vector extends BasePropDef { } } -// This will become similair to Main BUFFER +// This will become similar to Main BUFFER // and it can use it if there is an option used like "appendOnly: true" on the type // then we can switch to colvec for all main buffer props // if there are no var props we can iterate straight trough the colvec list using another iterator export const colvec = class ColVec extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) - this.compSize = getByteSize(schema.baseType) + this.compSize = baseTypeSize[schema.baseType] this.vecLen = schema.size * this.compSize } compSize: number @@ -55,7 +72,11 @@ export const colvec = class ColVec extends BasePropDef { _op: ModifyEnum, _lang: LangCodeEnum, ): asserts value is any { - throw new Error('Serialize colvec not implemented') + if (!isTypedArray(value)) { + throw new Error('Not a typed array') + } + const v = new Uint8Array(value.buffer).subarray(0, Math.min(value.byteLength, this.vecLen)) + buf.set(v, buf.length) } override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaColvec(buf, { @@ -66,20 +87,3 @@ export const colvec = class ColVec extends BasePropDef { }) } } - -function getByteSize(str?: string) { - switch (str) { - case 'int8': - case 'uint8': - return 1 - case 'int16': - case 'uint16': - return 2 - case 'int32': - case 'uint32': - case 'float32': - return 4 - default: - return 8 - } -} diff --git a/src/schema/schema/vector.ts b/src/schema/schema/vector.ts index 735090d904..54a459ff93 100644 --- a/src/schema/schema/vector.ts +++ b/src/schema/schema/vector.ts @@ -14,7 +14,7 @@ export type SchemaVector = Base & { * Base type of the vector. * float64 == number */ - baseType?: (typeof vectorBaseTypes)[number] + baseType: (typeof vectorBaseTypes)[number] } export const parseVector = (def: Record): SchemaVector => { From 6bcb96fd50fac3fa31d84352e844e741b3f93891 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 5 Feb 2026 14:46:06 +0100 Subject: [PATCH 167/449] prettier --- src/schema/defs/props/vector.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 272a8ee6f3..58fdb70af2 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -11,10 +11,10 @@ import { import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' -import {isTypedArray} from 'util/types' +import { isTypedArray } from 'util/types' const baseTypeSize: { [K in SchemaVector['baseType']]: number } = { - 'number': 8, + number: 8, int8: 1, uint8: 1, int16: 2, @@ -41,7 +41,10 @@ export const vector = class Vector extends BasePropDef { if (!isTypedArray(value)) { throw new Error('Not a typed array') } - const v = new Uint8Array(value.buffer).subarray(0, Math.min(value.byteLength, this.vectorSize)) + const v = new Uint8Array(value.buffer).subarray( + 0, + Math.min(value.byteLength, this.vectorSize), + ) buf.set(v, buf.length) } override pushSelvaSchema(buf: AutoSizedUint8Array) { @@ -75,7 +78,10 @@ export const colvec = class ColVec extends BasePropDef { if (!isTypedArray(value)) { throw new Error('Not a typed array') } - const v = new Uint8Array(value.buffer).subarray(0, Math.min(value.byteLength, this.vecLen)) + const v = new Uint8Array(value.buffer).subarray( + 0, + Math.min(value.byteLength, this.vecLen), + ) buf.set(v, buf.length) } override pushSelvaSchema(buf: AutoSizedUint8Array) { From f34df58bbfb21cfcb060124a587f74587b3c61b4 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 15:21:44 +0100 Subject: [PATCH 168/449] poc proxy result --- src/db-client/index.ts | 2 +- src/db-client/query2/index.ts | 25 ++++++++++++++++----- src/db-client/query2/result.ts | 40 ++++++++++++++++++++++++++++++++++ test/query/ast.ts | 26 ++++++++++++++++------ 4 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 src/db-client/query2/result.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index b8c4781416..fdab8deb26 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -106,7 +106,7 @@ export class DbClient extends DbShared { type: T, id?: number, ): BasedQuery2 { - return new BasedQuery2(type, id) + return new BasedQuery2(this, type, id) } create( diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 61b7716107..d5451257cd 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -2,14 +2,17 @@ import type { FilterLeaf, QueryAst } from '../../db-query/ast/ast.js' import type { PickOutput, ResolveInclude, - InferProp, Path, - FilterOpts, // FilterOpts + FilterOpts, Operator, ResolveDotPath, InferPathType, } from './types.js' -import type { ResolvedProps } from '../../schema/index.js' +import type { ResolvedProps, SchemaOut } from '../../schema/index.js' +import { astToQueryCtx } from '../../db-query/ast/toCtx.js' +import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' +import type { DbClient } from '../../sdk.js' +import { proxyResult } from './result.js' class QueryBranch< S extends { types: any } = { types: any }, @@ -105,17 +108,29 @@ export class BasedQuery2< | string = '*', IsSingle extends boolean = false, > extends QueryBranch { - constructor(type: T, target?: number) { + constructor(db: DbClient, type: T, target?: number) { super({}) this.ast.type = type as string this.ast.target = target + this.db = db } + db: DbClient async get(): Promise< IsSingle extends true ? PickOutput, K>> : PickOutput, K>>[] > { - return [] as any + if (!this.db.schema) { + await this.db.once('schema') + } + await this.db.isModified() + const ctx = astToQueryCtx( + this.db.schema!, + this.ast, + new AutoSizedUint8Array(1000), + ) + const result = await this.db.hooks.getQueryBuf(ctx.query) + return proxyResult(result, ctx.readSchema) as any } } diff --git a/src/db-client/query2/result.ts b/src/db-client/query2/result.ts new file mode 100644 index 0000000000..444978db58 --- /dev/null +++ b/src/db-client/query2/result.ts @@ -0,0 +1,40 @@ +import { + resultToObject, + type ReaderSchema, + ReaderSchemaEnum, +} from '../../protocol/index.js' + +const parse = (target: object) => + resultToObject( + target[$schema], + target[$buffer], + target[$buffer].byteLength - 4, + ) + +const sharedHandler: ProxyHandler = { + get(target, prop) { + if (prop === 'then') return target[prop] + target[$parsed] ??= parse(target) + return target[$parsed][prop] + }, + ownKeys(target) { + target[$parsed] ??= parse(target) + return Reflect.ownKeys(target[$parsed]) + }, + getOwnPropertyDescriptor(target, prop) { + target[$parsed] ??= parse(target) + return Reflect.getOwnPropertyDescriptor(target[$parsed], prop) + }, +} + +const $parsed = Symbol() +const $buffer = Symbol() +const $schema = Symbol() + +export const proxyResult = (buffer: Uint8Array, schema: ReaderSchema) => { + const target = schema.type === ReaderSchemaEnum.single ? {} : [] + const proxy = new Proxy(target, sharedHandler) + target[$buffer] = buffer + target[$schema] = schema + return proxy +} diff --git a/test/query/ast.ts b/test/query/ast.ts index f8c0c84dd6..5cf83ac93e 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -17,13 +17,25 @@ await test('query types', async (t) => { }, }) - const query = db - .query2('user') - .include('isNice', 'name', 'otherUsers.name') - .include((select) => - select('otherUsers').include('name').filter('name', '=', 'youzi'), - ) - .filter('otherUsers.name', '=', 'youzi') + db.create('user', { + isNice: true, + }) + + const query = db.query2('user').include('isNice', 'name', 'otherUsers.name') + // .include((select) => + // select('otherUsers').include('name').filter('name', '=', 'youzi'), + // ) + // .filter('otherUsers.name', '=', 'youzi') console.dir(query.ast, { depth: null }) + const proxy = await query.get() + console.log('-----------') + proxy + console.log('-----------1') + // proxy.forEach((a, b, c) => console.log('WAZZUP', { a, b, c })) + for (const i of proxy) { + console.log({ i }) + } + + // }) From 177356f0d630f324f778fd24d5ea12530c428b2f Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 5 Feb 2026 15:23:31 +0100 Subject: [PATCH 169/449] poc proxy result --- test/query/ast.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/query/ast.ts b/test/query/ast.ts index 5cf83ac93e..e23916f39c 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -1,3 +1,4 @@ +import { $buffer } from '../../src/db-client/query2/result.js' import { testDb } from '../shared/index.js' import test from '../shared/test.js' @@ -37,5 +38,7 @@ await test('query types', async (t) => { console.log({ i }) } + console.log('-->', proxy[$buffer]) + // }) From a452b0a38497ed5d8c1bc8621cbe94b86688e2df Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 08:20:58 +0100 Subject: [PATCH 170/449] update query types --- src/db-client/query2/index.ts | 43 +++++++++++++---- src/db-client/query2/result.ts | 75 +++++++++++++++++++---------- src/db-client/query2/types.ts | 83 ++++++++++++++++++++++++++++---- src/db-query/ast/include.ts | 1 + src/protocol/db-read/read.ts | 4 +- test/query-ast/include.ts | 3 ++ test/query/ast.ts | 88 +++++++++++++++++++++++++++++----- 7 files changed, 238 insertions(+), 59 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index d5451257cd..6269217d1d 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -7,6 +7,7 @@ import type { Operator, ResolveDotPath, InferPathType, + FilterEdges, } from './types.js' import type { ResolvedProps, SchemaOut } from '../../schema/index.js' import { astToQueryCtx } from '../../db-query/ast/toCtx.js' @@ -26,6 +27,7 @@ class QueryBranch< IsSingle extends boolean = false, SourceField extends string | number | symbol | undefined = undefined, IsRoot extends boolean = false, + EdgeProps extends Record = {}, > { constructor(ast: QueryAst) { this.ast = ast @@ -33,7 +35,7 @@ class QueryBranch< ast: QueryAst include< F extends ( - | keyof ResolvedProps + | (keyof (ResolvedProps & EdgeProps) & string) | Path | '*' | '**' @@ -54,7 +56,8 @@ class QueryBranch< (K extends '*' ? never : K) | ResolveIncludeArgs, IsSingle, SourceField, - IsRoot + IsRoot, + EdgeProps > { for (const prop of props as (string | Function)[]) { let target = this.ast @@ -62,16 +65,28 @@ class QueryBranch< prop((prop: string) => { const path = prop.split('.') for (const key of path) { - target.props ??= {} - target = target.props[key] = {} + if (key[0] === '$') { + target.edges ??= {} + target.edges.props ??= {} + target = target.edges.props[key] = {} + } else { + target.props ??= {} + target = target.props[key] = {} + } } return new QueryBranch(target) }) } else { const path = prop.split('.') for (const key of path) { - target.props ??= {} - target = target.props[key] = {} + if (key[0] === '$') { + target.edges ??= {} + target.edges.props ??= {} + target = target.edges.props[key] = {} + } else { + target.props ??= {} + target = target.props[key] = {} + } } target.include = {} } @@ -79,7 +94,11 @@ class QueryBranch< return this as any } - filter

| Path>( + filter< + P extends + | keyof (ResolvedProps & EdgeProps) + | Path, + >( prop: P, op: Operator, val: InferPathType, @@ -107,7 +126,7 @@ export class BasedQuery2< | { field: any; select: any } | string = '*', IsSingle extends boolean = false, -> extends QueryBranch { +> extends QueryBranch { constructor(db: DbClient, type: T, target?: number) { super({}) this.ast.type = type as string @@ -149,7 +168,12 @@ type SelectFn = < : never, '*', false, - P + P, + false, + FilterEdges[P]> & + (ResolvedProps[P] extends { items: infer Items } + ? FilterEdges + : {}) > // ResolveIncludeArgs needs to stay here because it refers to QueryBranch @@ -161,6 +185,7 @@ export type ResolveIncludeArgs = T extends ( infer K, infer Single, infer SourceField, + any, any > ? { field: SourceField; select: K } diff --git a/src/db-client/query2/result.ts b/src/db-client/query2/result.ts index 444978db58..763c3d0d43 100644 --- a/src/db-client/query2/result.ts +++ b/src/db-client/query2/result.ts @@ -4,37 +4,62 @@ import { ReaderSchemaEnum, } from '../../protocol/index.js' -const parse = (target: object) => - resultToObject( - target[$schema], - target[$buffer], - target[$buffer].byteLength - 4, +export const $buffer = Symbol() +export const $schema = Symbol() +export const $result = Symbol() + +const define = (result: any) => { + result.__proto__ = [] + const data = resultToObject( + result[$schema], + result[$buffer], + result[$buffer].byteLength - 4, + 0, + result, ) + if (data !== result) result.__proto__ = data + Object.defineProperty(result, $buffer, { enumerable: false }) + Object.defineProperty(result, $schema, { enumerable: false }) +} -const sharedHandler: ProxyHandler = { - get(target, prop) { - if (prop === 'then') return target[prop] - target[$parsed] ??= parse(target) - return target[$parsed][prop] +const handler: ProxyHandler = { + get(stub, prop) { + const result = stub[$result] + if (prop === $buffer || prop === $schema) { + return result[prop] + } + if (prop === 'then') { + // this can be improved!! + const schema: ReaderSchema = result[$schema] + if (schema.type !== ReaderSchemaEnum.single || !schema.props?.then) { + return undefined + } + } + define(result) + return result[prop] }, - ownKeys(target) { - target[$parsed] ??= parse(target) - return Reflect.ownKeys(target[$parsed]) + ownKeys(stub) { + const result = stub[$result] + define(result) + return Reflect.ownKeys(result) }, - getOwnPropertyDescriptor(target, prop) { - target[$parsed] ??= parse(target) - return Reflect.getOwnPropertyDescriptor(target[$parsed], prop) + getOwnPropertyDescriptor(stub, prop) { + const result = stub[$result] + define(result) + return Reflect.getOwnPropertyDescriptor(result, prop) }, } -const $parsed = Symbol() -const $buffer = Symbol() -const $schema = Symbol() - export const proxyResult = (buffer: Uint8Array, schema: ReaderSchema) => { - const target = schema.type === ReaderSchemaEnum.single ? {} : [] - const proxy = new Proxy(target, sharedHandler) - target[$buffer] = buffer - target[$schema] = schema - return proxy + const single = schema.type === ReaderSchemaEnum.single + const stub = single ? {} : [] + const result = single ? {} : [] + const proxy = new Proxy(stub, handler) + + result[$buffer] = buffer + result[$schema] = schema + stub[$result] = result + // @ts-ignore + result.__proto__ = proxy + return result } diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 76c5a390f0..3c1ac5b145 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -41,6 +41,36 @@ type TypeMap = { // Helper to check if Selection is provided (not never/any/unknown default behavior) type IsSelected = [T] extends [never] ? false : true +export type FilterEdges = { + [K in keyof T as K extends `$${string}` ? K : never]: T[K] +} + +export type PickOutputFromProps< + S extends { types: any; locales?: any }, + Props, + K, +> = { + [P in Extract | 'id']: P extends keyof Props + ? IsRefProp extends true + ? Props[P] extends { items: any } + ? { id: number }[] + : { id: number } + : InferProp< + Props[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : never +} & { + [Item in Extract as Item['field'] & + keyof Props]: InferProp< + Props[Item['field'] & keyof Props], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + Item['select'] + > +} + export type InferProp< Prop, Types, @@ -57,13 +87,29 @@ export type InferProp< : Prop extends { ref: infer R extends string } ? IsSelected extends true ? R extends keyof Types - ? PickOutput<{ types: Types; locales: Locales }, R, Selection> + ? PickOutputFromProps< + { types: Types; locales: Locales }, + ResolvedProps & FilterEdges, + ResolveInclude< + ResolvedProps & FilterEdges, + Selection + > + > : never : number // ID - : Prop extends { items: { ref: infer R extends string } } + : Prop extends { + items: { ref: infer R extends string } & infer Items + } ? IsSelected extends true ? R extends keyof Types - ? PickOutput<{ types: Types; locales: Locales }, R, Selection>[] + ? PickOutputFromProps< + { types: Types; locales: Locales }, + ResolvedProps & FilterEdges, + ResolveInclude< + ResolvedProps & FilterEdges, + Selection + > + >[] : never : number[] // IDs : unknown @@ -168,11 +214,15 @@ export type Path = [ [K in keyof ResolvedProps & string]: | K | (ResolvedProps[K] extends { ref: infer R extends string } - ? `${K}.${Path}` + ? `${K}.${ + | Path + | (keyof FilterEdges[K]> & string)}` : ResolvedProps[K] extends { - items: { ref: infer R extends string } + items: { ref: infer R extends string } & infer Items } - ? `${K}.${Path}` + ? `${K}.${ + | Path + | (keyof FilterEdges & string)}` : never) }[keyof ResolvedProps & string] @@ -182,7 +232,7 @@ export type ResolveDotPath = : T export type InferPathType< - S extends { types: any }, + S extends { types: any; locales?: any }, T extends keyof S['types'], P, > = P extends keyof ResolvedProps @@ -192,11 +242,24 @@ export type InferPathType< ? ResolvedProps[Head] extends { ref: infer R extends string } - ? InferPathType + ? Tail extends keyof FilterEdges[Head]> + ? InferProp< + ResolvedProps[Head][Tail & + keyof ResolvedProps[Head]], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : InferPathType : ResolvedProps[Head] extends { - items: { ref: infer R extends string } + items: { ref: infer R extends string } & infer Items } - ? InferPathType + ? Tail extends keyof FilterEdges + ? InferProp< + Items[Tail & keyof Items], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : InferPathType : never : never : never diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index 59e1635ac3..77e5668332 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -76,6 +76,7 @@ const walk = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { const prop = tree.get(field) const astProp = ast.props[field] const include = astProp.include + if (isPropDef(prop)) { if (prop.type === PropType.references) { references(astProp, ctx, prop) diff --git a/src/protocol/db-read/read.ts b/src/protocol/db-read/read.ts index bca63289b9..01a4a83e56 100644 --- a/src/protocol/db-read/read.ts +++ b/src/protocol/db-read/read.ts @@ -165,6 +165,7 @@ export const resultToObject = ( result: Uint8Array, end: number, offset: number = 0, + items: AggItem | [Item] = [], ) => { if (q.aggregate) { return readAggregate(q, result, 0, result.byteLength - 4) @@ -176,10 +177,9 @@ export const resultToObject = ( if (q.type === ReaderSchemaEnum.single) { return null } - return [] + return items } - let items: AggItem | [Item] = [] let i = 5 + offset const readHook = q.hook diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 3dc25d180c..9b6920e2e3 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -16,6 +16,9 @@ await test('include', async (t) => { t.after(() => db.destroy()) const client = await db.setSchema({ types: { + friend: { + y: 'uint16', + }, user: { name: 'string', x: 'boolean', diff --git a/test/query/ast.ts b/test/query/ast.ts index e23916f39c..79366406be 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -5,6 +5,15 @@ import test from '../shared/test.js' await test('query types', async (t) => { const db = await testDb(t, { types: { + soAnnoy: { + title: 'string', + users: { + items: { + ref: 'user', + prop: 'annoyingThings', + }, + }, + }, user: { name: 'string', isNice: 'boolean', @@ -12,33 +21,86 @@ await test('query types', async (t) => { items: { ref: 'user', prop: 'otherUsers', + $role: 'string', }, }, }, }, }) - db.create('user', { + const userA = db.create('user', { isNice: true, + // annoyingThings: [] + }) + + db.create('soAnnoy', { + title: 'super annoying', + users: [userA], }) - const query = db.query2('user').include('isNice', 'name', 'otherUsers.name') + const query = db + .query2('user') + .include( + 'isNice', + 'name', + 'otherUsers.$role', + 'otherUsers.name', + 'otherUsers.isNice', + ) + + const result = await query.get() + + for (const { name, isNice, otherUsers } of result) { + for (const item of otherUsers) { + const name: string = item.name + const isNice: boolean = item.isNice + const id: number = item.id + const $role: string = item.$role + } + } + // .include((select) => // select('otherUsers').include('name').filter('name', '=', 'youzi'), // ) // .filter('otherUsers.name', '=', 'youzi') - console.dir(query.ast, { depth: null }) - const proxy = await query.get() - console.log('-----------') - proxy - console.log('-----------1') - // proxy.forEach((a, b, c) => console.log('WAZZUP', { a, b, c })) - for (const i of proxy) { - console.log({ i }) - } + // console.dir(query.ast, { depth: null }) + // const proxy = await query.get() + // console.log('-----------', proxy) + // proxy + // console.log('-----------1') + // // proxy.forEach((a, b, c) => console.log('WAZZUP', { a, b, c })) + // for (const i of proxy) { + // console.log({ i }) + // } + + // console.log('-->', proxy[$buffer]) + // console.log('???', proxy) + // console.log('WOW', proxy[0]) + // console.log('json', JSON.stringify(proxy)) + // for (const i of proxy) { + // console.log({ i }) + // } + + // // + + // const $result = Symbol() + // const tmp = [] + // const proxy = new Proxy(tmp, { + // get(a, b) { + // console.log('get youzi') + // // @ts-ignore + // tmp[$result].__proto__ = { bla: true } + // return a[b] + // }, + // }) - console.log('-->', proxy[$buffer]) + // const result = tmp[$result] = { + // __proto__: proxy, + // } - // + // // @ts-ignore + // console.log('--->', result.bla) + // // @ts-ignore + // console.log('--->', result.bla) }) From 372244eb9952dfb87f9f97a4be7591978fcb7a5f Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 08:47:06 +0100 Subject: [PATCH 171/449] Vector fixes - Remove support for `number` as an alias for `float64` - Fix vector reader --- src/db-client/query/queryDefToReadSchema.ts | 16 +++++++++++++--- src/schema/def/typeDef.ts | 2 +- src/schema/def/utils.ts | 2 +- src/schema/defs/props/vector.ts | 21 +++++++++++++-------- src/schema/schema/vector.ts | 13 +++++++++++-- test/vector.ts | 3 ++- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/db-client/query/queryDefToReadSchema.ts b/src/db-client/query/queryDefToReadSchema.ts index 1bd47c8980..7549eb2471 100644 --- a/src/db-client/query/queryDefToReadSchema.ts +++ b/src/db-client/query/queryDefToReadSchema.ts @@ -1,6 +1,15 @@ import { IncludeOpts, QueryDef, Target } from './types.js' -import { LangCode, LangCodeInverse, PropType } from '../../zigTsExports.js' -import { type PropDef, type PropDefEdge } from '../../schema/index.js' +import { + LangCode, + LangCodeInverse, + PropType, + VectorBaseType, +} from '../../zigTsExports.js' +import { + SchemaVector, + type PropDef, + type PropDefEdge, +} from '../../schema/index.js' import { ReaderMeta, ReaderSchemaEnum, @@ -34,7 +43,8 @@ const createReaderPropDef = ( readerPropDef.enum = p.enum } if (p.typeIndex === PropType.vector || p.typeIndex === PropType.colVec) { - readerPropDef.vectorBaseType = p.vectorBaseType + readerPropDef.vectorBaseType = + VectorBaseType[(p.schema as SchemaVector).baseType] readerPropDef.len = p.len } if (p.typeIndex === PropType.cardinality) { diff --git a/src/schema/def/typeDef.ts b/src/schema/def/typeDef.ts index 09152b64ad..1102e220ed 100644 --- a/src/schema/def/typeDef.ts +++ b/src/schema/def/typeDef.ts @@ -279,7 +279,7 @@ const createSchemaTypeDef = ( prop.typeIndex === PropType.colVec ) { prop.vectorBaseType = schemaVectorBaseTypeToEnum( - ('baseType' in schemaProp && schemaProp.baseType) || 'number', + ('baseType' in schemaProp && schemaProp.baseType) || 'float64', ) } diff --git a/src/schema/def/utils.ts b/src/schema/def/utils.ts index c2bc358471..ec1e90a822 100644 --- a/src/schema/def/utils.ts +++ b/src/schema/def/utils.ts @@ -53,7 +53,7 @@ export const propIsNumerical = (prop: PropDef | PropDefEdge) => { export const schemaVectorBaseTypeToEnum = ( vector: SchemaVector['baseType'], ): VectorBaseTypeEnum => { - if (vector === 'number' || vector === undefined) { + if (vector === undefined) { return VectorBaseType.float64 } return VectorBaseTypeInverse[vector] diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 58fdb70af2..6c6401713b 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -14,7 +14,6 @@ import type { TypeDef } from '../index.js' import { isTypedArray } from 'util/types' const baseTypeSize: { [K in SchemaVector['baseType']]: number } = { - number: 8, int8: 1, uint8: 1, int16: 2, @@ -32,15 +31,18 @@ export const vector = class Vector extends BasePropDef { } vectorSize: number override type: PropTypeEnum = PropType.vector + override validate(value: unknown): asserts value is Uint8Array { + if (!isTypedArray(value)) { + throw new Error('Not a typed array') + } + } override pushValue( buf: AutoSizedUint8Array, value: unknown, _op?: ModifyEnum, _lang?: LangCodeEnum, ): asserts value is any { - if (!isTypedArray(value)) { - throw new Error('Not a typed array') - } + this.validate(value) const v = new Uint8Array(value.buffer).subarray( 0, Math.min(value.byteLength, this.vectorSize), @@ -69,15 +71,18 @@ export const colvec = class ColVec extends BasePropDef { compSize: number vecLen: number override type = PropType.colVec + override validate(value: unknown): asserts value is Uint8Array { + if (!isTypedArray(value)) { + throw new Error('Not a typed array') + } + } override pushValue( buf: AutoSizedUint8Array, value: unknown, _op: ModifyEnum, _lang: LangCodeEnum, ): asserts value is any { - if (!isTypedArray(value)) { - throw new Error('Not a typed array') - } + this.validate(value) const v = new Uint8Array(value.buffer).subarray( 0, Math.min(value.byteLength, this.vecLen), @@ -89,7 +94,7 @@ export const colvec = class ColVec extends BasePropDef { type: PropTypeSelva.colVec, vecLen: this.vecLen, compSize: this.compSize, - hasDefault: 0, + hasDefault: 0, // TODO default }) } } diff --git a/src/schema/schema/vector.ts b/src/schema/schema/vector.ts index 54a459ff93..356f64abae 100644 --- a/src/schema/schema/vector.ts +++ b/src/schema/schema/vector.ts @@ -1,8 +1,17 @@ import { assert, isNatural, isString } from './shared.js' import { parseBase, type Base } from './base.js' -import { numberTypes } from './number.js' -const vectorBaseTypes = [...numberTypes, 'float32', 'float64'] as const +// TODO This should probably come from zigTsExports.ts +const vectorBaseTypes = [ + 'int8', + 'uint8', + 'int16', + 'uint16', + 'int32', + 'uint32', + 'float32', + 'float64', +] as const export type SchemaVector = Base & { type: 'vector' | 'colvec' diff --git a/test/vector.ts b/test/vector.ts index e6d8ce70a0..733c874aad 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -49,7 +49,8 @@ async function initDb(t) { await test('vector set/get', async (t) => { const db = await initDb(t) - const res = (await db.query('data').get()).toObject() + + const res = (await db.query('data').include('name', 'a').get()).toObject() for (const r of res) { const a = new Uint8Array(r.a.buffer, 0, r.a.byteLength) const b = new Uint8Array(new Float32Array(data[r.name]).buffer) From 7ec4e4f59d6c1c635f6b6602e57105ca72311a10 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 09:33:51 +0100 Subject: [PATCH 172/449] Better name for the test case --- test/number.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/number.ts b/test/number.ts index b807adc8f1..9efebf94c7 100644 --- a/test/number.ts +++ b/test/number.ts @@ -2,7 +2,7 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' -await test('kkk', async (t) => { +await test('basic', async (t) => { const db = new BasedDb({ path: t.tmp, }) From cdc698de44dcb2e4885f656f165aa965b046c945 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 10:14:52 +0100 Subject: [PATCH 173/449] update query2 --- src/db-client/query2/index.ts | 3 ++ src/db-client/query2/result.ts | 15 ++++++-- src/db-query/ast/ast.ts | 2 +- src/db-query/ast/include.ts | 70 ++++++++++++++++++++++------------ test/modify/props/boolean.ts | 45 +++++++++++----------- test/query/ast.ts | 54 ++++---------------------- 6 files changed, 91 insertions(+), 98 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 6269217d1d..649d7b1a8c 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -139,6 +139,9 @@ export class BasedQuery2< ? PickOutput, K>> : PickOutput, K>>[] > { + if (!this.ast.props && !this.ast.include) { + this.include('*') + } if (!this.db.schema) { await this.db.once('schema') } diff --git a/src/db-client/query2/result.ts b/src/db-client/query2/result.ts index 763c3d0d43..aaab113ddc 100644 --- a/src/db-client/query2/result.ts +++ b/src/db-client/query2/result.ts @@ -3,6 +3,7 @@ import { type ReaderSchema, ReaderSchemaEnum, } from '../../protocol/index.js' +import { readUint32 } from '../../utils/uint8.js' export const $buffer = Symbol() export const $schema = Symbol() @@ -10,6 +11,7 @@ export const $result = Symbol() const define = (result: any) => { result.__proto__ = [] + if ('length' in result) result.length = 0 const data = resultToObject( result[$schema], result[$buffer], @@ -51,11 +53,16 @@ const handler: ProxyHandler = { } export const proxyResult = (buffer: Uint8Array, schema: ReaderSchema) => { - const single = schema.type === ReaderSchemaEnum.single - const stub = single ? {} : [] - const result = single ? {} : [] + let stub, result + if (schema.type === ReaderSchemaEnum.single) { + stub = {} + result = {} + } else { + stub = [] + result = [] + result.length = readUint32(buffer, 0) + } const proxy = new Proxy(stub, handler) - result[$buffer] = buffer result[$schema] = schema stub[$result] = result diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 191e5e95f4..9920c6559e 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -49,7 +49,7 @@ export type FilterAst = { } export type Include = { - glob?: '*' | '**' + // glob?: '*' | '**' // youri thinks we can just do these as props meta?: true | 'only' | false maxChars?: number maxBytes?: number diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index 77e5668332..7c58a79f2e 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -68,37 +68,59 @@ const includeMainProps = ( } } -const walk = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { +const walkProp = ( + astProp: QueryAst, + ctx: Ctx, + typeDef: TypeDef, + walkCtx: WalkCtx, + field: string, +) => { const { main, tree } = walkCtx + const prop = tree.get(field) + const include = astProp.include + + if (isPropDef(prop)) { + if (prop.type === PropType.references) { + references(astProp, ctx, prop) + } else if (prop.type === PropType.reference) { + reference(astProp, ctx, prop) + } else if (include) { + if (prop.id === 0) { + main.push({ prop, include }) + } else { + includeProp(ctx, prop, include) + } + } + } else { + if (prop) { + walk(astProp, ctx, typeDef, { + main, + tree: prop, + }) + } else { + // if EN, if NL + throw new Error(`Prop does not exist ${field}`) + } + } +} + +const walk = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { // if ast.include.glob === '*' include all from schema + // youri thinks we can just set this as a field, simpler (also for nested things like bla.**.id) // same for ast.include.glob === '**' for (const field in ast.props) { - const prop = tree.get(field) const astProp = ast.props[field] - const include = astProp.include - - if (isPropDef(prop)) { - if (prop.type === PropType.references) { - references(astProp, ctx, prop) - } else if (prop.type === PropType.reference) { - reference(astProp, ctx, prop) - } else if (include) { - if (prop.id === 0) { - main.push({ prop, include }) - } else { - includeProp(ctx, prop, include) - } + if (field === '*') { + for (const [field, prop] of typeDef.tree) { + if ('ref' in prop) continue + walkProp(astProp, ctx, typeDef, walkCtx, field) } - } else { - if (prop) { - walk(astProp, ctx, typeDef, { - main, - tree: prop, - }) - } else { - // if EN, if NL - throw new Error(`Prop does not exist ${field}`) + } else if (field === '**') { + for (const [field, prop] of typeDef.tree) { + if ('ref' in prop) walkProp(astProp, ctx, typeDef, walkCtx, field) } + } else { + walkProp(astProp, ctx, typeDef, walkCtx, field) } } return walkCtx diff --git a/test/modify/props/boolean.ts b/test/modify/props/boolean.ts index 3963caa948..7daa11ff9a 100644 --- a/test/modify/props/boolean.ts +++ b/test/modify/props/boolean.ts @@ -2,7 +2,7 @@ import { deepEqual } from '../../shared/assert.js' import { testDb } from '../../shared/index.js' import test from '../../shared/test.js' -await test('modify boolean', async (t) => { +await test('modify basic boolean', async (t) => { const db = await testDb(t, { types: { user: { @@ -15,7 +15,7 @@ await test('modify boolean', async (t) => { const b = db.create('user', { isNice: true }) const c = db.create('user', { isNice: false }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, isNice: false }, { id: 2, isNice: true }, { id: 3, isNice: false }, @@ -25,7 +25,7 @@ await test('modify boolean', async (t) => { db.update('user', b, { isNice: true }) db.update('user', c, { isNice: true }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, isNice: true }, { id: 2, isNice: true }, { id: 3, isNice: true }, @@ -34,7 +34,7 @@ await test('modify boolean', async (t) => { db.update('user', a, { isNice: false }) db.update('user', b, { isNice: false }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, isNice: false }, { id: 2, isNice: false }, { id: 3, isNice: true }, @@ -45,7 +45,7 @@ await test('modify boolean', async (t) => { db.update('user', b, { isNice: null }) db.update('user', c, { isNice: null }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, isNice: false }, { id: 2, isNice: false }, { id: 3, isNice: false }, @@ -90,27 +90,26 @@ await test('modify boolean on edge', async (t) => { // Basic creates // Check a (default false?) const resA = await db - .query('holder', await a) + .query2('holder', await a) .include('toUser.$edgeBool') .get() - .toObject() deepEqual(resA.toUser?.$edgeBool, false) // Check b (true) const resB = await db - .query('holder', await b) + .query2('holder', await b) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resB.toUser?.$edgeBool, true) // Check c (false) const resC = await db - .query('holder', await c) + .query2('holder', await c) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resC.toUser?.$edgeBool, false) // Updates to true @@ -119,22 +118,22 @@ await test('modify boolean on edge', async (t) => { db.update('holder', await c, { toUser: { id: u1, $edgeBool: true } }) const resA2 = await db - .query('holder', await a) + .query2('holder', await a) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resA2.toUser?.$edgeBool, true) const resB2 = await db - .query('holder', await b) + .query2('holder', await b) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resB2.toUser?.$edgeBool, true) const resC2 = await db - .query('holder', await c) + .query2('holder', await c) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resC2.toUser?.$edgeBool, true) // Updates to false @@ -143,21 +142,21 @@ await test('modify boolean on edge', async (t) => { db.update('holder', await c, { toUser: { id: u1, $edgeBool: false } }) const resA3 = await db - .query('holder', await a) + .query2('holder', await a) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resA3.toUser?.$edgeBool, false) const resB3 = await db - .query('holder', await b) + .query2('holder', await b) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resB3.toUser?.$edgeBool, false) const resC3 = await db - .query('holder', await c) + .query2('holder', await c) .include('toUser.$edgeBool') .get() - .toObject() + deepEqual(resC3.toUser?.$edgeBool, false) }) diff --git a/test/query/ast.ts b/test/query/ast.ts index 79366406be..9ed8307f12 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -17,6 +17,11 @@ await test('query types', async (t) => { user: { name: 'string', isNice: 'boolean', + friend: { + ref: 'user', + prop: 'friend', + $rank: 'number', + }, otherUsers: { items: { ref: 'user', @@ -43,6 +48,7 @@ await test('query types', async (t) => { .include( 'isNice', 'name', + 'friend.$rank', 'otherUsers.$role', 'otherUsers.name', 'otherUsers.isNice', @@ -50,7 +56,8 @@ await test('query types', async (t) => { const result = await query.get() - for (const { name, isNice, otherUsers } of result) { + for (const { name, isNice, otherUsers, friend } of result) { + const $rank: number = friend.$rank for (const item of otherUsers) { const name: string = item.name const isNice: boolean = item.isNice @@ -58,49 +65,4 @@ await test('query types', async (t) => { const $role: string = item.$role } } - - // .include((select) => - // select('otherUsers').include('name').filter('name', '=', 'youzi'), - // ) - // .filter('otherUsers.name', '=', 'youzi') - - // console.dir(query.ast, { depth: null }) - // const proxy = await query.get() - // console.log('-----------', proxy) - // proxy - // console.log('-----------1') - // // proxy.forEach((a, b, c) => console.log('WAZZUP', { a, b, c })) - // for (const i of proxy) { - // console.log({ i }) - // } - - // console.log('-->', proxy[$buffer]) - // console.log('???', proxy) - // console.log('WOW', proxy[0]) - // console.log('json', JSON.stringify(proxy)) - // for (const i of proxy) { - // console.log({ i }) - // } - - // // - - // const $result = Symbol() - // const tmp = [] - // const proxy = new Proxy(tmp, { - // get(a, b) { - // console.log('get youzi') - // // @ts-ignore - // tmp[$result].__proto__ = { bla: true } - // return a[b] - // }, - // }) - - // const result = tmp[$result] = { - // __proto__: proxy, - // } - - // // @ts-ignore - // console.log('--->', result.bla) - // // @ts-ignore - // console.log('--->', result.bla) }) From 630a07062f83b8fda486c03f687ab837cea26312 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 10:17:15 +0100 Subject: [PATCH 174/449] add capped to references --- src/schema/schema/references.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/schema/schema/references.ts b/src/schema/schema/references.ts index 0840317095..741fef9e6c 100644 --- a/src/schema/schema/references.ts +++ b/src/schema/schema/references.ts @@ -1,7 +1,7 @@ import { parseBase, type Base } from './base.js' import { parseReference, type SchemaReference } from './reference.js' import type { SchemaOut } from './schema.js' -import { assert, isRecord, type RequiredIfStrict } from './shared.js' +import { assert, isNatural, isRecord, type RequiredIfStrict } from './shared.js' export type SchemaReferences = Base & RequiredIfStrict<{ type: 'references' }, strict> & { @@ -14,8 +14,13 @@ export const parseReferences = ( locales: SchemaOut['locales'], ): SchemaReferences => { assert(isRecord(def.items), 'Items should be record') + assert( + def.capped === undefined || isNatural(def.capped), + 'Capped should be a number', + ) return parseBase(def, { type: 'references', + capped: def.capped, items: parseReference(def.items, locales, true), }) } From a245f3b7a8c66c4be9813792fe6a53865a00a854 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 10:17:17 +0100 Subject: [PATCH 175/449] Fix text fallbacks in tests --- test/edges/edgeText.ts | 2 +- test/exporter.ts | 4 ++-- test/instantModify.ts | 5 +++-- test/text/text.ts | 4 ++-- test/text/textFallback.ts | 4 ++-- test/validation/validation.ts | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/test/edges/edgeText.ts b/test/edges/edgeText.ts index aeca3a324a..f65aeaedb5 100644 --- a/test/edges/edgeText.ts +++ b/test/edges/edgeText.ts @@ -13,7 +13,7 @@ await test.skip('text in an edge prop', async (t) => { await db.setSchema({ locales: { en: {}, - it: { fallback: 'en' }, + it: { fallback: ['en'] }, }, types: { user: { diff --git a/test/exporter.ts b/test/exporter.ts index 8129e00e1a..a8eeab3404 100644 --- a/test/exporter.ts +++ b/test/exporter.ts @@ -51,8 +51,8 @@ await test('export to csv', async (t) => { await db.setSchema({ locales: { en: {}, - it: { fallback: 'en' }, - fi: { fallback: 'en' }, + it: { fallback: ['en'] }, + fi: { fallback: ['en'] }, }, types: { product: { diff --git a/test/instantModify.ts b/test/instantModify.ts index b709f1f070..dfea4e6d31 100644 --- a/test/instantModify.ts +++ b/test/instantModify.ts @@ -4,6 +4,7 @@ import test from './shared/test.js' import { dirname, join } from 'path' import { fileURLToPath } from 'url' import { Worker } from 'node:worker_threads' + await test.skip('instantModify', async (t) => { const db = new BasedDb({ path: t.tmp, @@ -16,8 +17,8 @@ await test.skip('instantModify', async (t) => { await db.setSchema({ locales: { en: {}, - it: { fallback: 'en' }, - fi: { fallback: 'en' }, + it: { fallback: ['en'] }, + fi: { fallback: ['en'] }, }, types: { country: { diff --git a/test/text/text.ts b/test/text/text.ts index 93c049af5a..942d51e56f 100644 --- a/test/text/text.ts +++ b/test/text/text.ts @@ -534,8 +534,8 @@ await test('sort', async (t) => { await db.setSchema({ locales: { en: {}, - it: { fallback: 'en' }, - fi: { fallback: 'en' }, + it: { fallback: ['en'] }, + fi: { fallback: ['en'] }, }, types: { dialog: { diff --git a/test/text/textFallback.ts b/test/text/textFallback.ts index cbb5a8fecf..d985575bb4 100644 --- a/test/text/textFallback.ts +++ b/test/text/textFallback.ts @@ -12,8 +12,8 @@ await test('textFallback', async (t) => { await db.setSchema({ locales: { - en: true, // do not know what required means - nl: { fallback: 'en' }, + en: true, + nl: { fallback: ['en'] }, }, types: { project: { diff --git a/test/validation/validation.ts b/test/validation/validation.ts index 9707b1313f..6beb4bf5f7 100644 --- a/test/validation/validation.ts +++ b/test/validation/validation.ts @@ -482,8 +482,8 @@ await test('query', async (t) => { await db.setSchema({ locales: { en: {}, - it: { fallback: 'en' }, - fi: { fallback: 'en' }, + it: { fallback: ['en'] }, + fi: { fallback: ['en'] }, }, types: { todo: { From 605ab9200f049d2064bea8ed040e3ac011819965 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 13:44:51 +0100 Subject: [PATCH 176/449] Add back capped types --- native/modify/modify.zig | 23 +++++++- native/selva/node.zig | 4 +- native/types.zig | 16 ++++-- src/db-client/modify/create.ts | 34 +++++++++--- src/zigTsExports.ts | 99 +++++++++++++++++++++++++++++++--- 5 files changed, 153 insertions(+), 23 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index dd9a7bed46..c07057b7c4 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -280,7 +280,28 @@ pub fn modify( db.ids[create.type - 1] = id; utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); - selva.markDirty(db, create.type, id); + i += create.size; + }, + .createRing => { + const create = utils.read(t.ModifyCreateRingHeader, buf, i); + i += utils.sizeOf(t.ModifyCreateRingHeader); + const typeEntry = try Node.getType(db, create.type); + const data: []u8 = buf[i .. i + create.size]; + const nextId = db.ids[create.type - 1] % create.maxNodeId + 1; + + var node = Node.getNode(typeEntry, nextId); + if (node) |oldNode| { + Node.flushNode(db, typeEntry, oldNode); + } else { + node = try Node.upsertNode(typeEntry, nextId); + } + + modifyProps(db, typeEntry, node.?, data, items) catch { + // handle errors + }; + db.ids[create.type - 1] = nextId; + utils.write(result, nextId, j); + utils.write(result, t.ModifyError.null, j + 4); i += create.size; }, .update => { diff --git a/native/selva/node.zig b/native/selva/node.zig index 0a80dbf5c9..9062ed688f 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -169,8 +169,8 @@ pub inline fn deleteNode(db: *DbCtx, typeEntry: Type, node: Node) !void { selva.c.selva_del_node(db.selva, typeEntry, node); } -pub inline fn flushNode(ctx: *Modify.ModifyCtx, typeEntry: Type, node: Node) void { - selva.c.selva_flush_node(ctx.db.selva, typeEntry, node); +pub inline fn flushNode(db: *DbCtx, typeEntry: Type, node: Node) void { + selva.c.selva_flush_node(db.selva, typeEntry, node); } pub inline fn expireNode(ctx: *Modify.ModifyCtx, typeId: t.TypeId, nodeId: u32, ts: i64) void { diff --git a/native/types.zig b/native/types.zig index 22feadcd8e..fe57e598d7 100644 --- a/native/types.zig +++ b/native/types.zig @@ -79,10 +79,11 @@ pub const ModOp = enum(u8) { pub const Modify = enum(u8) { create = 0, - update = 1, - delete = 2, - upsert = 3, - insert = 4, + createRing = 1, + update = 2, + delete = 3, + upsert = 4, + insert = 5, }; pub const ModifyHeader = packed struct { @@ -115,6 +116,13 @@ pub const ModifyCreateHeader = packed struct { size: u32, }; +pub const ModifyCreateRingHeader = packed struct { + op: Modify, + type: u8, + maxNodeId: u32, + size: u32, +}; + pub const ModifyIncrement = enum(u8) { none = 0, increment = 1, diff --git a/src/db-client/modify/create.ts b/src/db-client/modify/create.ts index 143a308c6c..18b317306e 100644 --- a/src/db-client/modify/create.ts +++ b/src/db-client/modify/create.ts @@ -4,6 +4,8 @@ import { Modify, pushModifyCreateHeader, writeModifyCreateHeaderProps, + pushModifyCreateRingHeader, + writeModifyCreateRingHeaderProps, type LangCodeEnum, } from '../../zigTsExports.js' import { getTypeDef } from './index.js' @@ -21,12 +23,28 @@ export const serializeCreate = < lang: LangCodeEnum, ) => { const typeDef = getTypeDef(schema, type) - const index = pushModifyCreateHeader(buf, { - op: Modify.create, - type: typeDef.id, - size: 0, - }) - const start = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.create, lang) - writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) + const maxNodeId = typeDef.schema.capped ?? 0 + + if (maxNodeId ?? 0 > 0) { + const index = pushModifyCreateRingHeader(buf, { + op: Modify.createRing, + type: typeDef.id, + maxNodeId, + size: 0, + }) + const start = buf.length + + serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + writeModifyCreateRingHeaderProps.size(buf.data, buf.length - start, index) + } else { + const index = pushModifyCreateHeader(buf, { + op: Modify.create, + type: typeDef.id, + size: 0, + }) + const start = buf.length + + serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) + } } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 54239d1665..76156e4515 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -198,22 +198,25 @@ export type ModOpEnum = (typeof ModOp)[keyof typeof ModOp] export const Modify = { create: 0, - update: 1, - delete: 2, - upsert: 3, - insert: 4, + createRing: 1, + update: 2, + delete: 3, + upsert: 4, + insert: 5, } as const export const ModifyInverse = { 0: 'create', - 1: 'update', - 2: 'delete', - 3: 'upsert', - 4: 'insert', + 1: 'createRing', + 2: 'update', + 3: 'delete', + 4: 'upsert', + 5: 'insert', } as const /** create, + createRing, update, delete, upsert, @@ -549,6 +552,86 @@ export const pushModifyCreateHeader = ( return index } +export type ModifyCreateRingHeader = { + op: ModifyEnum + type: number + maxNodeId: number + size: number +} + +export const ModifyCreateRingHeaderByteSize = 10 + +export const ModifyCreateRingHeaderAlignOf = 16 + +export const writeModifyCreateRingHeader = ( + buf: Uint8Array, + header: ModifyCreateRingHeader, + offset: number, +): number => { + buf[offset] = Number(header.op) + offset += 1 + buf[offset] = Number(header.type) + offset += 1 + writeUint32(buf, Number(header.maxNodeId), offset) + offset += 4 + writeUint32(buf, Number(header.size), offset) + offset += 4 + return offset +} + +export const writeModifyCreateRingHeaderProps = { + op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { + buf[offset] = Number(value) + }, + type: (buf: Uint8Array, value: number, offset: number) => { + buf[offset + 1] = Number(value) + }, + maxNodeId: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 2) + }, + size: (buf: Uint8Array, value: number, offset: number) => { + writeUint32(buf, Number(value), offset + 6) + }, +} + +export const readModifyCreateRingHeader = ( + buf: Uint8Array, + offset: number, +): ModifyCreateRingHeader => { + const value: ModifyCreateRingHeader = { + op: (buf[offset]) as ModifyEnum, + type: buf[offset + 1], + maxNodeId: readUint32(buf, offset + 2), + size: readUint32(buf, offset + 6), + } + return value +} + +export const readModifyCreateRingHeaderProps = { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => buf[offset + 1], + maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), +} + +export const createModifyCreateRingHeader = (header: ModifyCreateRingHeader): Uint8Array => { + const buffer = new Uint8Array(ModifyCreateRingHeaderByteSize) + writeModifyCreateRingHeader(buffer, header, 0) + return buffer +} + +export const pushModifyCreateRingHeader = ( + buf: AutoSizedUint8Array, + header: ModifyCreateRingHeader, +): number => { + const index = buf.length + buf.pushUint8(Number(header.op)) + buf.pushUint8(Number(header.type)) + buf.pushUint32(Number(header.maxNodeId)) + buf.pushUint32(Number(header.size)) + return index +} + export const ModifyIncrement = { none: 0, increment: 1, From 77565c6942aeac4aaea345f5dcf691d8139596c0 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 15:09:50 +0100 Subject: [PATCH 177/449] make query2 work --- native/query/include/append.zig | 2 +- native/query/include/include.zig | 8 +-- native/query/multiple.zig | 9 ++- native/query/single.zig | 11 ++- src/db-client/index.ts | 6 +- src/db-client/query/queryDefToReadSchema.ts | 1 + src/db-client/query2/index.ts | 63 ++++++++--------- src/db-client/query2/result.ts | 41 +++++++---- src/db-client/query2/types.ts | 76 +++++++++++---------- src/db-query/ast/ast.ts | 2 +- src/db-query/ast/include.ts | 39 +++++++---- src/db-query/ast/multiple.ts | 5 +- src/db-query/ast/readSchema.ts | 17 ++++- src/db-query/ast/single.ts | 59 ++++++++++++++-- src/db-query/ast/toCtx.ts | 6 +- src/protocol/db-read/main.ts | 13 +++- src/protocol/db-read/prop.ts | 1 + src/protocol/db-read/read.ts | 5 +- src/schema/defs/getTypeDefs.ts | 14 ++-- src/schema/defs/index.ts | 3 +- test/modify/props/alias.ts | 16 ++--- test/modify/props/binary.ts | 19 ++---- test/modify/props/boolean.ts | 22 ++---- test/modify/props/cardinality.ts | 19 +++--- test/modify/props/default.ts | 15 ++-- test/modify/props/enum.ts | 12 ++-- test/modify/props/json.ts | 18 ++--- test/modify/props/mixed.ts | 11 ++- test/modify/props/numbers.ts | 13 ++-- test/modify/props/object.ts | 21 ++---- test/modify/props/references.ts | 23 ++++--- test/modify/props/string.ts | 66 ++++++------------ test/modify/props/text.ts | 21 ++---- test/modify/props/timestamp.ts | 31 ++++----- test/modify/props/vector.ts | 28 +++----- test/number.ts | 1 - test/query-ast/include.ts | 2 +- 37 files changed, 377 insertions(+), 342 deletions(-) diff --git a/native/query/include/append.zig b/native/query/include/append.zig index 25e7469f86..f8941e29a8 100644 --- a/native/query/include/append.zig +++ b/native/query/include/append.zig @@ -1,7 +1,7 @@ const utils = @import("../../utils.zig"); const Thread = @import("../../thread/thread.zig"); const t = @import("../../types.zig"); - +const std = @import("std"); pub inline fn default(thread: *Thread.Thread, prop: u8, value: []u8) !void { if (value.len == 0) { return; diff --git a/native/query/include/include.zig b/native/query/include/include.zig index 7cebe186b3..03edea712d 100644 --- a/native/query/include/include.zig +++ b/native/query/include/include.zig @@ -49,7 +49,7 @@ pub fn include( var i: usize = 0; while (i < q.len) { const op: t.IncludeOp = @enumFromInt(q[i]); - + // std.debug.print("includeop: {any} - {any}\n", .{ op, q }); switch (op) { // add .referenceEdge? .reference => { @@ -122,18 +122,18 @@ pub fn include( const value = try get(typeEntry, node, &header); // std.debug.print("??? value {any} - {any}\n", .{ value, header }); switch (header.propType) { - t.PropType.text, + .text, => { var iter = Fields.textIterator(value); while (iter.next()) |textValue| { try append.stripCrc32(ctx.thread, header.prop, textValue); } }, - t.PropType.binary, t.PropType.string, t.PropType.json => { + .binary, .string, .json => { // utils.printString("derp", value); try append.stripCrc32(ctx.thread, header.prop, value); }, - t.PropType.microBuffer, t.PropType.vector, t.PropType.colVec => { + .microBuffer, .vector, .colVec => { // Fixed size try ctx.thread.query.append(header.prop); try ctx.thread.query.append(value); diff --git a/native/query/multiple.zig b/native/query/multiple.zig index dfb380dd0e..c288d11644 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -86,7 +86,15 @@ fn iteratorEdge( try ctx.thread.query.append(Node.getNodeId(ref.node)); try Include.include(ref.node, ctx, nestedQuery, typeEntry); try ctx.thread.query.append(t.ReadOp.edge); + const edgesByteSizeIndex = try ctx.thread.query.reserve(4); + const edgeStartIndex = ctx.thread.query.index; try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - edgeStartIndex), + edgesByteSizeIndex, + ); + // try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); nodeCnt += 1; if (nodeCnt >= header.limit) { break; @@ -297,7 +305,6 @@ pub fn aggregates( const header = utils.read(t.AggHeader, q, i); i += utils.sizeOf(t.AggHeader); - std.debug.print("header: {any}\n", .{header}); const typeId = header.typeId; const typeEntry = try Node.getType(ctx.db, typeId); const isSamplingSet = header.isSamplingSet; diff --git a/native/query/single.zig b/native/query/single.zig index 43cc8def56..8f825e0825 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -60,6 +60,7 @@ pub fn default( try ctx.thread.query.append(t.ReadOp.id); try ctx.thread.query.append(header.id); const nestedQuery = q[i .. i + header.includeSize]; + try Include.include(node, ctx, nestedQuery, typeEntry); } else { try ctx.thread.query.append(@as(u32, 0)); @@ -105,7 +106,6 @@ pub fn referenceEdge( i: *usize, ) !void { const header = utils.readNext(t.QueryHeaderSingleReference, q, i); - const fs = try Schema.getFieldSchema(fromType, header.prop); if (References.getReference(from, fs)) |ref| { const typeEntry = try Node.getType(ctx.db, header.typeId); @@ -129,10 +129,17 @@ pub fn referenceEdge( if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); + const edgesByteSizeIndex = try ctx.thread.query.reserve(4); + const edgeStartIndex = ctx.thread.query.index; try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - edgeStartIndex), + edgesByteSizeIndex, + ); } - i.* += header.edgeSize; + i.* += header.edgeSize + header.includeSize; ctx.thread.query.writeAs( u32, diff --git a/src/db-client/index.ts b/src/db-client/index.ts index fdab8deb26..1f7d2e6390 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -11,6 +11,7 @@ import { type SchemaOut, type ResolveSchema, type Schema, + type ResolvedProps, } from '../schema/index.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' import { LangCode, Modify } from '../zigTsExports.js' @@ -21,6 +22,7 @@ import { serializeUpdate } from './modify/update.js' import { serializeDelete } from './modify/delete.js' import { serializeUpsert } from './modify/upsert.js' import { BasedQuery2 } from './query2/index.js' +import type { InferSchemaOutput } from './query2/types.js' type DbClientOpts = { hooks: DbClientHooks @@ -100,11 +102,11 @@ export class DbClient extends DbShared { ): BasedQuery2 query2( type: T, - id: number, + id: number | Partial>, ): BasedQuery2 query2( type: T, - id?: number, + id?: number | Partial>, ): BasedQuery2 { return new BasedQuery2(this, type, id) } diff --git a/src/db-client/query/queryDefToReadSchema.ts b/src/db-client/query/queryDefToReadSchema.ts index 7549eb2471..c7a2823b84 100644 --- a/src/db-client/query/queryDefToReadSchema.ts +++ b/src/db-client/query/queryDefToReadSchema.ts @@ -176,6 +176,7 @@ export const convertToReaderSchema = ( for (const [start, p, opts] of q.include.main.include.values()) { readerSchema.main.props[start] = createReaderPropDef(p, locales, opts) } + for (const [k, v] of q.references.entries()) { const target = v.target as Target const propDef = target.propDef! diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 649d7b1a8c..659f52abc6 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -8,6 +8,7 @@ import type { ResolveDotPath, InferPathType, FilterEdges, + InferSchemaOutput, } from './types.js' import type { ResolvedProps, SchemaOut } from '../../schema/index.js' import { astToQueryCtx } from '../../db-query/ast/toCtx.js' @@ -60,35 +61,10 @@ class QueryBranch< EdgeProps > { for (const prop of props as (string | Function)[]) { - let target = this.ast if (typeof prop === 'function') { - prop((prop: string) => { - const path = prop.split('.') - for (const key of path) { - if (key[0] === '$') { - target.edges ??= {} - target.edges.props ??= {} - target = target.edges.props[key] = {} - } else { - target.props ??= {} - target = target.props[key] = {} - } - } - return new QueryBranch(target) - }) + prop((prop: string) => new QueryBranch(traverse(this.ast, prop))) } else { - const path = prop.split('.') - for (const key of path) { - if (key[0] === '$') { - target.edges ??= {} - target.edges.props ??= {} - target = target.edges.props[key] = {} - } else { - target.props ??= {} - target = target.props[key] = {} - } - } - target.include = {} + traverse(this.ast, prop).include = {} } } return this as any @@ -104,12 +80,7 @@ class QueryBranch< val: InferPathType, opts?: FilterOpts, ): this { - let target: FilterLeaf = (this.ast.filter ??= {}) - const path = (prop as string).split('.') - for (const key of path) { - target.props ??= {} - target = target.props[key] = {} - } + const target = traverse((this.ast.filter ??= {}), prop as string) target.ops ??= [] target.ops.push({ op, val }) return this @@ -127,7 +98,11 @@ export class BasedQuery2< | string = '*', IsSingle extends boolean = false, > extends QueryBranch { - constructor(db: DbClient, type: T, target?: number) { + constructor( + db: DbClient, + type: T, + target?: number | Partial>, + ) { super({}) this.ast.type = type as string this.ast.target = target @@ -139,9 +114,11 @@ export class BasedQuery2< ? PickOutput, K>> : PickOutput, K>>[] > { - if (!this.ast.props && !this.ast.include) { + if (!this.ast.props) { this.include('*') } + // console.dir(this.ast, { depth: null }) + if (!this.db.schema) { await this.db.once('schema') } @@ -151,6 +128,7 @@ export class BasedQuery2< this.ast, new AutoSizedUint8Array(1000), ) + // console.dir(ctx.readSchema, { depth: null }) const result = await this.db.hooks.getQueryBuf(ctx.query) return proxyResult(result, ctx.readSchema) as any } @@ -195,3 +173,18 @@ export type ResolveIncludeArgs = T extends ( : T extends string ? ResolveDotPath : T + +function traverse(target: any, prop: string) { + const path = prop.split('.') + for (const key of path) { + if (key[0] === '$') { + target.edges ??= {} + target.edges.props ??= {} + target = target.edges.props[key] ??= {} + } else { + target.props ??= {} + target = target.props[key] ??= {} + } + } + return target +} diff --git a/src/db-client/query2/result.ts b/src/db-client/query2/result.ts index aaab113ddc..61365fdea5 100644 --- a/src/db-client/query2/result.ts +++ b/src/db-client/query2/result.ts @@ -10,16 +10,29 @@ export const $schema = Symbol() export const $result = Symbol() const define = (result: any) => { - result.__proto__ = [] - if ('length' in result) result.length = 0 - const data = resultToObject( - result[$schema], - result[$buffer], - result[$buffer].byteLength - 4, - 0, - result, - ) - if (data !== result) result.__proto__ = data + if ('length' in result) { + result.__proto__ = Array.prototype + result.length = 0 + resultToObject( + result[$schema], + result[$buffer], + result[$buffer].byteLength - 4, + 0, + result, + ) + } else { + result.__proto__ = Object.prototype + Object.assign( + result, + resultToObject( + result[$schema], + result[$buffer], + result[$buffer].byteLength - 4, + 0, + ), + ) + } + Object.defineProperty(result, $buffer, { enumerable: false }) Object.defineProperty(result, $schema, { enumerable: false }) } @@ -37,6 +50,7 @@ const handler: ProxyHandler = { return undefined } } + define(result) return result[prop] }, @@ -53,14 +67,17 @@ const handler: ProxyHandler = { } export const proxyResult = (buffer: Uint8Array, schema: ReaderSchema) => { + const single = schema.type === ReaderSchemaEnum.single + const length = readUint32(buffer, 0) + if (length === 0) return single ? null : [] let stub, result - if (schema.type === ReaderSchemaEnum.single) { + if (single) { stub = {} result = {} } else { stub = [] result = [] - result.length = readUint32(buffer, 0) + result.length = length } const proxy = new Proxy(stub, handler) result[$buffer] = buffer diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 3c1ac5b145..d83692ead5 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -50,17 +50,19 @@ export type PickOutputFromProps< Props, K, > = { - [P in Extract | 'id']: P extends keyof Props - ? IsRefProp extends true - ? Props[P] extends { items: any } - ? { id: number }[] - : { id: number } - : InferProp< - Props[P], - S['types'], - S['locales'] extends Record ? S['locales'] : {} - > - : never + [P in Extract | 'id']: P extends 'id' + ? number + : P extends keyof Props + ? IsRefProp extends true + ? Props[P] extends { items: any } + ? { id: number }[] + : { id: number } + : InferProp< + Props[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : never } & { [Item in Extract as Item['field'] & keyof Props]: InferProp< @@ -216,13 +218,15 @@ export type Path = [ | (ResolvedProps[K] extends { ref: infer R extends string } ? `${K}.${ | Path - | (keyof FilterEdges[K]> & string)}` + | (keyof FilterEdges[K]> & string) + | 'id'}` : ResolvedProps[K] extends { items: { ref: infer R extends string } & infer Items } ? `${K}.${ | Path - | (keyof FilterEdges & string)}` + | (keyof FilterEdges & string) + | 'id'}` : never) }[keyof ResolvedProps & string] @@ -235,31 +239,33 @@ export type InferPathType< S extends { types: any; locales?: any }, T extends keyof S['types'], P, -> = P extends keyof ResolvedProps - ? InferProp[P], S['types']> - : P extends `${infer Head}.${infer Tail}` - ? Head extends keyof ResolvedProps - ? ResolvedProps[Head] extends { - ref: infer R extends string - } - ? Tail extends keyof FilterEdges[Head]> - ? InferProp< - ResolvedProps[Head][Tail & - keyof ResolvedProps[Head]], - S['types'], - S['locales'] extends Record ? S['locales'] : {} - > - : InferPathType - : ResolvedProps[Head] extends { - items: { ref: infer R extends string } & infer Items - } - ? Tail extends keyof FilterEdges +> = P extends 'id' + ? number + : P extends keyof ResolvedProps + ? InferProp[P], S['types']> + : P extends `${infer Head}.${infer Tail}` + ? Head extends keyof ResolvedProps + ? ResolvedProps[Head] extends { + ref: infer R extends string + } + ? Tail extends keyof FilterEdges[Head]> ? InferProp< - Items[Tail & keyof Items], + ResolvedProps[Head][Tail & + keyof ResolvedProps[Head]], S['types'], S['locales'] extends Record ? S['locales'] : {} > : InferPathType - : never + : ResolvedProps[Head] extends { + items: { ref: infer R extends string } & infer Items + } + ? Tail extends keyof FilterEdges + ? InferProp< + Items[Tail & keyof Items], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : InferPathType + : never + : never : never - : never diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 9920c6559e..d8c4c38b19 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -62,7 +62,7 @@ export type QueryAst = { locale?: string range?: { start: number; end: number } type?: string - target?: string | number | (number | string)[] + target?: number | number[] | Record filter?: FilterAst sort?: { prop: string; order: 'asc' | 'desc' } props?: Record diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index 7c58a79f2e..7bd53f1f60 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -52,7 +52,7 @@ const includeMainProps = ( prop: 0, propType: PropType.microBuffer, }) - } else if (props.length > 0) { + } else { pushIncludePartialHeader(ctx.query, { op: IncludeOp.partial, prop: MAIN_PROP, @@ -91,33 +91,42 @@ const walkProp = ( includeProp(ctx, prop, include) } } + } else if (prop) { + walk(astProp, ctx, typeDef, { + main, + tree: prop, + }) } else { - if (prop) { - walk(astProp, ctx, typeDef, { - main, - tree: prop, - }) - } else { - // if EN, if NL - throw new Error(`Prop does not exist ${field}`) - } + // if EN, if NL + throw new Error(`Prop does not exist ${field}`) } } const walk = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { + if (ast.include) { + ast.props ??= {} + ast.props['*'] ??= {} + ast.props['*'].include ??= ast.include + } // if ast.include.glob === '*' include all from schema // youri thinks we can just set this as a field, simpler (also for nested things like bla.**.id) // same for ast.include.glob === '**' for (const field in ast.props) { const astProp = ast.props[field] + if (field === 'id') { + continue + } if (field === '*') { - for (const [field, prop] of typeDef.tree) { - if ('ref' in prop) continue - walkProp(astProp, ctx, typeDef, walkCtx, field) + for (const [field, prop] of walkCtx.tree) { + if (!('ref' in prop)) { + walkProp(astProp, ctx, typeDef, walkCtx, field) + } } } else if (field === '**') { for (const [field, prop] of typeDef.tree) { - if ('ref' in prop) walkProp(astProp, ctx, typeDef, walkCtx, field) + if ('ref' in prop) { + walkProp(astProp, ctx, typeDef, walkCtx, field) + } } } else { walkProp(astProp, ctx, typeDef, walkCtx, field) @@ -132,6 +141,6 @@ export const include = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef): number => { main: [], tree: typeDef.tree, }) - includeMainProps(ctx, main, typeDef) + if (main.length) includeMainProps(ctx, main, typeDef) return ctx.query.length - startIndex } diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 9d036ba7a3..db0be8389e 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -44,7 +44,6 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { if (ast.filter) { const filterSize = filter(ast.filter, ctx, typeDef) - console.log({ filterSize }) props.filterSize(ctx.query.data, filterSize, headerIndex) } @@ -63,7 +62,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { op: QueryType.references, prop: prop.id, includeSize: 0, - typeId: prop.typeDef.id, + typeId: prop.ref!.id, offset: rangeStart, limit: (ast.range?.end || 100) + rangeStart, sort: false, @@ -88,7 +87,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { ...ctx, readSchema: schema, }, - prop.typeDef, + prop.ref!, ) props.includeSize(ctx.query.data, size, headerIndex) diff --git a/src/db-query/ast/readSchema.ts b/src/db-query/ast/readSchema.ts index f3d3f39118..0ea0cb831a 100644 --- a/src/db-query/ast/readSchema.ts +++ b/src/db-query/ast/readSchema.ts @@ -49,9 +49,20 @@ export const readPropDef = ( // opts?.meta === 'only' ? ReaderMeta.only : ReaderMeta.combined // } // } - if (p.type === PropType.enum) { - // console.log(p) - // readerPropDef.enum = p.prop.enum + if ('vals' in p) { + // @ts-ignore TODO make this nice + readerPropDef.enum = Array.from(p.vals.keys()) + } + + if (p.type === PropType.text) { + // @ts-ignore TODO make this nice + readerPropDef.locales = Object.keys(p.typeDef.schemaRoot.locales).reduce( + (map, lang: string) => { + map[LangCode[lang]] = lang + return map + }, + {}, + ) } // if (p.type === PropType.vector || p.type === PropType.colVec) { // readerPropDef.vectorBaseType = p.vectorBaseType diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index c727a3e04b..b946b1d9cd 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -1,19 +1,70 @@ import { ReaderSchemaEnum } from '../../protocol/index.js' -import { PropDef } from '../../schema/defs/index.js' +import { PropDef, type TypeDef } from '../../schema/defs/index.js' import { pushQueryHeaderSingleReference, QueryType, writeQueryHeaderSingleReferenceProps as props, + pushQueryHeaderSingle, + writeQueryHeaderSingleProps, + PropType, + Modify, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { include } from './include.js' -import { getIteratorType } from './iteratorType.js' import { readPropDef, readSchema } from './readSchema.js' +export const defaultSingle = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { + let id = 0 + let prop = 0 + let aliasProp: PropDef | undefined + let aliasValue + if (typeof ast.target === 'number') { + id = ast.target + } else if (typeof ast.target === 'object' && ast.target !== null) { + for (const key in ast.target) { + aliasProp = typeDef.props.get(key) + if (aliasProp?.type !== PropType.alias) { + throw new Error('invalid alias target') + } + prop = aliasProp.id + aliasValue = ast.target[key] + break + } + } else { + throw new Error('ast.target not supported (yet)') + } + + const headerIndex = pushQueryHeaderSingle(ctx.query, { + op: aliasProp ? QueryType.alias : QueryType.id, + includeSize: 0, + typeId: typeDef.id, + filterSize: 0, + aliasSize: 0, + id, + prop, + }) + + if (aliasProp) { + const start = ctx.query.length + aliasProp.pushValue(ctx.query, aliasValue, Modify.create) + writeQueryHeaderSingleProps.aliasSize( + ctx.query.data, + ctx.query.length - start, + headerIndex, + ) + } + + writeQueryHeaderSingleProps.includeSize( + ctx.query.data, + include(ast, ctx, typeDef), + headerIndex, + ) +} + export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { const headerIndex = pushQueryHeaderSingleReference(ctx.query, { op: QueryType.reference, - typeId: prop.typeDef.id, + typeId: prop.ref!.id, includeSize: 0, edgeTypeId: 0, edgeSize: 0, @@ -31,7 +82,7 @@ export const reference = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { ...ctx, readSchema: schema, }, - prop.typeDef, + prop.ref!, ) props.includeSize(ctx.query.data, size, headerIndex) diff --git a/src/db-query/ast/toCtx.ts b/src/db-query/ast/toCtx.ts index 0ad6ea82ad..35d7af7a2f 100644 --- a/src/db-query/ast/toCtx.ts +++ b/src/db-query/ast/toCtx.ts @@ -8,6 +8,7 @@ import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { Ctx, QueryAst } from './ast.js' import { defaultMultiple } from './multiple.js' import { getReaderLocales, readSchema } from './readSchema.js' +import { defaultSingle } from './single.js' export const astToQueryCtx = ( schema: SchemaOut, @@ -40,7 +41,10 @@ export const astToQueryCtx = ( locales: getReaderLocales(schema), } - if (!ast.target) { + if (ast.target) { + defaultSingle(ast, ctx, typeDef) + ctx.readSchema.type = ReaderSchemaEnum.single + } else { defaultMultiple(ast, ctx, typeDef) } diff --git a/src/protocol/db-read/main.ts b/src/protocol/db-read/main.ts index 80969d38ef..237c39e1e6 100644 --- a/src/protocol/db-read/main.ts +++ b/src/protocol/db-read/main.ts @@ -20,6 +20,7 @@ const readMainValue = ( item: Item, ) => { const typeIndex = prop.typeIndex + if (typeIndex === PropType.timestamp) { addProp(prop, readInt64(result, i), item) } else if (typeIndex === PropType.number) { @@ -34,7 +35,10 @@ const readMainValue = ( } else { addProp(prop, prop.enum![result[i] - 1], item) } - } else if (typeIndex === PropType.string) { + } else if ( + typeIndex === PropType.string || + typeIndex === PropType.stringFixed + ) { const len = result[i] i++ const value = len === 0 ? '' : readUtf8(result, i, len) @@ -48,12 +52,15 @@ const readMainValue = ( } else { addProp(prop, value, item) } - } else if (typeIndex === PropType.json) { + } else if (typeIndex === PropType.json || typeIndex === PropType.jsonFixed) { const len = result[i] i++ const value = len === 0 ? null : global.JSON.parse(readUtf8(result, i, len)) addProp(prop, value, item) - } else if (typeIndex === PropType.binary) { + } else if ( + typeIndex === PropType.binary || + typeIndex === PropType.binaryFixed + ) { const len = result[i] i++ const value = len === 0 ? new Uint8Array(0) : result.subarray(i, i + len) diff --git a/src/protocol/db-read/prop.ts b/src/protocol/db-read/prop.ts index e26bfea113..8909b922b6 100644 --- a/src/protocol/db-read/prop.ts +++ b/src/protocol/db-read/prop.ts @@ -57,6 +57,7 @@ export const readProp = ( if (size === 0) { // do nothing } else { + console.log('->', prop.typeIndex) if (!prop.locales || prop.meta! > 2) { addProp(prop, readString(result, i + 4, size, true), item) } else { diff --git a/src/protocol/db-read/read.ts b/src/protocol/db-read/read.ts index 01a4a83e56..1c995f0067 100644 --- a/src/protocol/db-read/read.ts +++ b/src/protocol/db-read/read.ts @@ -18,6 +18,7 @@ import { PropType, readIncludeResponseMeta, ReadOp, + ReadOpInverse, } from '../../zigTsExports.js' export * from './types.js' @@ -109,7 +110,9 @@ const references: ReadInstruction = (q, result, i, item) => { } const edge: ReadInstruction = (q, result, i, item) => { - return readInstruction(result[i], q.edges!, result, i + 1, item) + const size = readUint32(result, i) + i += 4 + return readProps(q.edges!, result, i, i + size, item) } const readInstruction = ( diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 7fc78c17f5..decf49ceed 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -66,16 +66,21 @@ const addPropDef = ( return def } -const getTypeDef = (schema: SchemaType): TypeDef => { +const getTypeDef = ( + name: string, + schema: SchemaType, + schemaRoot: SchemaOut, +): TypeDef => { const { props } = schema const typeDef: TypeDef = { id: 0, - name: '', + name, separate: [], props: new Map(), main: [], tree: new Map(), schema, + schemaRoot, } const walk = ( @@ -111,14 +116,13 @@ export const getTypeDefs = (schema: SchemaOut): Map => { const typeDefs = new Map( Object.entries(schema.types) .sort() - .map(([key, type]) => [key, getTypeDef(type)]), + .map(([name, type]) => [name, getTypeDef(name, type, schema)]), ) // -------- connect references, add edges and assign ids -------- let typeId = 1 for (const [typeName, typeDef] of typeDefs) { typeDef.id = typeId++ - typeDef.name = typeName for (const [propPath, def] of typeDef.props) { const prop = def.schema.type === 'references' ? def.schema.items : def.schema @@ -154,8 +158,8 @@ export const getTypeDefs = (schema: SchemaOut): Map => { edges[edge] = prop[edge] } if (edges) { - def.edges = getTypeDef({ props: edges }) const edgeTypeName = `_${typeName}.${propPath}` + def.edges = getTypeDef(edgeTypeName, { props: edges }, schema) typeDefs.set(edgeTypeName, def.edges) } } diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index c0536438ae..effdc71b90 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -1,4 +1,4 @@ -import type { SchemaProp, SchemaType } from '../../schema.js' +import type { SchemaOut, SchemaProp, SchemaType } from '../../schema.js' import type { LangCodeEnum, ModifyEnum, @@ -23,6 +23,7 @@ export type TypeDef = { props: Map tree: PropTree schema: SchemaType + schemaRoot: SchemaOut } export type PropDef = { diff --git a/test/modify/props/alias.ts b/test/modify/props/alias.ts index 63ed10c710..bee06c5e0c 100644 --- a/test/modify/props/alias.ts +++ b/test/modify/props/alias.ts @@ -19,18 +19,15 @@ await test('modify alias', async (t) => { const id2 = await db.create('thing', { myAlias: 'b-alias', }) - - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, myAlias: 'my-alias-value', }) - // Update await db.update('thing', id1, { myAlias: 'another-alias', }) - - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, myAlias: 'another-alias', }) @@ -38,13 +35,12 @@ await test('modify alias', async (t) => { await db.update('thing', id2, { myAlias: 'another-alias', }) - - deepEqual(await db.query('thing', { myAlias: 'b-alias' }).get(), null) - deepEqual(await db.query('thing', { myAlias: 'another-alias' }).get(), { + deepEqual(await db.query2('thing', { myAlias: 'b-alias' }).get(), null) + deepEqual(await db.query2('thing', { myAlias: 'another-alias' }).get(), { id: id2, myAlias: 'another-alias', }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, myAlias: '', }) @@ -52,7 +48,7 @@ await test('modify alias', async (t) => { await db.update('thing', id2, { myAlias: null, }) - deepEqual((await db.query('thing', id2).get().toObject()).myAlias, '') + deepEqual((await db.query2('thing', id2).get()).myAlias, '') }) await test('schema alias on edge not allowed', async (t) => { diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts index 208faf0ad7..487eef4503 100644 --- a/test/modify/props/binary.ts +++ b/test/modify/props/binary.ts @@ -15,7 +15,7 @@ await test('modify binary', async (t) => { const id1 = await db.create('thing', { blob: b1, }) - const res1 = await db.query('thing', id1).get().toObject() + const res1 = await db.query2('thing', id1).get() deepEqual(res1.blob, b1) @@ -24,14 +24,14 @@ await test('modify binary', async (t) => { blob: b2, }) - const res2 = await db.query('thing', id1).get().toObject() + const res2 = await db.query2('thing', id1).get() deepEqual(res2.blob, b2) // Delete await db.update('thing', id1, { blob: null, }) - const res3 = await db.query('thing', id1).get().toObject() + const res3 = await db.query2('thing', id1).get() deepEqual(res3.blob, new Uint8Array()) }) @@ -60,11 +60,7 @@ await test('modify binary on edge', async (t) => { }, }) - const res1 = await db - .query('holder', id1) - .include('toThing.$edgeBlob') - .get() - .toObject() + const res1 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() deepEqual(res1.toThing?.$edgeBlob, b1) @@ -76,10 +72,7 @@ await test('modify binary on edge', async (t) => { }, }) - const res2 = await db - .query('holder', id1) - .include('toThing.$edgeBlob') - .get() - .toObject() + const res2 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() + deepEqual(res2.toThing?.$edgeBlob, b2) }) diff --git a/test/modify/props/boolean.ts b/test/modify/props/boolean.ts index 7daa11ff9a..51af3c6bf7 100644 --- a/test/modify/props/boolean.ts +++ b/test/modify/props/boolean.ts @@ -93,15 +93,11 @@ await test('modify boolean on edge', async (t) => { .query2('holder', await a) .include('toUser.$edgeBool') .get() - deepEqual(resA.toUser?.$edgeBool, false) // Check b (true) - const resB = await db - .query2('holder', await b) - .include('toUser.$edgeBool') - .get() - + const resB = await db.query2('holder', b).include('toUser.$edgeBool').get() + resB.toUser?.$edgeBool deepEqual(resB.toUser?.$edgeBool, true) // Check c (false) @@ -114,7 +110,7 @@ await test('modify boolean on edge', async (t) => { // Updates to true db.update('holder', await a, { toUser: { id: u1, $edgeBool: true } }) - db.update('holder', await b, { toUser: { id: u1, $edgeBool: true } }) + db.update('holder', b, { toUser: { id: u1, $edgeBool: true } }) db.update('holder', await c, { toUser: { id: u1, $edgeBool: true } }) const resA2 = await db @@ -123,10 +119,7 @@ await test('modify boolean on edge', async (t) => { .get() deepEqual(resA2.toUser?.$edgeBool, true) - const resB2 = await db - .query2('holder', await b) - .include('toUser.$edgeBool') - .get() + const resB2 = await db.query2('holder', b).include('toUser.$edgeBool').get() deepEqual(resB2.toUser?.$edgeBool, true) const resC2 = await db @@ -138,7 +131,7 @@ await test('modify boolean on edge', async (t) => { // Updates to false db.update('holder', await a, { toUser: { id: u1, $edgeBool: false } }) - db.update('holder', await b, { toUser: { id: u1, $edgeBool: false } }) + db.update('holder', b, { toUser: { id: u1, $edgeBool: false } }) db.update('holder', await c, { toUser: { id: u1, $edgeBool: false } }) const resA3 = await db @@ -147,10 +140,7 @@ await test('modify boolean on edge', async (t) => { .get() deepEqual(resA3.toUser?.$edgeBool, false) - const resB3 = await db - .query2('holder', await b) - .include('toUser.$edgeBool') - .get() + const resB3 = await db.query2('holder', b).include('toUser.$edgeBool').get() deepEqual(resB3.toUser?.$edgeBool, false) const resC3 = await db diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 924374bb74..626c4f9366 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -19,21 +19,21 @@ await test('modify cardinality basic', async (t) => { // Assuming we can read the count? Or the approximation. // The query might return the count. - const res1 = await db.query('thing', id1).get().toObject() + const res1 = await db.query2('thing', id1).get() deepEqual(res1.counter, 1) // Add another unique item await db.update('thing', id1, { counter: 'item2', }) - const res2 = await db.query('thing', id1).get().toObject() + const res2 = await db.query2('thing', id1).get() deepEqual(res2.counter, 2) // Add duplicate item (count shouldn't change) await db.update('thing', id1, { counter: 'item1', }) - const res3 = await db.query('thing', id1).get().toObject() + const res3 = await db.query2('thing', id1).get() deepEqual(res3.counter, 2) // Delete @@ -41,7 +41,7 @@ await test('modify cardinality basic', async (t) => { counter: null, }) - const res4 = await db.query('thing', id1).get().toObject() + const res4 = await db.query2('thing', id1).get() deepEqual(res4.counter, 0) }) @@ -70,10 +70,9 @@ await test('modify cardinality on edge', async (t) => { }) const res1 = await db - .query('holder', id1) + .query2('holder', id1) .include('toThing.$edgeCounter') .get() - .toObject() deepEqual(res1.toThing?.$edgeCounter, 1) @@ -85,10 +84,10 @@ await test('modify cardinality on edge', async (t) => { }) const res2 = await db - .query('holder', id1) + .query2('holder', id1) .include('toThing.$edgeCounter') .get() - .toObject() + deepEqual(res2.toThing?.$edgeCounter, 2) }) @@ -106,7 +105,7 @@ await test('modify cardinality array', async (t) => { counter: ['item1', 'item2'], }) - const res1 = await db.query('thing', id1).get().toObject() + const res1 = await db.query2('thing', id1).get() // Should have 2 unique items deepEqual(res1.counter, 2) @@ -115,7 +114,7 @@ await test('modify cardinality array', async (t) => { counter: ['item2', 'item3'], }) - const res2 = await db.query('thing', id1).get().toObject() + const res2 = await db.query2('thing', id1).get() // item1, item2, item3 -> 3 unique items deepEqual(res2.counter, 3) }) diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index 11e8ddedc0..a1c0afa7f2 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -20,7 +20,7 @@ await test('modify - default values basic', async (t) => { // 1. Create with no values provided const a = await db.create('thing', {}) - const resA: any = await db.query('thing', a).get() + const resA: any = await db.query2('thing', a).get() deepEqual(resA, { id: a, name: 'Untitled', @@ -42,7 +42,7 @@ await test('modify - default values basic', async (t) => { myText: { en: 'Hi' }, myTs: 2000, }) - const resB = await db.query('thing', b).get() + const resB = await db.query2('thing', b).get() deepEqual(resB, { id: b, name: 'Specific', @@ -56,7 +56,7 @@ await test('modify - default values basic', async (t) => { // 3. Create with mixed values const c = await db.create('thing', { score: 50, myEnum: 'b' }) - const resC = await db.query('thing', c).get() + const resC = await db.query2('thing', c).get() deepEqual(resC, { id: c, name: 'Untitled', @@ -98,12 +98,11 @@ await test('modify - default values on edge', async (t) => { }) const resG1 = await db - .query('group', g1) + .query2('group', g1) .include('member.$role') .include('member.$level') .include('member.id') .get() - .toObject() deepEqual(resG1.member?.$role, 'member') deepEqual(resG1.member?.$level, 1) @@ -121,7 +120,7 @@ await test('modify - default values on edge', async (t) => { }) const resG2: any = await db - .query('group', g2) + .query2('group', g2) .include('member.$role') .include('member.$level') .include('member.id') @@ -129,7 +128,6 @@ await test('modify - default values on edge', async (t) => { .include('member.$edgeJson') .include('member.$edgeText') .get() - .toObject() deepEqual(resG2.member?.$role, 'admin') deepEqual(resG2.member?.$level, 99) @@ -143,14 +141,13 @@ await test('modify - default values on edge', async (t) => { }) const resG3: any = await db - .query('group', g3) + .query2('group', g3) .include('member.$role') .include('member.$level') .include('member.$edgeEnum') .include('member.$edgeJson') .include('member.$edgeText') .get() - .toObject() deepEqual(resG3.member?.$role, 'member') deepEqual(resG3.member?.$level, 1) diff --git a/test/modify/props/enum.ts b/test/modify/props/enum.ts index dd339daa56..486e686c59 100644 --- a/test/modify/props/enum.ts +++ b/test/modify/props/enum.ts @@ -15,7 +15,7 @@ await test('modify enum', async (t) => { option: 'first', }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, option: 'first', }) @@ -24,7 +24,7 @@ await test('modify enum', async (t) => { option: 'second', }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, option: 'second', }) @@ -33,7 +33,7 @@ await test('modify enum', async (t) => { await db.update('thing', id1, { option: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).option, undefined) + deepEqual((await db.query2('thing', id1).get()).option, undefined) }) await test('modify enum on edge', async (t) => { @@ -61,10 +61,9 @@ await test('modify enum on edge', async (t) => { }) const res1 = await db - .query('holder', id1) + .query2('holder', id1) .include('toThing.$edgeOption') .get() - .toObject() deepEqual(res1.toThing?.$edgeOption, 'first') @@ -76,9 +75,8 @@ await test('modify enum on edge', async (t) => { }) const res2 = await db - .query('holder', id1) + .query2('holder', id1) .include('toThing.$edgeOption') .get() - .toObject() deepEqual(res2.toThing?.$edgeOption, 'second') }) diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts index 5ae2256b5b..96382ac1d6 100644 --- a/test/modify/props/json.ts +++ b/test/modify/props/json.ts @@ -16,7 +16,7 @@ await test('modify json', async (t) => { data: obj, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, data: obj, }) @@ -26,7 +26,7 @@ await test('modify json', async (t) => { data: arr, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, data: arr, }) @@ -35,7 +35,7 @@ await test('modify json', async (t) => { await db.update('thing', id1, { data: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).data, null) + deepEqual((await db.query2('thing', id1).get()).data, null) }) await test('modify json on edge', async (t) => { @@ -63,11 +63,7 @@ await test('modify json on edge', async (t) => { }, }) - const res1 = await db - .query('holder', id1) - .include('toThing.$edgeData') - .get() - .toObject() + const res1 = await db.query2('holder', id1).include('toThing.$edgeData').get() deepEqual(res1.toThing?.$edgeData, obj) @@ -79,10 +75,6 @@ await test('modify json on edge', async (t) => { }, }) - const res2 = await db - .query('holder', id1) - .include('toThing.$edgeData') - .get() - .toObject() + const res2 = await db.query2('holder', id1).include('toThing.$edgeData').get() deepEqual(res2.toThing?.$edgeData, obj2) }) diff --git a/test/modify/props/mixed.ts b/test/modify/props/mixed.ts index 900aaa2706..d0e038a0ab 100644 --- a/test/modify/props/mixed.ts +++ b/test/modify/props/mixed.ts @@ -37,13 +37,8 @@ await test('mixed props', async (t) => { r: 'a', }) - const typeTest = await db - .query('typeTest') - .include('*', '**') - .get() - .toObject() - const user = await db.query('user').include('*', '**').get().toObject() - + console.log('-------a') + const typeTest = await db.query2('typeTest').include('*', '**').get() deepEqual(typeTest, [ { id: 1, @@ -58,6 +53,8 @@ await test('mixed props', async (t) => { }, }, ]) + console.log('-------b') + const user = await db.query2('user').include('*', '**').get() deepEqual(user, [ { diff --git a/test/modify/props/numbers.ts b/test/modify/props/numbers.ts index 6454447f28..ae04304156 100644 --- a/test/modify/props/numbers.ts +++ b/test/modify/props/numbers.ts @@ -49,7 +49,7 @@ await test('modify numbers', async (t) => { i32: -2147483648, }) - deepEqual(await db.query('thing').get(), [ + deepEqual(await db.query2('thing').get(), [ { id: id1, n: 1.5, @@ -92,7 +92,7 @@ await test('modify numbers', async (t) => { i32: -100001, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, n: 2.5, u8: 11, @@ -113,7 +113,7 @@ await test('modify numbers', async (t) => { i32: { increment: 1 }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, n: 5, u8: 12, @@ -134,7 +134,7 @@ await test('modify numbers', async (t) => { i32: { increment: -1 }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, n: 2.5, u8: 11, @@ -156,7 +156,7 @@ await test('modify numbers', async (t) => { i32: null, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: 1, n: 0, i32: 0, @@ -237,7 +237,7 @@ await test('modify numbers on edge', async (t) => { // Helper to get edge props const getEdgeProps = async (id: number) => { const res = await db - .query('holder', id) + .query2('holder', id) .include( 'toThing.$edgeN', 'toThing.$edgeU8', @@ -248,7 +248,6 @@ await test('modify numbers on edge', async (t) => { 'toThing.$edgeI32', ) .get() - .toObject() if (!res.toThing || Array.isArray(res.toThing)) { return {} diff --git a/test/modify/props/object.ts b/test/modify/props/object.ts index 698228ce5d..e571295a5c 100644 --- a/test/modify/props/object.ts +++ b/test/modify/props/object.ts @@ -24,7 +24,7 @@ await test('modify object', async (t) => { }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, info: { title: 'my title', @@ -39,7 +39,7 @@ await test('modify object', async (t) => { }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, info: { title: 'my title', @@ -53,7 +53,7 @@ await test('modify object', async (t) => { title: null, }, }) - deepEqual((await db.query('thing', id1).get().toObject()).info, { + deepEqual((await db.query2('thing', id1).get()).info, { count: 20, title: '', }) @@ -62,7 +62,7 @@ await test('modify object', async (t) => { await db.update('thing', id1, { info: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).info, { + deepEqual((await db.query2('thing', id1).get()).info, { count: 0, title: '', }) @@ -101,11 +101,7 @@ await test('modify object on edge', async (t) => { }, }) - const res1 = await db - .query('holder', id1) - .include('toThing.$edgeInfo') - .get() - .toObject() + const res1 = await db.query2('holder', id1).include('toThing.$edgeInfo').get() deepEqual(res1.toThing?.$edgeInfo, { title: 'edge title', @@ -122,11 +118,8 @@ await test('modify object on edge', async (t) => { }, }) - const res2 = await db - .query('holder', id1) - .include('toThing.$edgeInfo') - .get() - .toObject() + const res2 = await db.query2('holder', id1).include('toThing.$edgeInfo').get() + deepEqual(res2.toThing?.$edgeInfo, { title: 'edge title', count: 15, diff --git a/test/modify/props/references.ts b/test/modify/props/references.ts index 6eaccb16af..e6c8c50f51 100644 --- a/test/modify/props/references.ts +++ b/test/modify/props/references.ts @@ -23,7 +23,8 @@ await test('modify single reference', async (t) => { const realT2 = await t2 { - const res = await db.query('holder', h1).include('dest.id').get().toObject() + const res = await db.query2('holder', h1).include('dest.id').get() + deepEqual(res, { id: h1, dest: { id: realT1 }, @@ -34,7 +35,8 @@ await test('modify single reference', async (t) => { await db.update('holder', h1, { dest: t2 }) { - const res = await db.query('holder', h1).include('dest.id').get().toObject() + const res = await db.query2('holder', h1).include('dest.id').get() + deepEqual(res, { id: h1, dest: { id: realT2 }, @@ -45,7 +47,8 @@ await test('modify single reference', async (t) => { await db.update('holder', h1, { dest: { id: t1 } }) { - const res = await db.query('holder', h1).include('dest.id').get().toObject() + const res = await db.query2('holder', h1).include('dest.id').get() + deepEqual(res, { id: h1, dest: { id: realT1 }, @@ -54,7 +57,7 @@ await test('modify single reference', async (t) => { // Delete await db.update('holder', h1, { dest: null }) - deepEqual(await db.query('holder', h1).include('dest').get(), { + deepEqual(await db.query2('holder', h1).include('dest').get(), { id: h1, dest: null, }) @@ -89,7 +92,7 @@ await test('modify references', async (t) => { const h1 = await db.create('holder', { dests: [t1, t2Promise] }) const check = async (ids: number[], msg) => { - const res = await db.query('holder', h1).include('dests').get().toObject() + const res = await db.query2('holder', h1).include('dests').get() const currentIds = res.dests?.map((v: any) => v.id) || [] currentIds.sort() ids.sort() @@ -159,10 +162,9 @@ await test('modify references no await', async (t) => { // Verify const res = await db - .query('holder', await h1) + .query2('holder', await h1) .include('dests.id') .get() - .toObject() const currentIds = res.dests?.map((v: any) => v.id) || [] currentIds.sort() @@ -205,10 +207,10 @@ await test('modify single reference on edge', async (t) => { // Verify const getEdgeRef = async (id: number) => { const res = await db - .query('holder', id) + .query2('holder', id) .include('toThing.$edgeRef.id') .get() - .toObject() + return res.toThing && !Array.isArray(res.toThing) ? res.toThing.$edgeRef : undefined @@ -274,10 +276,9 @@ await test('modify references on edge', async (t) => { const check = async (ids: number[], msg) => { const res = await db - .query('holder', h1) + .query2('holder', h1) .include('toThing.$edgeRefs.id') .get() - .toObject() const edge = res.toThing && !Array.isArray(res.toThing) ? res.toThing : {} const currentIds = edge.$edgeRefs?.map((v: any) => v.id) || [] diff --git a/test/modify/props/string.ts b/test/modify/props/string.ts index 342e5c170b..6ff7578076 100644 --- a/test/modify/props/string.ts +++ b/test/modify/props/string.ts @@ -16,7 +16,7 @@ await test('modify string', async (t) => { const id1 = await db.create('thing', { name: s1, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s1, }) @@ -26,7 +26,7 @@ await test('modify string', async (t) => { await db.update('thing', id1, { name: s2, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s2, }) @@ -36,7 +36,7 @@ await test('modify string', async (t) => { await db.update('thing', id1, { name: s3, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s3, }) @@ -46,7 +46,7 @@ await test('modify string', async (t) => { await db.update('thing', id1, { name: s4, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s4, }) @@ -56,7 +56,7 @@ await test('modify string', async (t) => { await db.update('thing', id1, { name: s5, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s5.normalize('NFD'), }) @@ -66,7 +66,7 @@ await test('modify string', async (t) => { await db.update('thing', id1, { name: s6, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s6, }) @@ -76,7 +76,7 @@ await test('modify string', async (t) => { name: null, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: '', // Should probably be empty string for string props? }) @@ -108,11 +108,7 @@ await test('modify string on edge', async (t) => { }, }) - const res1 = await db - .query('holder', id1) - .include('toThing.$edgeName') - .get() - .toObject() + const res1 = await db.query2('holder', id1).include('toThing.$edgeName').get() deepEqual(res1.toThing?.$edgeName, s1) @@ -124,11 +120,7 @@ await test('modify string on edge', async (t) => { $edgeName: s2, }, }) - const res2 = await db - .query('holder', id1) - .include('toThing.$edgeName') - .get() - .toObject() + const res2 = await db.query2('holder', id1).include('toThing.$edgeName').get() deepEqual(res2.toThing?.$edgeName, s2) // String with spaces @@ -139,11 +131,7 @@ await test('modify string on edge', async (t) => { $edgeName: s3, }, }) - const res3 = await db - .query('holder', id1) - .include('toThing.$edgeName') - .get() - .toObject() + const res3 = await db.query2('holder', id1).include('toThing.$edgeName').get() deepEqual(res3.toThing?.$edgeName, s3) // Empty string @@ -154,11 +142,7 @@ await test('modify string on edge', async (t) => { $edgeName: s4, }, }) - const res4 = await db - .query('holder', id1) - .include('toThing.$edgeName') - .get() - .toObject() + const res4 = await db.query2('holder', id1).include('toThing.$edgeName').get() deepEqual(res4.toThing?.$edgeName, s4) // Unicode / Special characters @@ -169,11 +153,7 @@ await test('modify string on edge', async (t) => { $edgeName: s5, }, }) - const res5 = await db - .query('holder', id1) - .include('toThing.$edgeName') - .get() - .toObject() + const res5 = await db.query2('holder', id1).include('toThing.$edgeName').get() deepEqual(res5.toThing?.$edgeName, s5.normalize('NFD')) // Long string @@ -184,11 +164,7 @@ await test('modify string on edge', async (t) => { $edgeName: s6, }, }) - const res6 = await db - .query('holder', id1) - .include('toThing.$edgeName') - .get() - .toObject() + const res6 = await db.query2('holder', id1).include('toThing.$edgeName').get() deepEqual(res6.toThing?.$edgeName, s6) }) @@ -209,7 +185,7 @@ await test('modify fixed string', async (t) => { const id1 = await db.create('thing', { name: s1, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s1, }) @@ -219,7 +195,7 @@ await test('modify fixed string', async (t) => { await db.update('thing', id1, { name: s2, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s2, }) @@ -229,7 +205,7 @@ await test('modify fixed string', async (t) => { await db.update('thing', id1, { name: s3, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s3, }) @@ -239,7 +215,7 @@ await test('modify fixed string', async (t) => { await db.update('thing', id1, { name: s4, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s4, }) @@ -248,7 +224,7 @@ await test('modify fixed string', async (t) => { await db.update('thing', id1, { name: null, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: '', }) @@ -273,7 +249,7 @@ await test('modify long string', async (t) => { const id1 = await db.create('thing', { name: s1, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s1, }) @@ -283,7 +259,7 @@ await test('modify long string', async (t) => { await db.update('thing', id1, { name: s2, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, name: s2, }) @@ -292,7 +268,7 @@ await test('modify long string', async (t) => { const id2 = await db.create('longCompressedString', { name: s1, }) - deepEqual(await db.query('longCompressedString', id2).get(), { + deepEqual(await db.query2('longCompressedString', id2).get(), { id: id2, name: s1, }) diff --git a/test/modify/props/text.ts b/test/modify/props/text.ts index 412f633fea..efea478928 100644 --- a/test/modify/props/text.ts +++ b/test/modify/props/text.ts @@ -23,7 +23,7 @@ await test('modify text', async (t) => { }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, content: { en: 'Hello', @@ -39,7 +39,7 @@ await test('modify text', async (t) => { }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, content: { en: 'Hello', @@ -55,7 +55,7 @@ await test('modify text', async (t) => { }, }) - deepEqual(await db.query('thing', id1).get(), { + deepEqual(await db.query2('thing', id1).get(), { id: id1, content: { en: 'Hi', @@ -68,7 +68,7 @@ await test('modify text', async (t) => { await db.update('thing', id1, { content: null, }) - deepEqual((await db.query('thing', id1).get().toObject()).content, { + deepEqual((await db.query2('thing', id1).get()).content, { nl: '', en: '', de: '', @@ -102,11 +102,7 @@ await test('modify text on edge', async (t) => { }, }) - const res1 = await db - .query('holder', id1) - .include('toThing.$edgeText') - .get() - .toObject() + const res1 = await db.query2('holder', id1).include('toThing.$edgeText').get() deepEqual(res1.toThing?.$edgeText, { en: 'edge hello' }) @@ -117,10 +113,7 @@ await test('modify text on edge', async (t) => { }, }) - const res2 = await db - .query('holder', id1) - .include('toThing.$edgeText') - .get() - .toObject() + const res2 = await db.query2('holder', id1).include('toThing.$edgeText').get() + deepEqual(res2.toThing?.$edgeText, { en: 'edge hi' }) }) diff --git a/test/modify/props/timestamp.ts b/test/modify/props/timestamp.ts index 91f929c0ff..68539a2b24 100644 --- a/test/modify/props/timestamp.ts +++ b/test/modify/props/timestamp.ts @@ -19,7 +19,7 @@ await test('modify timestamp', async (t) => { ts: t1, }) - deepEqual(await db.query('event', id1).get(), { + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: t1, }) @@ -28,7 +28,7 @@ await test('modify timestamp', async (t) => { ts: t2, }) - deepEqual(await db.query('event', id1).get(), { + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: t2, }) @@ -37,41 +37,41 @@ await test('modify timestamp', async (t) => { ts: t3, }) - deepEqual(await db.query('event', id1).get(), { + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: t3, }) // Edge cases await db.update('event', id1, { ts: 0 }) - deepEqual(await db.query('event', id1).get(), { id: id1, ts: 0 }) + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: 0 }) const farFuture = 8640000000000000 // Max JS Date timestamp await db.update('event', id1, { ts: farFuture }) - deepEqual(await db.query('event', id1).get(), { id: id1, ts: farFuture }) + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: farFuture }) // Increment await db.update('event', id1, { ts: 1000 }) await db.update('event', id1, { ts: { increment: 1000 }, }) - deepEqual(await db.query('event', id1).get(), { id: id1, ts: 2000 }) + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: 2000 }) await db.update('event', id1, { ts: { increment: -500 }, }) - deepEqual(await db.query('event', id1).get(), { id: id1, ts: 1500 }) + deepEqual(await db.query2('event', id1).get(), { id: id1, ts: 1500 }) // String formats const now = Date.now() await db.update('event', id1, { ts: 'now' }) - const r1: any = await db.query('event', id1).get() + const r1: any = await db.query2('event', id1).get() if (Math.abs(r1.ts - now) > 200) { throw new Error(`Timestamp 'now' is too far off: ${r1.ts} vs ${now}`) } await db.update('event', id1, { ts: 'now + 1h' }) - const r2: any = await db.query('event', id1).get() + const r2: any = await db.query2('event', id1).get() const t2Expr = now + 1000 * 60 * 60 if (Math.abs(r2.ts - t2Expr) > 200) { throw new Error( @@ -80,7 +80,7 @@ await test('modify timestamp', async (t) => { } await db.update('event', id1, { ts: 'now - 1d' }) - const r3: any = await db.query('event', id1).get() + const r3: any = await db.query2('event', id1).get() const t3Expr = now - 1000 * 60 * 60 * 24 if (Math.abs(r3.ts - t3Expr) > 200) { throw new Error( @@ -92,12 +92,12 @@ await test('modify timestamp', async (t) => { const dateStr = '2025-01-01T00:00:00.000Z' const dateTs = new Date(dateStr).valueOf() await db.update('event', id1, { ts: dateStr }) - const r4: any = await db.query('event', id1).get() + const r4: any = await db.query2('event', id1).get() deepEqual(r4, { id: id1, ts: dateTs }) // Delete await db.update('event', id1, { ts: null }) - deepEqual((await db.query('event', id1).get().toObject()).ts, 0) + deepEqual((await db.query2('event', id1).get()).ts, 0) }) await test('modify timestamp on edge', async (t) => { @@ -131,11 +131,8 @@ await test('modify timestamp on edge', async (t) => { // Helper const getEdgeTs = async (id: number) => { - const res = await db - .query('holder', id) - .include('toEvent.$edgeTs') - .get() - .toObject() + const res = await db.query2('holder', id).include('toEvent.$edgeTs').get() + return res.toEvent?.$edgeTs || 0 } diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 18e50bca31..c611e83ffb 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -2,7 +2,7 @@ import { testDb } from '../../shared/index.js' import test from '../../shared/test.js' import assert from 'node:assert' -await test('modify vector', async (t) => { +await test.skip('modify vector', async (t) => { const db = await testDb(t, { types: { thing: { @@ -18,7 +18,7 @@ await test('modify vector', async (t) => { // Float precision might require approximate equality or strict check if implementation preserves bits // For now assuming deepEqual works or we might need a tolerance check - const res = await db.query('thing', id1).get().toObject() + const res = await db.query2('thing', id1).get() // Convert result back to array if it is returned as TypedArray const vecArr = Array.from(res.vec) as number[] @@ -33,7 +33,7 @@ await test('modify vector', async (t) => { vec: v2, }) - const res2 = await db.query('thing', id1).get().toObject() + const res2 = await db.query2('thing', id1).get() const vecArr2 = Array.from(res2.vec) as number[] assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) @@ -44,7 +44,7 @@ await test('modify vector', async (t) => { await db.update('thing', id1, { vec: null, }) - const res3 = await db.query('thing', id1).get().toObject() + const res3 = await db.query2('thing', id1).get() assert(res3.vec === undefined) }) @@ -67,7 +67,7 @@ await test.skip('modify colvec', async (t) => { vec: v1, }) - const res = await db.query('thing', id1).get().toObject() + const res = await db.query2('thing', id1).get() const vecArr = Array.from(res.vec) as number[] assert(Math.abs(vecArr[0] - v1[0]) < 0.0001) @@ -79,7 +79,7 @@ await test.skip('modify colvec', async (t) => { vec: v2, }) - const res2 = await db.query('thing', id1).get().toObject() + const res2 = await db.query2('thing', id1).get() const vecArr2 = Array.from(res2.vec) as number[] assert(Math.abs(vecArr2[0] - v2[0]) < 0.0001) @@ -90,11 +90,11 @@ await test.skip('modify colvec', async (t) => { await db.update('thing', id1, { vec: null, }) - const res3 = await db.query('thing', id1).get().toObject() + const res3 = await db.query2('thing', id1).get() assert(res3.vec === undefined) }) -await test('modify vector on edge', async (t) => { +await test.skip('modify vector on edge', async (t) => { const db = await testDb(t, { types: { thing: { @@ -119,11 +119,7 @@ await test('modify vector on edge', async (t) => { }, }) - const res = await db - .query('holder', id1) - .include('toThing.$edgeVec') - .get() - .toObject() + const res = await db.query2('holder', id1).include('toThing.$edgeVec').get() if (res.toThing) { const vecArr = Array.from(res.toThing.$edgeVec) as number[] @@ -142,11 +138,7 @@ await test('modify vector on edge', async (t) => { }, }) - const res2 = await db - .query('holder', id1) - .include('toThing.$edgeVec') - .get() - .toObject() + const res2 = await db.query2('holder', id1).include('toThing.$edgeVec').get() if (res2.toThing) { const vecArr2 = Array.from(res2.toThing.$edgeVec) as number[] diff --git a/test/number.ts b/test/number.ts index 9efebf94c7..f732a67c5e 100644 --- a/test/number.ts +++ b/test/number.ts @@ -78,7 +78,6 @@ await test('basic', async (t) => { } }), ) - console.log('==========================') const newThing = await db.create('user', { number: { increment: 12, diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 9b6920e2e3..11943c4797 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -74,7 +74,7 @@ await test('include', async (t) => { cookie: 1234, }, y: 0, - mrFriend: { id: a, $level: 67 }, + mrFriend: { id: a, $level: 99 }, // friends: [{ id: a, $level: 250 }, b], }) From cf9da0d422850b9ab81bfc7797d75e2aa8f57267 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 15:13:32 +0100 Subject: [PATCH 178/449] remove log --- src/protocol/db-read/prop.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/protocol/db-read/prop.ts b/src/protocol/db-read/prop.ts index 8909b922b6..e26bfea113 100644 --- a/src/protocol/db-read/prop.ts +++ b/src/protocol/db-read/prop.ts @@ -57,7 +57,6 @@ export const readProp = ( if (size === 0) { // do nothing } else { - console.log('->', prop.typeIndex) if (!prop.locales || prop.meta! > 2) { addProp(prop, readString(result, i + 4, size, true), item) } else { From d0822feb45ba7fbd05d194ce1dcfb88b21600c78 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 14:46:14 +0100 Subject: [PATCH 179/449] raw cardinality buffer can be any len --- src/schema/defs/props/cardinality.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/props/cardinality.ts b/src/schema/defs/props/cardinality.ts index 26e0f9b549..510977caf0 100644 --- a/src/schema/defs/props/cardinality.ts +++ b/src/schema/defs/props/cardinality.ts @@ -35,7 +35,7 @@ export const cardinality = class Cardinality extends BasePropDef { if (typeof item === 'string') { buf.reserveUint64() xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) - } else if (item instanceof Uint8Array && item.byteLength === 8) { + } else if (item instanceof Uint8Array) { buf.set(item, buf.length) } else { throw new Error('Invalid value for cardinality ' + this.path.join('.')) From 5ac2c23a2cd826703fcb577260799165979dc85c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 14:46:30 +0100 Subject: [PATCH 180/449] rename test case --- test/raw.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/raw.ts b/test/raw.ts index 6dfa5898cb..75d2938965 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -2,7 +2,7 @@ import { BasedDb } from '../src/index.js' import { deepEqual } from './shared/assert.js' import test from './shared/test.js' -await test('raw', async (t) => { +await test('cardinality', async (t) => { const db = new BasedDb({ path: t.tmp, }) From 4138e42181019afacd784f93d75817bca57b6625 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 15:12:19 +0100 Subject: [PATCH 181/449] Revert "raw cardinality buffer can be any len" This is actually just for the hash. This reverts commit 68f4e75f828a192c454be621e420f3b4d472f23b. --- src/schema/defs/props/cardinality.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/props/cardinality.ts b/src/schema/defs/props/cardinality.ts index 510977caf0..26e0f9b549 100644 --- a/src/schema/defs/props/cardinality.ts +++ b/src/schema/defs/props/cardinality.ts @@ -35,7 +35,7 @@ export const cardinality = class Cardinality extends BasePropDef { if (typeof item === 'string') { buf.reserveUint64() xxHash64(ENCODER.encode(item), buf.data, buf.length - 8) - } else if (item instanceof Uint8Array) { + } else if (item instanceof Uint8Array && item.byteLength === 8) { buf.set(item, buf.length) } else { throw new Error('Invalid value for cardinality ' + this.path.join('.')) From c52516f0760af1482d69dcb43d3cd9f84f8dfc16 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 15:21:55 +0100 Subject: [PATCH 182/449] deepEqual checks types --- test/shared/assert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shared/assert.ts b/test/shared/assert.ts index 33cff334ff..d23a07597e 100644 --- a/test/shared/assert.ts +++ b/test/shared/assert.ts @@ -6,7 +6,7 @@ import { PropTypeInverse } from '../../src/zigTsExports.js' export { perf } from './perf.js' // add fn -export const deepEqual = (a, b, msg?: string) => { +export const deepEqual = (a: A, b: NoInfer, msg?: string) => { if (a instanceof BasedQueryResponse) { a = a.toObject() } From 8cdef27f11f77a9a6e146cb25dd18b61d0b2defd Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 15:40:41 +0100 Subject: [PATCH 183/449] raw string test --- test/raw.ts | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/test/raw.ts b/test/raw.ts index 75d2938965..92b2bafc42 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -1,14 +1,14 @@ import { BasedDb } from '../src/index.js' import { deepEqual } from './shared/assert.js' +import {italy} from './shared/examples.js' import test from './shared/test.js' -await test('cardinality', async (t) => { +await test.skip('cardinality', async (t) => { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) await db.setSchema({ @@ -35,3 +35,44 @@ await test('cardinality', async (t) => { const [a, b] = await db.query('user').get().toObject() deepEqual(a.uniqueSkills, b.uniqueSkills) }) + +await test('string', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + + await db.start({ clean: true }) + t.after(() => t.backup(db)) + + await db.setSchema({ + types: { + user: { + name: 'string', + role: { type: 'string', maxBytes: 4 }, + resume: { type: 'string', compression: 'deflate' }, + }, + }, + }) + + const one = await db.create('user', { + name: 'user', + role: 'root', + resume: italy, + }) + const { name, role, resume } = await db + .query('user', one) + .include('name', { raw: true }) + .get() + .toObject() + + await db.create('user', { + name, + role, + resume, + }) + + const [a, b] = await db.query('user').get().toObject() + deepEqual(a.name, b.name) + deepEqual(a.role, b.role) + deepEqual(a.resume, b.resume) +}) From 2c836cd6d544207e2586510d96287e79933ef19c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 6 Feb 2026 15:44:44 +0100 Subject: [PATCH 184/449] fix test --- test/raw.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/raw.ts b/test/raw.ts index 92b2bafc42..86ffbc2939 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -61,7 +61,7 @@ await test('string', async (t) => { }) const { name, role, resume } = await db .query('user', one) - .include('name', { raw: true }) + .include(['name', 'role', 'resume'], { raw: true }) .get() .toObject() From 13e91ffdab8c2f08318ff9dfa365a7e0a2cb4fdd Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 16:04:18 +0100 Subject: [PATCH 185/449] update types --- src/db-client/query2/index.ts | 4 +--- src/db-client/query2/types.ts | 13 ++++++++----- test/modify/props/alias.ts | 2 +- test/modify/props/binary.ts | 10 +++++----- test/modify/props/boolean.ts | 20 ++++++++++---------- test/modify/props/cardinality.ts | 16 ++++++++-------- test/modify/props/default.ts | 4 ++-- test/modify/props/enum.ts | 6 +++--- test/modify/props/json.ts | 6 +++--- test/modify/props/numbers.ts | 18 ++++++++++-------- test/modify/props/string.ts | 12 ++++++------ test/query/ast.ts | 23 +++++++++++++---------- 12 files changed, 70 insertions(+), 64 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 659f52abc6..2abdcd11c6 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -111,13 +111,12 @@ export class BasedQuery2< db: DbClient async get(): Promise< IsSingle extends true - ? PickOutput, K>> + ? PickOutput, K>> | null : PickOutput, K>>[] > { if (!this.ast.props) { this.include('*') } - // console.dir(this.ast, { depth: null }) if (!this.db.schema) { await this.db.once('schema') @@ -128,7 +127,6 @@ export class BasedQuery2< this.ast, new AutoSizedUint8Array(1000), ) - // console.dir(ctx.readSchema, { depth: null }) const result = await this.db.hooks.getQueryBuf(ctx.query) return proxyResult(result, ctx.readSchema) as any } diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index d83692ead5..0f162c3faf 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -79,7 +79,7 @@ export type InferProp< Locales extends Record = Record, Selection = never, > = Prop extends { type: 'text' } - ? string + ? { [K in keyof Locales]-?: string } : Prop extends { type: 'object'; props: infer P } ? InferType : Prop extends { type: infer T extends keyof TypeMap } @@ -96,7 +96,7 @@ export type InferProp< ResolvedProps & FilterEdges, Selection > - > + > | null : never : number // ID : Prop extends { @@ -164,9 +164,12 @@ export type PickOutput< | Extract> | 'id']: P extends keyof ResolvedProps ? IsRefProp[P]> extends true - ? ResolvedProps[P] extends { items: any } - ? { id: number }[] - : { id: number } + ? InferProp< + ResolvedProps[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + '*' + > : InferSchemaOutput[P] : InferSchemaOutput[P] } & { diff --git a/test/modify/props/alias.ts b/test/modify/props/alias.ts index bee06c5e0c..36231e0d90 100644 --- a/test/modify/props/alias.ts +++ b/test/modify/props/alias.ts @@ -48,7 +48,7 @@ await test('modify alias', async (t) => { await db.update('thing', id2, { myAlias: null, }) - deepEqual((await db.query2('thing', id2).get()).myAlias, '') + deepEqual((await db.query2('thing', id2).get())!.myAlias, '') }) await test('schema alias on edge not allowed', async (t) => { diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts index 487eef4503..7e1f338481 100644 --- a/test/modify/props/binary.ts +++ b/test/modify/props/binary.ts @@ -17,7 +17,7 @@ await test('modify binary', async (t) => { }) const res1 = await db.query2('thing', id1).get() - deepEqual(res1.blob, b1) + deepEqual(res1?.blob, b1) const b2 = new Uint8Array([4, 5, 6, 7]) await db.update('thing', id1, { @@ -25,14 +25,14 @@ await test('modify binary', async (t) => { }) const res2 = await db.query2('thing', id1).get() - deepEqual(res2.blob, b2) + deepEqual(res2?.blob, b2) // Delete await db.update('thing', id1, { blob: null, }) const res3 = await db.query2('thing', id1).get() - deepEqual(res3.blob, new Uint8Array()) + deepEqual(res3?.blob, new Uint8Array()) }) await test('modify binary on edge', async (t) => { @@ -62,7 +62,7 @@ await test('modify binary on edge', async (t) => { const res1 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() - deepEqual(res1.toThing?.$edgeBlob, b1) + deepEqual(res1?.toThing.$edgeBlob, b1) const b2 = new Uint8Array([4, 5, 6, 7]) await db.update('holder', id1, { @@ -74,5 +74,5 @@ await test('modify binary on edge', async (t) => { const res2 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() - deepEqual(res2.toThing?.$edgeBlob, b2) + deepEqual(res2?.toThing.$edgeBlob, b2) }) diff --git a/test/modify/props/boolean.ts b/test/modify/props/boolean.ts index 51af3c6bf7..4520bd9062 100644 --- a/test/modify/props/boolean.ts +++ b/test/modify/props/boolean.ts @@ -93,12 +93,12 @@ await test('modify boolean on edge', async (t) => { .query2('holder', await a) .include('toUser.$edgeBool') .get() - deepEqual(resA.toUser?.$edgeBool, false) + deepEqual(resA?.toUser?.$edgeBool, false) // Check b (true) const resB = await db.query2('holder', b).include('toUser.$edgeBool').get() - resB.toUser?.$edgeBool - deepEqual(resB.toUser?.$edgeBool, true) + resB?.toUser?.$edgeBool + deepEqual(resB?.toUser?.$edgeBool, true) // Check c (false) const resC = await db @@ -106,7 +106,7 @@ await test('modify boolean on edge', async (t) => { .include('toUser.$edgeBool') .get() - deepEqual(resC.toUser?.$edgeBool, false) + deepEqual(resC?.toUser?.$edgeBool, false) // Updates to true db.update('holder', await a, { toUser: { id: u1, $edgeBool: true } }) @@ -118,16 +118,16 @@ await test('modify boolean on edge', async (t) => { .include('toUser.$edgeBool') .get() - deepEqual(resA2.toUser?.$edgeBool, true) + deepEqual(resA2?.toUser?.$edgeBool, true) const resB2 = await db.query2('holder', b).include('toUser.$edgeBool').get() - deepEqual(resB2.toUser?.$edgeBool, true) + deepEqual(resB2?.toUser?.$edgeBool, true) const resC2 = await db .query2('holder', await c) .include('toUser.$edgeBool') .get() - deepEqual(resC2.toUser?.$edgeBool, true) + deepEqual(resC2?.toUser?.$edgeBool, true) // Updates to false db.update('holder', await a, { toUser: { id: u1, $edgeBool: false } }) @@ -139,14 +139,14 @@ await test('modify boolean on edge', async (t) => { .include('toUser.$edgeBool') .get() - deepEqual(resA3.toUser?.$edgeBool, false) + deepEqual(resA3?.toUser?.$edgeBool, false) const resB3 = await db.query2('holder', b).include('toUser.$edgeBool').get() - deepEqual(resB3.toUser?.$edgeBool, false) + deepEqual(resB3?.toUser?.$edgeBool, false) const resC3 = await db .query2('holder', await c) .include('toUser.$edgeBool') .get() - deepEqual(resC3.toUser?.$edgeBool, false) + deepEqual(resC3?.toUser?.$edgeBool, false) }) diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 626c4f9366..638bf9a6a7 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -20,21 +20,21 @@ await test('modify cardinality basic', async (t) => { // Assuming we can read the count? Or the approximation. // The query might return the count. const res1 = await db.query2('thing', id1).get() - deepEqual(res1.counter, 1) + deepEqual(res1?.counter, 1) // Add another unique item await db.update('thing', id1, { counter: 'item2', }) const res2 = await db.query2('thing', id1).get() - deepEqual(res2.counter, 2) + deepEqual(res2?.counter, 2) // Add duplicate item (count shouldn't change) await db.update('thing', id1, { counter: 'item1', }) const res3 = await db.query2('thing', id1).get() - deepEqual(res3.counter, 2) + deepEqual(res3?.counter, 2) // Delete await db.update('thing', id1, { @@ -42,7 +42,7 @@ await test('modify cardinality basic', async (t) => { }) const res4 = await db.query2('thing', id1).get() - deepEqual(res4.counter, 0) + deepEqual(res4?.counter, 0) }) await test('modify cardinality on edge', async (t) => { @@ -74,7 +74,7 @@ await test('modify cardinality on edge', async (t) => { .include('toThing.$edgeCounter') .get() - deepEqual(res1.toThing?.$edgeCounter, 1) + deepEqual(res1?.toThing.$edgeCounter, 1) await db.update('holder', id1, { toThing: { @@ -88,7 +88,7 @@ await test('modify cardinality on edge', async (t) => { .include('toThing.$edgeCounter') .get() - deepEqual(res2.toThing?.$edgeCounter, 2) + deepEqual(res2?.toThing.$edgeCounter, 2) }) await test('modify cardinality array', async (t) => { @@ -107,7 +107,7 @@ await test('modify cardinality array', async (t) => { const res1 = await db.query2('thing', id1).get() // Should have 2 unique items - deepEqual(res1.counter, 2) + deepEqual(res1?.counter, 2) // Update with array (one new, one duplicate) await db.update('thing', id1, { @@ -116,5 +116,5 @@ await test('modify cardinality array', async (t) => { const res2 = await db.query2('thing', id1).get() // item1, item2, item3 -> 3 unique items - deepEqual(res2.counter, 3) + deepEqual(res2?.counter, 3) }) diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index a1c0afa7f2..7d51eb03b3 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -104,8 +104,8 @@ await test('modify - default values on edge', async (t) => { .include('member.id') .get() - deepEqual(resG1.member?.$role, 'member') - deepEqual(resG1.member?.$level, 1) + deepEqual(resG1?.member.$role, 'member') + deepEqual(resG1?.member.$level, 1) // 2. Create edge with edge props const g2 = await db.create('group', { diff --git a/test/modify/props/enum.ts b/test/modify/props/enum.ts index 486e686c59..82904f8403 100644 --- a/test/modify/props/enum.ts +++ b/test/modify/props/enum.ts @@ -33,7 +33,7 @@ await test('modify enum', async (t) => { await db.update('thing', id1, { option: null, }) - deepEqual((await db.query2('thing', id1).get()).option, undefined) + deepEqual((await db.query2('thing', id1).get())?.option, undefined) }) await test('modify enum on edge', async (t) => { @@ -65,7 +65,7 @@ await test('modify enum on edge', async (t) => { .include('toThing.$edgeOption') .get() - deepEqual(res1.toThing?.$edgeOption, 'first') + deepEqual(res1?.toThing.$edgeOption, 'first') await db.update('holder', id1, { toThing: { @@ -78,5 +78,5 @@ await test('modify enum on edge', async (t) => { .query2('holder', id1) .include('toThing.$edgeOption') .get() - deepEqual(res2.toThing?.$edgeOption, 'second') + deepEqual(res2?.toThing.$edgeOption, 'second') }) diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts index 96382ac1d6..794624ae40 100644 --- a/test/modify/props/json.ts +++ b/test/modify/props/json.ts @@ -35,7 +35,7 @@ await test('modify json', async (t) => { await db.update('thing', id1, { data: null, }) - deepEqual((await db.query2('thing', id1).get()).data, null) + deepEqual((await db.query2('thing', id1).get())?.data, null) }) await test('modify json on edge', async (t) => { @@ -65,7 +65,7 @@ await test('modify json on edge', async (t) => { const res1 = await db.query2('holder', id1).include('toThing.$edgeData').get() - deepEqual(res1.toThing?.$edgeData, obj) + deepEqual(res1?.toThing.$edgeData, obj) const obj2 = { baz: 'qux' } await db.update('holder', id1, { @@ -76,5 +76,5 @@ await test('modify json on edge', async (t) => { }) const res2 = await db.query2('holder', id1).include('toThing.$edgeData').get() - deepEqual(res2.toThing?.$edgeData, obj2) + deepEqual(res2?.toThing.$edgeData, obj2) }) diff --git a/test/modify/props/numbers.ts b/test/modify/props/numbers.ts index ae04304156..f31c83ff72 100644 --- a/test/modify/props/numbers.ts +++ b/test/modify/props/numbers.ts @@ -249,18 +249,20 @@ await test('modify numbers on edge', async (t) => { ) .get() - if (!res.toThing || Array.isArray(res.toThing)) { + const toThing = res?.toThing + + if (!toThing || Array.isArray(toThing)) { return {} } return { - edgeN: res.toThing.$edgeN, - edgeU8: res.toThing.$edgeU8, - edgeI8: res.toThing.$edgeI8, - edgeU16: res.toThing.$edgeU16, - edgeI16: res.toThing.$edgeI16, - edgeU32: res.toThing.$edgeU32, - edgeI32: res.toThing.$edgeI32, + edgeN: toThing.$edgeN, + edgeU8: toThing.$edgeU8, + edgeI8: toThing.$edgeI8, + edgeU16: toThing.$edgeU16, + edgeI16: toThing.$edgeI16, + edgeU32: toThing.$edgeU32, + edgeI32: toThing.$edgeI32, } } diff --git a/test/modify/props/string.ts b/test/modify/props/string.ts index 6ff7578076..d00026cc3b 100644 --- a/test/modify/props/string.ts +++ b/test/modify/props/string.ts @@ -110,7 +110,7 @@ await test('modify string on edge', async (t) => { const res1 = await db.query2('holder', id1).include('toThing.$edgeName').get() - deepEqual(res1.toThing?.$edgeName, s1) + deepEqual(res1?.toThing?.$edgeName, s1) // Update to another string const s2 = 'world' @@ -121,7 +121,7 @@ await test('modify string on edge', async (t) => { }, }) const res2 = await db.query2('holder', id1).include('toThing.$edgeName').get() - deepEqual(res2.toThing?.$edgeName, s2) + deepEqual(res2?.toThing?.$edgeName, s2) // String with spaces const s3 = 'foo bar' @@ -132,7 +132,7 @@ await test('modify string on edge', async (t) => { }, }) const res3 = await db.query2('holder', id1).include('toThing.$edgeName').get() - deepEqual(res3.toThing?.$edgeName, s3) + deepEqual(res3?.toThing?.$edgeName, s3) // Empty string const s4 = '' @@ -143,7 +143,7 @@ await test('modify string on edge', async (t) => { }, }) const res4 = await db.query2('holder', id1).include('toThing.$edgeName').get() - deepEqual(res4.toThing?.$edgeName, s4) + deepEqual(res4?.toThing?.$edgeName, s4) // Unicode / Special characters const s5 = 'ñàéïô SPECIAL !@#$%^&*()_+ 123' @@ -154,7 +154,7 @@ await test('modify string on edge', async (t) => { }, }) const res5 = await db.query2('holder', id1).include('toThing.$edgeName').get() - deepEqual(res5.toThing?.$edgeName, s5.normalize('NFD')) + deepEqual(res5?.toThing?.$edgeName, s5.normalize('NFD')) // Long string const s6 = 'a'.repeat(1000) @@ -165,7 +165,7 @@ await test('modify string on edge', async (t) => { }, }) const res6 = await db.query2('holder', id1).include('toThing.$edgeName').get() - deepEqual(res6.toThing?.$edgeName, s6) + deepEqual(res6?.toThing?.$edgeName, s6) }) await test('modify fixed string', async (t) => { diff --git a/test/query/ast.ts b/test/query/ast.ts index 9ed8307f12..1dcad18e41 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -4,9 +4,14 @@ import test from '../shared/test.js' await test('query types', async (t) => { const db = await testDb(t, { + locales: { + en: true, + nl: true, + }, types: { soAnnoy: { title: 'string', + users: { items: { ref: 'user', @@ -17,6 +22,7 @@ await test('query types', async (t) => { user: { name: 'string', isNice: 'boolean', + textField: 'text', friend: { ref: 'user', prop: 'friend', @@ -35,6 +41,10 @@ await test('query types', async (t) => { const userA = db.create('user', { isNice: true, + textField: { + nl: 'mijn text', + en: 'my text', + }, // annoyingThings: [] }) @@ -45,24 +55,17 @@ await test('query types', async (t) => { const query = db .query2('user') - .include( - 'isNice', - 'name', - 'friend.$rank', - 'otherUsers.$role', - 'otherUsers.name', - 'otherUsers.isNice', - ) + .include('isNice', 'name', 'otherUsers', 'textField', 'friend') const result = await query.get() for (const { name, isNice, otherUsers, friend } of result) { - const $rank: number = friend.$rank + const friendName = friend?.name for (const item of otherUsers) { const name: string = item.name const isNice: boolean = item.isNice const id: number = item.id - const $role: string = item.$role + const textField: { nl: string; en: string } = item.textField } } }) From 1cad6364abfb8d0a7e9f8c27a5e37dce07f13c70 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 16:05:24 +0100 Subject: [PATCH 186/449] update tests --- test/modify/props/binary.ts | 4 ++-- test/modify/props/cardinality.ts | 4 ++-- test/modify/props/default.ts | 4 ++-- test/modify/props/enum.ts | 4 ++-- test/modify/props/json.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/modify/props/binary.ts b/test/modify/props/binary.ts index 7e1f338481..938b780efb 100644 --- a/test/modify/props/binary.ts +++ b/test/modify/props/binary.ts @@ -62,7 +62,7 @@ await test('modify binary on edge', async (t) => { const res1 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() - deepEqual(res1?.toThing.$edgeBlob, b1) + deepEqual(res1?.toThing?.$edgeBlob, b1) const b2 = new Uint8Array([4, 5, 6, 7]) await db.update('holder', id1, { @@ -74,5 +74,5 @@ await test('modify binary on edge', async (t) => { const res2 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() - deepEqual(res2?.toThing.$edgeBlob, b2) + deepEqual(res2?.toThing?.$edgeBlob, b2) }) diff --git a/test/modify/props/cardinality.ts b/test/modify/props/cardinality.ts index 638bf9a6a7..5f16be3252 100644 --- a/test/modify/props/cardinality.ts +++ b/test/modify/props/cardinality.ts @@ -74,7 +74,7 @@ await test('modify cardinality on edge', async (t) => { .include('toThing.$edgeCounter') .get() - deepEqual(res1?.toThing.$edgeCounter, 1) + deepEqual(res1?.toThing?.$edgeCounter, 1) await db.update('holder', id1, { toThing: { @@ -88,7 +88,7 @@ await test('modify cardinality on edge', async (t) => { .include('toThing.$edgeCounter') .get() - deepEqual(res2?.toThing.$edgeCounter, 2) + deepEqual(res2?.toThing?.$edgeCounter, 2) }) await test('modify cardinality array', async (t) => { diff --git a/test/modify/props/default.ts b/test/modify/props/default.ts index 7d51eb03b3..9da5f96955 100644 --- a/test/modify/props/default.ts +++ b/test/modify/props/default.ts @@ -104,8 +104,8 @@ await test('modify - default values on edge', async (t) => { .include('member.id') .get() - deepEqual(resG1?.member.$role, 'member') - deepEqual(resG1?.member.$level, 1) + deepEqual(resG1?.member?.$role, 'member') + deepEqual(resG1?.member?.$level, 1) // 2. Create edge with edge props const g2 = await db.create('group', { diff --git a/test/modify/props/enum.ts b/test/modify/props/enum.ts index 82904f8403..7153d394aa 100644 --- a/test/modify/props/enum.ts +++ b/test/modify/props/enum.ts @@ -65,7 +65,7 @@ await test('modify enum on edge', async (t) => { .include('toThing.$edgeOption') .get() - deepEqual(res1?.toThing.$edgeOption, 'first') + deepEqual(res1?.toThing?.$edgeOption, 'first') await db.update('holder', id1, { toThing: { @@ -78,5 +78,5 @@ await test('modify enum on edge', async (t) => { .query2('holder', id1) .include('toThing.$edgeOption') .get() - deepEqual(res2?.toThing.$edgeOption, 'second') + deepEqual(res2?.toThing?.$edgeOption, 'second') }) diff --git a/test/modify/props/json.ts b/test/modify/props/json.ts index 794624ae40..4d78e2b1c2 100644 --- a/test/modify/props/json.ts +++ b/test/modify/props/json.ts @@ -65,7 +65,7 @@ await test('modify json on edge', async (t) => { const res1 = await db.query2('holder', id1).include('toThing.$edgeData').get() - deepEqual(res1?.toThing.$edgeData, obj) + deepEqual(res1?.toThing?.$edgeData, obj) const obj2 = { baz: 'qux' } await db.update('holder', id1, { @@ -76,5 +76,5 @@ await test('modify json on edge', async (t) => { }) const res2 = await db.query2('holder', id1).include('toThing.$edgeData').get() - deepEqual(res2?.toThing.$edgeData, obj2) + deepEqual(res2?.toThing?.$edgeData, obj2) }) From 4989f3687358c6b4446671fae95a8d2bb6c9192e Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 17:34:02 +0100 Subject: [PATCH 187/449] make types pretty and great --- src/db-client/index.ts | 12 ++--- src/db-client/modify/types.ts | 66 ++++++++++++++++------- src/db-client/query2/types.ts | 99 +++++++++++++++++++---------------- src/index.ts | 10 ++++ src/schema/schema/schema.ts | 51 +++++++++++------- test/query/ast.ts | 7 ++- test/shared/index.ts | 52 ++++++++++++++++-- 7 files changed, 204 insertions(+), 93 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 1f7d2e6390..05a9908027 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -31,18 +31,18 @@ type DbClientOpts = { debug?: boolean } -type BasedCreatePromise = BasedModify -type BasedUpdatePromise = BasedModify -type BasedDeletePromise = BasedModify -type BasedUpsertPromise = BasedModify -type BasedInsertPromise = BasedUpsertPromise +export type BasedCreatePromise = BasedModify +export type BasedUpdatePromise = BasedModify +export type BasedDeletePromise = BasedModify +export type BasedUpsertPromise = BasedModify +export type BasedInsertPromise = BasedUpsertPromise export type ModifyOpts = { unsafe?: boolean locale?: keyof typeof LangCode } -export class DbClient extends DbShared { +export class DbClient extends DbShared { constructor({ hooks, maxModifySize = 100 * 1e3 * 1e3, diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 4e79bf0171..0f9fcd8cfe 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -35,14 +35,22 @@ type TypeMap = { cardinality: string | string[] } +type EdgeKeys = keyof T extends infer K + ? K extends string + ? string extends K + ? never + : K extends `$${string}` + ? K + : never + : never + : never + type InferEdgeProps< Prop, Types, Locales extends Record = Record, > = { - [K in keyof Prop as K extends `$${string}` - ? K - : never]?: Prop[K] extends keyof TypeMap + [K in EdgeKeys]?: Prop[K] extends keyof TypeMap ? TypeMap[Prop[K]] : InferProp } @@ -53,8 +61,14 @@ type InferRefValue< Locales extends Record = Record, > = | number - | BasedModify - | ({ id: number | BasedModify } & InferEdgeProps) + | BasedModify + | (EdgeKeys extends never + ? { id: number | BasedModify } + : { id: number | BasedModify } & InferEdgeProps< + Prop, + Types, + Locales + >) type InferReferences< Prop, @@ -63,9 +77,9 @@ type InferReferences< > = | InferRefValue[] | { - add?: InferRefValue[] - update?: InferRefValue[] - delete?: (number | BasedModify)[] + add?: Prettify>[] + update?: Prettify>[] + delete?: (number | BasedModify)[] } type InferProp< @@ -81,24 +95,38 @@ type InferProp< : Prop extends { enum: infer E extends readonly any[] } ? E[number] : Prop extends { ref: string } - ? InferRefValue + ? Prettify> : Prop extends { items: { ref: string } } - ? InferReferences + ? Prettify> : never +type Prettify = Target extends any + ? Target extends (infer U)[] + ? Prettify[] + : Target extends BasedModify + ? Target + : Target extends object + ? { + -readonly [K in keyof Target]: Target[K] + } + : Target + : never + type InferType< Props, Types, Locales extends Record = Record, -> = { - [K in keyof Props as Props[K] extends { required: true } - ? K - : never]: InferProp -} & { - [K in keyof Props as Props[K] extends { required: true } - ? never - : K]?: InferProp | null -} +> = Prettify< + { + [K in keyof Props as Props[K] extends { required: true } + ? K + : never]: InferProp + } & { + [K in keyof Props as Props[K] extends { required: true } + ? never + : K]?: InferProp | null + } +> export type InferPayload< S extends { types: any; locales?: any }, diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 0f162c3faf..bf85a95ea7 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -45,33 +45,40 @@ export type FilterEdges = { [K in keyof T as K extends `$${string}` ? K : never]: T[K] } +// Utility to clean up intersection types +type Prettify = { + [K in keyof T]: T[K] +} & {} + export type PickOutputFromProps< S extends { types: any; locales?: any }, Props, K, -> = { - [P in Extract | 'id']: P extends 'id' - ? number - : P extends keyof Props - ? IsRefProp extends true - ? Props[P] extends { items: any } - ? { id: number }[] - : { id: number } - : InferProp< - Props[P], - S['types'], - S['locales'] extends Record ? S['locales'] : {} - > - : never -} & { - [Item in Extract as Item['field'] & - keyof Props]: InferProp< - Props[Item['field'] & keyof Props], - S['types'], - S['locales'] extends Record ? S['locales'] : {}, - Item['select'] - > -} +> = Prettify< + { + [P in Extract | 'id']: P extends 'id' + ? number + : P extends keyof Props + ? IsRefProp extends true + ? Props[P] extends { items: any } + ? { id: number }[] + : { id: number } + : InferProp< + Props[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : never + } & { + [Item in Extract as Item['field'] & + keyof Props]: InferProp< + Props[Item['field'] & keyof Props], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + Item['select'] + > + } +> export type InferProp< Prop, @@ -159,29 +166,31 @@ export type PickOutput< S extends { types: any; locales?: any }, T extends keyof S['types'], K, -> = { - [P in - | Extract> - | 'id']: P extends keyof ResolvedProps - ? IsRefProp[P]> extends true - ? InferProp< - ResolvedProps[P], - S['types'], - S['locales'] extends Record ? S['locales'] : {}, - '*' - > +> = Prettify< + { + [P in + | Extract> + | 'id']: P extends keyof ResolvedProps + ? IsRefProp[P]> extends true + ? InferProp< + ResolvedProps[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + '*' + > + : InferSchemaOutput[P] : InferSchemaOutput[P] - : InferSchemaOutput[P] -} & { - [Item in Extract as Item['field'] & - keyof ResolvedProps]: InferProp< - ResolvedProps[Item['field'] & - keyof ResolvedProps], - S['types'], - S['locales'] extends Record ? S['locales'] : {}, - Item['select'] - > -} + } & { + [Item in Extract as Item['field'] & + keyof ResolvedProps]: InferProp< + ResolvedProps[Item['field'] & + keyof ResolvedProps], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + Item['select'] + > + } +> export type FilterOpts = { lowerCase?: boolean diff --git a/src/index.ts b/src/index.ts index b854834810..7f74cf5883 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,15 @@ import { Emitter } from './shared/Emitter.js' import wait from './utils/wait.js' // export { stringCompress } export { DbClient, DbServer } +export type { + BasedCreatePromise, + BasedUpdatePromise, + BasedDeletePromise, + BasedUpsertPromise, + BasedInsertPromise, + ModifyOpts, +} from './db-client/index.js' +export type { InferPayload, InferTarget } from './db-client/modify/types.js' export { xxHash64 } from './db-client/xxHash64.js' export { crc32 } from './db-client/crc32.js' export { default as createHash } from './db-server/dbHash.js' @@ -15,6 +24,7 @@ export * from './db-client/query/query.js' export * from './db-client/query/BasedDbQuery.js' export * from './db-client/query/BasedQueryResponse.js' export * from './db-client/hooks.js' +export { BasedModify } from './db-client/modify/index.js' export const SCHEMA_FILE_DEPRECATED = 'schema.json' export const SCHEMA_FILE = 'schema.bin' diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index f12e53a6f0..d298478d0a 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -58,6 +58,11 @@ type NormalizeProp = T extends string ? T & { type: 'enum' } : T +// Utility to normalize properties in an object +type NormalizeEdges = { + [K in keyof T]: NormalizeProp +} + // Utility to convert a Union to an Intersection type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( k: infer I, @@ -87,7 +92,11 @@ type GetBackRefs = UnionToIntersection< type: 'reference' ref: K & string prop: P & string - } + } & NormalizeEdges< + Props[P] extends { items: infer I } + ? Omit + : Omit + > } } : never @@ -108,8 +117,7 @@ export type ResolvedProps< K, string >]: (Props & - ([BackRefs] extends [never] ? {} : Omit))[K] & - SchemaProp + ([BackRefs] extends [never] ? {} : Omit))[K] } type NormalizeType = T extends { props: infer P } @@ -192,23 +200,28 @@ export type ValidateSchema = Omit & { import { type LangName, type SchemaLocale } from './locales.js' -export type ResolveSchema = Omit< - SchemaOut, - 'types' | 'locales' -> & { - types: { - [K in keyof S['types']]: Omit, 'props'> & { - props: ResolvedProps +type Prettify = { + [K in keyof T]: T[K] +} & {} + +export type ResolveSchema = Prettify< + Omit & { + types: { + [K in keyof S['types']]: Prettify< + Omit, 'props'> & { + props: ResolvedProps + } + > } + locales: S extends { locales: infer L } + ? L extends readonly (infer K extends LangName)[] + ? Partial>> + : L extends Record + ? Partial>> + : SchemaLocales + : SchemaLocales } - locales: S extends { locales: infer L } - ? L extends readonly (infer K extends LangName)[] - ? Partial>> - : L extends Record - ? Partial>> - : SchemaLocales - : SchemaLocales -} +> const isMigrations = (v: unknown): v is SchemaMigrations => isRecord(v) && @@ -316,7 +329,7 @@ export const parseSchema = ( // TODO we can remove hash from here after we finish new schema defs (internal schema) result.hash = hash(result) - return result as ResolveSchema + return result as unknown as ResolveSchema } catch (e) { if (tracking) { e = Error(`${path.join('.')}: ${inspect(value)} - ${e}`, { cause: e }) diff --git a/test/query/ast.ts b/test/query/ast.ts index 1dcad18e41..132af391e6 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -45,7 +45,12 @@ await test('query types', async (t) => { nl: 'mijn text', en: 'my text', }, - // annoyingThings: [] + annoyingThings: [ + { + id: 2, + // $snurk: true, + }, + ], }) db.create('soAnnoy', { diff --git a/test/shared/index.ts b/test/shared/index.ts index e1394657a1..74584f03cb 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -39,14 +39,57 @@ export function logMemoryUsage() { } } +import { + BasedCreatePromise, + BasedDeletePromise, + BasedInsertPromise, + BasedUpdatePromise, + BasedUpsertPromise, + ModifyOpts, + InferPayload, + InferTarget, + BasedModify, +} from '../../src/sdk.js' + +export interface BasedTestDb + extends DbClient> { + create( + type: T, + obj: InferPayload, T>, + opts?: ModifyOpts, + ): BasedCreatePromise + update( + type: T, + target: number | BasedModify, + obj: InferPayload, T>, + opts?: ModifyOpts, + ): BasedUpdatePromise + upsert( + type: T, + target: InferTarget, T>, + obj: InferPayload, T>, + opts?: ModifyOpts, + ): BasedUpsertPromise + insert( + type: T, + target: InferTarget, T>, + obj: InferPayload, T>, + opts?: ModifyOpts, + ): BasedInsertPromise + delete( + type: keyof S['types'] & string, + target: number | BasedModify, + ): BasedDeletePromise +} + export const testDb = async ( t, schema: S & ValidateSchema, -): Promise>> => { +): Promise> => { const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) t.after(() => db.destroy()) - return db.setSchema(schema) as unknown as DbClient> + return db.setSchema(schema) as unknown as BasedTestDb } export async function countDirtyBlocks(server: DbServer) { @@ -76,7 +119,10 @@ export const getActiveBlocks = async ( const block2start = (block: number, capacity: number): number => block * capacity + 1 -export const hashType = async (db: DbServer, typeName: string): Promise => { +export const hashType = async ( + db: DbServer, + typeName: string, +): Promise => { const tc = db.schemaTypesParsed[typeName].id const capacity = db.schemaTypesParsed[typeName].blockCapacity const hash = createHash('sha256') From 56e8aba85de93dd0d011674f5efbc4cd4251b8cc Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 6 Feb 2026 17:42:30 +0100 Subject: [PATCH 188/449] make it better --- src/db-client/query2/types.ts | 8 +++-- test/modify/type_display.ts | 67 +++++++++++++++++++++++++++++++++++ test/query/ast.ts | 25 +++++++------ 3 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 test/modify/type_display.ts diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index bf85a95ea7..04c9806d98 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -231,14 +231,18 @@ export type Path = [ ? `${K}.${ | Path | (keyof FilterEdges[K]> & string) - | 'id'}` + | 'id' + | '*' + | '**'}` : ResolvedProps[K] extends { items: { ref: infer R extends string } & infer Items } ? `${K}.${ | Path | (keyof FilterEdges & string) - | 'id'}` + | 'id' + | '*' + | '**'}` : never) }[keyof ResolvedProps & string] diff --git a/test/modify/type_display.ts b/test/modify/type_display.ts new file mode 100644 index 0000000000..98b3b16030 --- /dev/null +++ b/test/modify/type_display.ts @@ -0,0 +1,67 @@ +import { BasedTestDb, testDb } from '../../test/shared/index.js' + +const schema = { + locales: { + en: true, + nl: true, + }, + types: { + soAnnoy: { + title: 'string', + users: { + items: { + ref: 'user', + prop: 'annoyingThings', + $validEdge: 'boolean', + }, + }, + }, + user: { + name: 'string', + isNice: 'boolean', + textField: 'text', + friend: { + ref: 'user', + prop: 'friend', + $rank: 'number', + }, + otherUsers: { + items: { + ref: 'user', + prop: 'otherUsers', + $role: 'string', + }, + }, + }, + }, +} as const + +// Simulated usage +async function run() { + const t = { after: () => {}, tmp: './tmp' } + const db: BasedTestDb = await testDb(t, schema) + // Inspect this function signature via d.ts + checkTypes(db) +} + +export function checkTypes(db: BasedTestDb) { + const userA = db.create('user', { + isNice: true, + textField: { + nl: 'mijn text', + en: 'my text', + }, + annoyingThings: [ + { + id: 2, + // @ts-expect-error + $invalidEdge: true, // Should error + $validEdge: true, + }, + { + id: 3, + // correct + }, + ], + }) +} diff --git a/test/query/ast.ts b/test/query/ast.ts index 132af391e6..5a8bcc0f07 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -58,19 +58,18 @@ await test('query types', async (t) => { users: [userA], }) - const query = db - .query2('user') - .include('isNice', 'name', 'otherUsers', 'textField', 'friend') + // const query = db.query2('user').include('isNice', 'name', '**.id') - const result = await query.get() + // const result = await query.get() - for (const { name, isNice, otherUsers, friend } of result) { - const friendName = friend?.name - for (const item of otherUsers) { - const name: string = item.name - const isNice: boolean = item.isNice - const id: number = item.id - const textField: { nl: string; en: string } = item.textField - } - } + // for (const { name, isNice, otherUsers, friend } of result) { + // // const friendName = friend?.name + // const friendFriend = friend?.isNice + // for (const item of otherUsers) { + // const name: string = item.name + // const isNice: boolean = item.isNice + // const id: number = item.id + // const textField: { nl: string; en: string } = item.textField + // } + // } }) From 04cbdebace86b13209e4a18b5b9eb28e7090769d Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Fri, 6 Feb 2026 14:55:16 -0300 Subject: [PATCH 189/449] ongoing ast group by --- src/db-client/query/aggregates/aggregates.ts | 35 +++++ .../query/aggregates/aggregation_old.ts | 127 +----------------- src/db-query/ast/aggregates.ts | 69 +++++++++- test/query-ast/aggregates.ts | 77 ++++++++++- 4 files changed, 176 insertions(+), 132 deletions(-) diff --git a/src/db-client/query/aggregates/aggregates.ts b/src/db-client/query/aggregates/aggregates.ts index a63925eea0..cada904c02 100644 --- a/src/db-client/query/aggregates/aggregates.ts +++ b/src/db-client/query/aggregates/aggregates.ts @@ -30,3 +30,38 @@ export const isRootCountOnly = (def: QueryDef, filterSize: number) => { } return true } + +export const getTimeZoneOffsetInMinutes = ( + timeZone: string, + date: Date = new Date(), +): number => { + const formatter = new Intl.DateTimeFormat('en-US', { + timeZone, + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: false, + }) + + const parts = formatter.formatToParts(date) + const getPart = (partName: string) => + parseInt(parts.find((p) => p.type === partName)?.value || '0', 10) + + const targetTimeAsUTC = Date.UTC( + getPart('year'), + getPart('month') - 1, + getPart('day'), + getPart('hour'), + getPart('minute'), + getPart('second'), + ) + + const originalUTCTime = date.getTime() + const offsetInMilliseconds = targetTimeAsUTC - originalUTCTime + const offsetInMinutes = offsetInMilliseconds / (1000 * 60) + + return Math.round(offsetInMinutes) +} diff --git a/src/db-client/query/aggregates/aggregation_old.ts b/src/db-client/query/aggregates/aggregation_old.ts index 616c544a9d..ed83686489 100644 --- a/src/db-client/query/aggregates/aggregation_old.ts +++ b/src/db-client/query/aggregates/aggregation_old.ts @@ -19,65 +19,7 @@ import { AggFunction, type AggFunctionEnum, } from '../../../zigTsExports.js' - -// export const aggregateToBuffer = ( -// aggregates: QueryDefAggregation, -// ): Uint8Array => { -// const aggBuffer = new Uint8Array(aggregates.size) -// let i = 0 -// if (aggregates.groupBy) { -// aggBuffer[i] = GroupBy.HAS_GROUP -// i += 1 -// aggBuffer[i] = aggregates.groupBy.prop -// i += 1 -// aggBuffer[i] = aggregates.groupBy.typeIndex -// i += 1 -// writeUint16(aggBuffer, aggregates.groupBy.start, i) -// i += 2 -// writeUint16(aggBuffer, aggregates.groupBy.len, i) -// i += 2 -// aggBuffer[i] = aggregates.groupBy.stepType || 0 -// i += 1 -// writeUint32(aggBuffer, aggregates.groupBy.stepRange || 0, i) -// i += 4 -// writeInt16(aggBuffer, aggregates.groupBy.tz || 0, i) -// i += 2 -// } else { -// aggBuffer[i] = GroupBy.NONE -// i += 1 -// } -// writeUint16(aggBuffer, aggregates.totalResultsSize, i) -// i += 2 -// writeUint16(aggBuffer, aggregates.totalAccumulatorSize, i) -// i += 2 -// aggBuffer[i] = setMode[aggregates?.option?.mode!] || 0 -// i += 1 -// for (const [prop, aggregatesArray] of aggregates.aggregates.entries()) { -// aggBuffer[i] = prop -// i += 1 -// let sizeIndex = i -// let size = 0 -// i += 2 -// for (const agg of aggregatesArray) { -// let startI = i -// aggBuffer[i] = agg.type -// i += 1 -// aggBuffer[i] = agg.propDef.typeIndex -// i += 1 -// writeUint16(aggBuffer, agg.propDef.start!, i) -// i += 2 -// writeUint16(aggBuffer, agg.resultPos, i) -// i += 2 -// writeUint16(aggBuffer, agg.accumulatorPos, i) -// i += 2 -// aggBuffer[i] = agg.propDef.__isEdge ? 1 : 0 -// i += 1 -// size += i - startI -// } -// writeUint16(aggBuffer, size, sizeIndex) -// } -// return aggBuffer -// } +import { getTimeZoneOffsetInMinutes } from './aggregates.js' const ensureAggregate = (def: QueryDef) => { if (!def.aggregate) { @@ -315,70 +257,3 @@ export const addAggregate = ( } } } - -// export const isRootCountOnly = (def: QueryDef, filterSize: number) => { -// if (filterSize != 0) { -// return false -// } -// if (def.type !== QueryDefType.Root) { -// return false -// } -// const aggregate = def.aggregate! -// if (aggregate.groupBy) { -// return false -// } -// if (aggregate.aggregates.size !== 1) { -// return false -// } -// if (!aggregate.aggregates.has(255)) { -// return false -// } -// const aggs = aggregate.aggregates.get(255)! -// if (aggs.length !== 1) { -// return false -// } -// if (aggs[0].type !== AggFunction.count) { -// return false -// } - -// // later -// // if (def.filter && def.filter.size > 0) { -// return false -// // } -// // return true -// } - -function getTimeZoneOffsetInMinutes( - timeZone: string, - date: Date = new Date(), -): number { - const formatter = new Intl.DateTimeFormat('en-US', { - timeZone, - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - hour12: false, - }) - - const parts = formatter.formatToParts(date) - const getPart = (partName: string) => - parseInt(parts.find((p) => p.type === partName)?.value || '0', 10) - - const targetTimeAsUTC = Date.UTC( - getPart('year'), - getPart('month') - 1, - getPart('day'), - getPart('hour'), - getPart('minute'), - getPart('second'), - ) - - const originalUTCTime = date.getTime() - const offsetInMilliseconds = targetTimeAsUTC - originalUTCTime - const offsetInMinutes = offsetInMilliseconds / (1000 * 60) - - return Math.round(offsetInMinutes) -} diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index c8413cc9da..934796b633 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -6,13 +6,21 @@ import { AggHeaderByteSize, createAggHeader, createAggProp, + createGroupByKeyProp, + GroupByKeyPropByteSize, AggPropByteSize, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' -import { aggregateTypeMap } from '../../db-client/query/aggregates/types.js' +import { + aggregateTypeMap, + IntervalString, + Interval, +} from '../../db-client/query/aggregates/types.js' import { readPropDef } from './readSchema.js' -import { truncate } from 'node:fs' +import { getTimeZoneOffsetInMinutes } from '../../db-client/query/aggregates/aggregates.js' + +type Sizes = { result: number; accumulator: number } export const pushAggregatesQuery = ( ast: QueryAst, @@ -31,12 +39,12 @@ export const pushAggregatesQuery = ( filterSize = filter(ast.filter, ctx, typeDef) } - const sizes = { + let sizes: Sizes = { result: 0, accumulator: 0, } - const hasGroupBy = false // TODO : later + const hasGroupBy = pushGroupBy(ast, ctx, typeDef, sizes) pushAggregates(ast, ctx, typeDef, sizes) @@ -69,7 +77,7 @@ const buildAggregateHeader = ( typeDef: TypeDef, filterSize: number, hasGroupBy: boolean, - sizes: { result: number; accumulator: number }, + sizes: Sizes, ) => { const rangeStart = ast.range?.start || 0 @@ -217,3 +225,54 @@ const checkSamplingMode = (ast: QueryAst): boolean => { return false else return true } + +const pushGroupBy = ( + ast: QueryAst, + ctx: Ctx, + typeDef: TypeDef, + sizes: Sizes, +): boolean => { + if (!ast.groupBy) return false + + const { prop: propName, step, timeZone } = ast.groupBy + const propDef = typeDef.props.get(propName) + + if (!propDef) { + throw new Error(`Group By property '${propName}' not found in AST.`) + // to put the equivalent to aggregationFieldDoesNotExist to handle the error + } + + const { stepType, stepRange } = step + ? parseStep(step) + : { stepType: 0, stepRange: 0 } + + const timeZoneOffset = timeZone ? getTimeZoneOffsetInMinutes(timeZone) : 0 + + const buffer = createGroupByKeyProp({ + propId: propDef.id, + propType: propDef.type || 0, + propDefStart: propDef.start || 0, + stepType, + stepRange, + timezone: timeZoneOffset, + }) + + ctx.query.data.set(buffer, ctx.query.length) + ctx.query.length += GroupByKeyPropByteSize + + return true +} + +type Step = { stepType: number; stepRange: number } +const parseStep = (step: number | IntervalString): Step => { + let stepRange = 0 + let stepType = 0 + if (typeof step === 'string') { + const intervalEnumKey = step as IntervalString + stepType = Interval[intervalEnumKey] + } else { + // validateStepRange(def, step) // TODO: see/make the equivalent for def.errors + stepRange = step + } + return { stepType, stepRange } as Step +} diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index f10dde6d66..c9066eb9c0 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -72,7 +72,7 @@ await test('basic', async (t) => { 'basic accum, no groupby, no refs', ) - console.dir(obj, { depth: 10 }) + // console.dir(obj, { depth: 10 }) console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) @@ -82,3 +82,78 @@ await test('basic', async (t) => { // r.debug() // r.inspect(10) }) + +await test('group by', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => db.stop()) + + const client = await db.setSchema({ + types: { + trip: { + pickup: 'timestamp', + dropoff: 'timestamp', + distance: 'number', + vendorIduint8: 'uint8', + vendorIdint8: 'int8', + vendorIduint16: 'uint16', + vendorIdint16: 'int16', + vendorIduint32: 'int32', + vendorIdint32: 'int32', + vendorIdnumber: 'number', + vendorName: 'string', + }, + }, + }) + + db.create('trip', { + vendorIduint8: 13, + vendorIdint8: 13, + vendorIduint16: 813, + vendorIdint16: 813, + vendorIduint32: 813, + vendorIdint32: 813, + vendorIdnumber: 813.813, + vendorName: 'Derp taxis', + pickup: new Date('11/12/2024 11:00'), + dropoff: new Date('11/12/2024 11:10'), + distance: 513.44, + }) + + await db.drain() + const ast: QueryAst = { + type: 'trip', + sum: { + props: ['distance'], + }, + // count: {}, + groupBy: { + prop: 'vendorName', + // step?: number | IntervalString + // timeZone?: string + // timeFormat?: Intl.DateTimeFormat + }, + } + // const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + // const result = await db.server.getQueryBuf(ctx.query) + // debugBuffer(result) + + // const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + + // const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + // console.dir(obj, { depth: 10 }) + + console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + + const r = await db + .query('trip') + // .count() + .sum('distance') + .groupBy('vendorName') + .get() + r.debug() + r.inspect(10) +}) From 24051892faf2c58e658269bc927e371d0d5c0a8d Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Sat, 7 Feb 2026 16:15:53 -0300 Subject: [PATCH 190/449] fix ast groupBy size handling --- src/db-client/query/queryDefToReadSchema.ts | 1 + src/db-query/ast/aggregates.ts | 27 +++++++++++++++++---- test/query-ast/aggregates.ts | 15 ++++++------ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/db-client/query/queryDefToReadSchema.ts b/src/db-client/query/queryDefToReadSchema.ts index c7a2823b84..520f60d3b6 100644 --- a/src/db-client/query/queryDefToReadSchema.ts +++ b/src/db-client/query/queryDefToReadSchema.ts @@ -140,6 +140,7 @@ export const convertToReaderSchema = ( if (q.aggregate.groupBy.display) { a.groupBy.display = q.aggregate.groupBy.display } + // MV: Tto review if (q.aggregate.groupBy.enum) { a.groupBy.enum = q.aggregate.groupBy.enum } diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index 934796b633..b041f5e82d 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -9,6 +9,7 @@ import { createGroupByKeyProp, GroupByKeyPropByteSize, AggPropByteSize, + type QueryIteratorTypeEnum, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' @@ -97,15 +98,16 @@ const buildAggregateHeader = ( // TODO: references + let iteratorType = QueryIteratorType.aggregate + if (hasGroupBy) iteratorType += 2 + if (filterSize > 0) iteratorType += 1 + headerBuffer = createAggHeader({ ...commonHeader, op, typeId: typeDef.id, limit: (ast.range?.end || 1000) + rangeStart, - iteratorType: - filterSize === 0 - ? QueryIteratorType.aggregate - : QueryIteratorType.aggregateFilter, // TODO : later + iteratorType: iteratorType as QueryIteratorTypeEnum, }) return headerBuffer } @@ -116,6 +118,12 @@ const pushAggregates = ( typeDef: TypeDef, sizes: { result: number; accumulator: number }, ) => { + ctx.readSchema.aggregate = ctx.readSchema.aggregate || { + aggregates: [], + totalResultsSize: 0, + groupBy: undefined, + } + // this for loop may be temporary // need to support repeated funcs or keep it very strict // adding a validation to force only distinct funcs with props[] @@ -198,6 +206,8 @@ const pushAggregates = ( sizes.result += resSize sizes.accumulator += accSize + + ctx.readSchema.aggregate.totalResultsSize += resSize } } } @@ -259,7 +269,14 @@ const pushGroupBy = ( ctx.query.data.set(buffer, ctx.query.length) ctx.query.length += GroupByKeyPropByteSize - + if (ctx.readSchema.aggregate) { + ctx.readSchema.aggregate.groupBy = { + typeIndex: propDef.type, + // stepRange, // MV: TODO review + // stepType: false, + // enum: [], + } + } return true } diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index c9066eb9c0..e4e4b8c074 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -136,15 +136,15 @@ await test('group by', async (t) => { // timeFormat?: Intl.DateTimeFormat }, } - // const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) - // const result = await db.server.getQueryBuf(ctx.query) - // debugBuffer(result) + const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + const result = await db.server.getQueryBuf(ctx.query) + debugBuffer(result) - // const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) - // const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - // console.dir(obj, { depth: 10 }) + console.dir(obj, { depth: 10 }) console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') @@ -154,6 +154,7 @@ await test('group by', async (t) => { .sum('distance') .groupBy('vendorName') .get() + r.debug() - r.inspect(10) + console.dir(r.toObject(), { depth: 10 }) }) From c333ae544c11cc710f848d3d86406d6f833c45f6 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Sat, 7 Feb 2026 16:37:48 -0300 Subject: [PATCH 191/449] aggFunction loop --- src/db-query/ast/aggregates.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index b041f5e82d..686f8f0233 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -10,6 +10,7 @@ import { GroupByKeyPropByteSize, AggPropByteSize, type QueryIteratorTypeEnum, + type AggFunctionEnum, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' @@ -124,25 +125,14 @@ const pushAggregates = ( groupBy: undefined, } - // this for loop may be temporary - // need to support repeated funcs or keep it very strict - // adding a validation to force only distinct funcs with props[] - const aggs = [ - { key: 'count', fn: AggFunction.count }, - { key: 'sum', fn: AggFunction.sum }, - { key: 'avg', fn: AggFunction.avg }, - { key: 'min', fn: AggFunction.min }, - { key: 'max', fn: AggFunction.max }, - { key: 'cardinality', fn: AggFunction.cardinality }, - { key: 'stddev', fn: AggFunction.stddev }, - { key: 'var', fn: AggFunction.variance }, - { key: 'harmonicMean', fn: AggFunction.hmean }, - ] - - for (const { key, fn } of aggs) { + for (const key in AggFunction) { + if (!(key in ast)) continue + const data = ast[key] if (!data) continue + const fn = AggFunction[key] + let props = Array.isArray(data.props) ? data.props : data.props From af83c48774a2c546b407005b538018b076e48ea2 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 09:55:11 -0300 Subject: [PATCH 192/449] fix display time interval format on groupBy ast --- src/db-query/ast/aggregates.ts | 12 ++- src/db-query/ast/ast.ts | 2 +- test/query-ast/aggregates.ts | 147 +++++++++++++++++++++++++++++---- 3 files changed, 142 insertions(+), 19 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index 686f8f0233..baae861110 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -11,6 +11,7 @@ import { AggPropByteSize, type QueryIteratorTypeEnum, type AggFunctionEnum, + IntervalInverse, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' @@ -234,7 +235,7 @@ const pushGroupBy = ( ): boolean => { if (!ast.groupBy) return false - const { prop: propName, step, timeZone } = ast.groupBy + const { prop: propName, step, timeZone, display } = ast.groupBy const propDef = typeDef.props.get(propName) if (!propDef) { @@ -259,14 +260,17 @@ const pushGroupBy = ( ctx.query.data.set(buffer, ctx.query.length) ctx.query.length += GroupByKeyPropByteSize + if (ctx.readSchema.aggregate) { ctx.readSchema.aggregate.groupBy = { typeIndex: propDef.type, - // stepRange, // MV: TODO review - // stepType: false, - // enum: [], + stepRange, + ...(stepType !== 0 && { stepType: IntervalInverse[stepType] }), + ...(display !== undefined && { display }), + // enum: [], // TODO: review enum engine } } + return true } diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index d8c4c38b19..749e8090f2 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -80,7 +80,7 @@ export type QueryAst = { prop: string step?: number | IntervalString timeZone?: string - timeFormat?: Intl.DateTimeFormat + display?: Intl.DateTimeFormat } } diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index e4e4b8c074..828ccf6a3d 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -117,34 +117,149 @@ await test('group by', async (t) => { vendorIdint32: 813, vendorIdnumber: 813.813, vendorName: 'Derp taxis', - pickup: new Date('11/12/2024 11:00'), - dropoff: new Date('11/12/2024 11:10'), + pickup: new Date('2024-12-11T11:00-03:00'), + dropoff: new Date('2024-12-11T11:10-03:00'), distance: 513.44, }) + db.create('trip', { + vendorIduint8: 13, + vendorIdint8: 13, + vendorIduint16: 813, + vendorIdint16: 813, + vendorIduint32: 813, + vendorIdint32: 813, + vendorIdnumber: 813.813, + vendorName: 'Derp taxis', + pickup: new Date('2024-12-11T13:00-03:00'), + dropoff: new Date('2024-12-11T13:30-03:00'), + distance: 100.1, + }) + await db.drain() - const ast: QueryAst = { + + // --------------- Group By string key --------------- // + let ast: any + let ctx: any + let result: any + let readSchemaBuf: any + let obj: any + + ast = { type: 'trip', sum: { props: ['distance'], }, - // count: {}, groupBy: { prop: 'vendorName', - // step?: number | IntervalString - // timeZone?: string - // timeFormat?: Intl.DateTimeFormat + }, + } as QueryAst + ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + result = await db.server.getQueryBuf(ctx.query) + // debugBuffer(result) + + readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + + obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + // console.dir(obj, { depth: 10 }) + deepEqual( + obj, + { 'Derp taxis': { distance: { sum: 613.5400000000001 } } }, + 'Group By string key', + ) // TODO: rounding check + + // --------------- Group By numeric key --------------- // + ast = { + type: 'trip', + sum: { + props: ['distance'], + }, + count: {}, + groupBy: { + prop: 'vendorIduint32', }, } - const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) - const result = await db.server.getQueryBuf(ctx.query) - debugBuffer(result) + ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + result = await db.server.getQueryBuf(ctx.query) - const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + readSchemaBuf = await serializeReaderSchema(ctx.readSchema) - const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + deepEqual( + obj, + { '813': { count: 2, distance: { sum: 613.5400000000001 } } }, + 'Group By numeric key', + ) + + // --------------- Group By named interval --------------- // + + const dtFormat = new Intl.DateTimeFormat('pt-BR', { + dateStyle: 'short', + timeStyle: 'short', + timeZone: 'America/Sao_Paulo', + }) + + ast = { + type: 'trip', + sum: { + props: ['distance'], + }, + count: {}, + groupBy: { + prop: 'pickup', + step: 'day', + timeFormat: dtFormat, + timeZone: 'America/Sao_Paulo', + }, + } as QueryAst + ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + result = await db.server.getQueryBuf(ctx.query) + + readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + + obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + deepEqual( + obj, + { '11': { count: 2, distance: { sum: 613.5400000000001 } } }, + 'Group By named interval', + ) + + // --------------- Group By range interval with output format --------------- // + ast = { + type: 'trip', + sum: { + props: ['distance'], + }, + count: {}, + groupBy: { + prop: 'pickup', + step: 2.5 * 60 * 60, // 2:30h = 2.5 * 3600s + display: dtFormat, + timeZone: 'America/Sao_Paulo', + }, + } as QueryAst + ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + result = await db.server.getQueryBuf(ctx.query) + + readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + + obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + + deepEqual( + obj, + { + '11/12/2024 11:00 – 13:30': { + count: 2, + distance: { sum: 613.5400000000001 }, + }, + }, + 'Group By range interval with output format', + ) - console.dir(obj, { depth: 10 }) + // --------------- Group By enum keys --------------- // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') @@ -152,7 +267,11 @@ await test('group by', async (t) => { .query('trip') // .count() .sum('distance') - .groupBy('vendorName') + .groupBy('pickup', { + timeZone: 'America/Sao_Paulo', + display: dtFormat, + step: 2.5 * 60 * 60, // 2:30h = 2.5 * 3600s + }) .get() r.debug() From db52ee0eef7b64db2d236cade5993a214ba11dd5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 15:43:39 +0100 Subject: [PATCH 193/449] Explicit toObject() is not needed --- test/boolean.ts | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/test/boolean.ts b/test/boolean.ts index 92efef8ebe..f870eda9ef 100644 --- a/test/boolean.ts +++ b/test/boolean.ts @@ -7,8 +7,7 @@ await test('boolean', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - // t.after(() => t.backup(db)) - t.after(() => db.stop(true)) + t.after(() => t.backup(db)) const client = await db.setSchema({ types: { @@ -32,26 +31,22 @@ await test('boolean', async (t) => { await client.drain() - deepEqual((await client.query('user').get()).toObject(), [ + deepEqual(await client.query('user').get(), [ { id: 1, isNice: false }, { id: 2, isNice: true }, { id: 3, isNice: false }, ]) - deepEqual( - (await client.query('user').filter('isNice', '=', true).get()).toObject(), - [{ id: 2, isNice: true }], - ) + deepEqual(await client.query('user').filter('isNice', '=', true).get(), [ + { id: 2, isNice: true }, + ]) - deepEqual((await client.query('user').filter('isNice').get()).toObject(), [ + deepEqual(await client.query('user').filter('isNice').get(), [ { id: 2, isNice: true }, ]) - deepEqual( - (await client.query('user').filter('isNice', '=', false).get()).toObject(), - [ - { id: 1, isNice: false }, - { id: 3, isNice: false }, - ], - ) + deepEqual(await client.query('user').filter('isNice', '=', false).get(), [ + { id: 1, isNice: false }, + { id: 3, isNice: false }, + ]) }) From 1b1eae96527fc8400f4e74bce725679ca64a8964 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 15:43:56 +0100 Subject: [PATCH 194/449] Proper typing --- test/vector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vector.ts b/test/vector.ts index 733c874aad..fcd57b1100 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -12,7 +12,7 @@ const data = { car: [81.6, -72.1, 16, -20.2, 102], } -async function initDb(t) { +async function initDb(t: Parameters[1]>[0]): Promise { const db = new BasedDb({ path: t.tmp, }) From 1a30a0a2f0dba3b155a41d2a6cc9240cf567211a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 15:46:44 +0100 Subject: [PATCH 195/449] Add FIXME --- test/vector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vector.ts b/test/vector.ts index fcd57b1100..5d0fe9c4b7 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -102,7 +102,7 @@ await test('query by vector', async (t) => { deepEqual(r2.length, 1) }) -// this is broken! see https://linear.app/1ce/issue/FDN-1302 needs alignment! +// FIXME this is broken! see https://linear.app/1ce/issue/FDN-1302 needs alignment! await test.skip('vector like', async (t) => { const db = await initDb(t) From 6009533bdcdfc9d5171e63b58816156ef471514d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 16:01:54 +0100 Subject: [PATCH 196/449] Cleanup --- test/text/text.ts | 251 ++++++++++++++++++++-------------------------- 1 file changed, 111 insertions(+), 140 deletions(-) diff --git a/test/text/text.ts b/test/text/text.ts index 942d51e56f..29ce5e1bb2 100644 --- a/test/text/text.ts +++ b/test/text/text.ts @@ -34,9 +34,8 @@ await test('simple', async (t) => { await db.drain() - let result = await db.query('dialog').include('id', 'fun').get() deepEqual( - result.toObject(), + await db.query('dialog').include('id', 'fun').get(), [ { id: dialogId, @@ -50,9 +49,8 @@ await test('simple', async (t) => { 'Initial dialog with fun property', ) - result = await db.query('dialog').include('id').get() deepEqual( - result.toObject(), + await db.query('dialog').include('id').get(), [ { id: dialogId, @@ -61,9 +59,8 @@ await test('simple', async (t) => { 'Dialog with only id included', ) - result = await db.query('dialog').locale('it').include('id', 'fun').get() deepEqual( - result.toObject(), + await db.query('dialog').locale('it').include('id', 'fun').get(), [ { id: dialogId, @@ -73,22 +70,23 @@ await test('simple', async (t) => { 'Dialog with locale set to it', ) - result = await db - .query('dialog') - .locale('it') - .include('id', 'fun') - .filter('fun', 'includes', 'fliperdieflaperdiefloep', { lowerCase: true }) - .get() - deepEqual(result.toObject(), [], 'Filter fun with non-existent text') - - result = await db - .query('dialog') - .include('id', 'fun') - .filter('fun', 'includes', 'italy', { lowerCase: true }) - .get() + deepEqual( + await db + .query('dialog') + .locale('it') + .include('id', 'fun') + .filter('fun', 'includes', 'fliperdieflaperdiefloep', { lowerCase: true }) + .get(), + [], + 'Filter fun with non-existent text', + ) deepEqual( - result.toObject(), + await db + .query('dialog') + .include('id', 'fun') + .filter('fun', 'includes', 'italy', { lowerCase: true }) + .get(), [ { id: dialogId, @@ -102,14 +100,13 @@ await test('simple', async (t) => { 'Filter fun with text italy', ) - result = await db - .query('dialog') - .locale('it') - .include('id', 'fun') - .filter('fun', 'includes', 'italy', { lowerCase: true }) - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .locale('it') + .include('id', 'fun') + .filter('fun', 'includes', 'italy', { lowerCase: true }) + .get(), [ { id: dialogId, @@ -119,20 +116,22 @@ await test('simple', async (t) => { 'Filter fun with text italy and locale set to it', ) - result = await db - .query('dialog') - .include('id', 'fun') - .filter('fun.en', 'includes', 'italy', { lowerCase: true }) - .get() - deepEqual(result.toObject(), [], 'Filter fun.en with text italy') - - result = await db - .query('dialog') - .include('id', 'fun') - .filter('fun.it', 'includes', 'italy', { lowerCase: true }) - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .include('id', 'fun') + .filter('fun.en', 'includes', 'italy', { lowerCase: true }) + .get(), + [], + 'Filter fun.en with text italy', + ) + + deepEqual( + await db + .query('dialog') + .include('id', 'fun') + .filter('fun.it', 'includes', 'italy', { lowerCase: true }) + .get(), [ { id: dialogId, @@ -146,14 +145,13 @@ await test('simple', async (t) => { 'Filter fun.it with text italy', ) - result = await db - .query('dialog') - .locale('en') - .include('id', 'fun') - .filter('fun.it', 'includes', 'italy', { lowerCase: true }) - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .locale('en') + .include('id', 'fun') + .filter('fun.it', 'includes', 'italy', { lowerCase: true }) + .get(), [ { id: 1, @@ -171,9 +169,8 @@ await test('simple', async (t) => { { locale: 'fi' }, ) - result = await db.query('dialog').include('id', 'fun').locale('fi').get() deepEqual( - result.toObject(), + await db.query('dialog').include('id', 'fun').locale('fi').get(), [ { id: dialogId, @@ -196,9 +193,8 @@ await test('simple', async (t) => { { locale: 'fi' }, ) - result = await db.query('dialog').include('id', 'fun').locale('fi').get() deepEqual( - result.toObject(), + await db.query('dialog').include('id', 'fun').locale('fi').get(), [ { id: dialogId, @@ -214,9 +210,8 @@ await test('simple', async (t) => { const derpderp = await db.create('dialog', {}) - result = await db.query('dialog', mrSnurfInFinland).get() deepEqual( - result.toObject(), + await db.query('dialog', mrSnurfInFinland).get(), { id: mrSnurfInFinland, fun: { @@ -228,9 +223,8 @@ await test('simple', async (t) => { 'Query mr snurf in finland', ) - result = await db.query('dialog', derpderp).get() deepEqual( - result.toObject(), + await db.query('dialog', derpderp).get(), { id: derpderp, fun: { @@ -242,14 +236,13 @@ await test('simple', async (t) => { 'Query empty dialog', ) - result = await db - .query('dialog') - .locale('fi') - .include('id', 'fun') - .filter('fun', '=', '3', { lowerCase: true }) - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .locale('fi') + .include('id', 'fun') + .filter('fun', '=', '3', { lowerCase: true }) + .get(), [ { id: dialogId, @@ -259,14 +252,13 @@ await test('simple', async (t) => { 'Exact match on fi', ) - result = await db - .query('dialog') - .locale('fi') - .include('id', 'fun') - .filter('fun', '=', 'mr snurf in finland!', { lowerCase: true }) - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .locale('fi') + .include('id', 'fun') + .filter('fun', '=', 'mr snurf in finland!', { lowerCase: true }) + .get(), [ { id: 2, @@ -280,14 +272,12 @@ await test('simple', async (t) => { fun: { en: 'drink some tea!' }, }) - result = await db - .query('dialog') - .include('fun.en') - .filter('fun', '=', 'mr snurf in finland!', { lowerCase: true }) - .get() - deepEqual( - result.toObject(), + await db + .query('dialog') + .include('fun.en') + .filter('fun', '=', 'mr snurf in finland!', { lowerCase: true }) + .get(), [ { id: 2, @@ -331,13 +321,12 @@ await test('search', async (t) => { await db.drain() - let result = await db - .query('dialog') - .include('id', 'fun') - .search('finland', 'fun') - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .include('id', 'fun') + .search('finland', 'fun') + .get(), [ { id: 1, @@ -351,13 +340,12 @@ await test('search', async (t) => { 'Search for finland', ) - result = await db - .query('dialog') - .include('id', 'fun') - .search('kingdom', 'fun') - .get() deepEqual( - result.toObject(), + await db + .query('dialog') + .include('id', 'fun') + .search('kingdom', 'fun') + .get(), [ { id: 1, @@ -371,14 +359,8 @@ await test('search', async (t) => { 'Search for kingdom', ) - result = await db - .query('dialog') - .include('id', 'fun') - .search('snurp', 'fun') - .get() - deepEqual( - result.toObject(), + await db.query('dialog').include('id', 'fun').search('snurp', 'fun').get(), [ { id: 2, @@ -392,14 +374,8 @@ await test('search', async (t) => { 'Search for snurp', ) - result = await db - .query('dialog') - .include('id', 'fun') - .search('derp', 'fun') - .get() - deepEqual( - result.toObject(), + await db.query('dialog').include('id', 'fun').search('derp', 'fun').get(), [ { id: 2, @@ -413,24 +389,24 @@ await test('search', async (t) => { 'Search for derp', ) - result = await db - .query('dialog') - .locale('fi') - .include('id', 'fun') - .search('derp', 'fun') - .get() - - deepEqual(result.toObject(), [], 'Search for derp with locale set to fi') - - result = await db - .query('dialog') - .locale('en') - .include('id', 'fun') - .search('derp', 'fun') - .get() + deepEqual( + await db + .query('dialog') + .locale('fi') + .include('id', 'fun') + .search('derp', 'fun') + .get(), + [], + 'Search for derp with locale set to fi', + ) deepEqual( - result.toObject(), + await db + .query('dialog') + .locale('en') + .include('id', 'fun') + .search('derp', 'fun') + .get(), [ { id: 2, @@ -441,14 +417,12 @@ await test('search', async (t) => { 'Search for derp with locale set to en', ) - result = await db - .query('dialog') - .include('id', 'fun') - .search('derp', 'fun.en') - .get() - deepEqual( - result, + await db + .query('dialog') + .include('id', 'fun') + .search('derp', 'fun.en') + .get(), [ { id: 2, @@ -494,7 +468,7 @@ await test('reference text', async (t) => { country: country1, }) - deepEqual(await db.query('country').include('*').get().toObject(), [ + deepEqual(await db.query('country').include('*').get(), [ { id: 1, name: '', @@ -505,23 +479,20 @@ await test('reference text', async (t) => { }, ]) - deepEqual( - await db.query('contestant').include('*', 'country').get().toObject(), - [ - { + deepEqual(await db.query('contestant').include('*', 'country').get(), [ + { + id: 1, + name: 'New contestant', + country: { id: 1, - name: 'New contestant', - country: { - id: 1, - name: '', - votingLegal: { - en: '', - fr: '', - }, + name: '', + votingLegal: { + en: '', + fr: '', }, }, - ], - ) + }, + ]) }) await test('sort', async (t) => { From 1ac942555da520816754b395c02bc1b0978601b2 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 12:10:36 -0300 Subject: [PATCH 197/449] implemeneted AST group by enum --- src/db-query/ast/aggregates.ts | 10 +++++- src/db-query/ast/ast.ts | 1 + test/query-ast/aggregates.ts | 56 +++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index baae861110..a4bda5f366 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -12,6 +12,7 @@ import { type QueryIteratorTypeEnum, type AggFunctionEnum, IntervalInverse, + PropType, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' @@ -22,6 +23,7 @@ import { } from '../../db-client/query/aggregates/types.js' import { readPropDef } from './readSchema.js' import { getTimeZoneOffsetInMinutes } from '../../db-client/query/aggregates/aggregates.js' +import type { Enum } from 'valibot' type Sizes = { result: number; accumulator: number } @@ -258,6 +260,12 @@ const pushGroupBy = ( timezone: timeZoneOffset, }) + let enumProxy + if (propDef.type === PropType.enum) { + // @ts-ignore + enumProxy = Object.values(propDef.enum) + } + ctx.query.data.set(buffer, ctx.query.length) ctx.query.length += GroupByKeyPropByteSize @@ -267,7 +275,7 @@ const pushGroupBy = ( stepRange, ...(stepType !== 0 && { stepType: IntervalInverse[stepType] }), ...(display !== undefined && { display }), - // enum: [], // TODO: review enum engine + ...(enumProxy !== undefined && { enum: enumProxy }), } } diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 749e8090f2..89d82b64d0 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -81,6 +81,7 @@ export type QueryAst = { step?: number | IntervalString timeZone?: string display?: Intl.DateTimeFormat + enum?: string[] } } diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index 828ccf6a3d..e904007a8c 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -90,6 +90,8 @@ await test('group by', async (t) => { await db.start({ clean: true }) t.after(() => db.stop()) + const tripClass = ['Cupper', 'Silver', 'Gold'] + const client = await db.setSchema({ types: { trip: { @@ -104,6 +106,7 @@ await test('group by', async (t) => { vendorIdint32: 'int32', vendorIdnumber: 'number', vendorName: 'string', + class: tripClass, }, }, }) @@ -120,6 +123,7 @@ await test('group by', async (t) => { pickup: new Date('2024-12-11T11:00-03:00'), dropoff: new Date('2024-12-11T11:10-03:00'), distance: 513.44, + class: 'Cupper', }) db.create('trip', { @@ -134,17 +138,19 @@ await test('group by', async (t) => { pickup: new Date('2024-12-11T13:00-03:00'), dropoff: new Date('2024-12-11T13:30-03:00'), distance: 100.1, + class: 'Gold', }) await db.drain() - // --------------- Group By string key --------------- // let ast: any let ctx: any let result: any let readSchemaBuf: any let obj: any + // --------------- Group By string key --------------- // + ast = { type: 'trip', sum: { @@ -261,19 +267,41 @@ await test('group by', async (t) => { // --------------- Group By enum keys --------------- // - console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + ast = { + type: 'trip', + sum: { + props: ['distance'], + }, + count: {}, + groupBy: { + prop: 'class', + }, + } as QueryAst + ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + result = await db.server.getQueryBuf(ctx.query) - const r = await db - .query('trip') - // .count() - .sum('distance') - .groupBy('pickup', { - timeZone: 'America/Sao_Paulo', - display: dtFormat, - step: 2.5 * 60 * 60, // 2:30h = 2.5 * 3600s - }) - .get() + readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + + obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - r.debug() - console.dir(r.toObject(), { depth: 10 }) + deepEqual( + obj, + { + Cupper: { count: 1, distance: { sum: 513.44 } }, + Gold: { count: 1, distance: { sum: 100.1 } }, + }, + 'Group By enum keys', + ) + + // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') + + // const r = await db + // .query('trip') + // // .count() + // .sum('distance') + // .groupBy('class', {}) + // .get() + + // r.debug() + // console.dir(r.toObject(), { depth: 10 }) }) From 43e6040892be91a6e901dec4e5dde4ef6f255754 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 12:12:29 -0300 Subject: [PATCH 198/449] removing unused imports --- src/db-query/ast/aggregates.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index a4bda5f366..f920c1a61f 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -10,7 +10,6 @@ import { GroupByKeyPropByteSize, AggPropByteSize, type QueryIteratorTypeEnum, - type AggFunctionEnum, IntervalInverse, PropType, } from '../../zigTsExports.js' @@ -23,7 +22,6 @@ import { } from '../../db-client/query/aggregates/types.js' import { readPropDef } from './readSchema.js' import { getTimeZoneOffsetInMinutes } from '../../db-client/query/aggregates/aggregates.js' -import type { Enum } from 'valibot' type Sizes = { result: number; accumulator: number } From 84e1fbcc86a87c72d9ca9173c7f2e504e6a2cb12 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 16:53:20 +0100 Subject: [PATCH 199/449] Read funcs should take buf as const --- native/utils.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/native/utils.zig b/native/utils.zig index 43147f9c6d..1b9f535538 100644 --- a/native/utils.zig +++ b/native/utils.zig @@ -89,10 +89,10 @@ pub inline fn toSlice(comptime T: type, value: []u8) []T { pub inline fn readPtr( comptime T: type, - buffer: []u8, + buffer: []const u8, offset: usize, ) *T { - return @as(*T, @ptrCast(@alignCast(buffer.ptr + offset))); + return @as(*T, @constCast(@ptrCast(@alignCast(buffer.ptr + offset)))); } pub inline fn read( @@ -127,7 +127,7 @@ pub inline fn read( pub fn ReadIterator(comptime T: type) type { return struct { offset: *usize, - buffer: []u8, + buffer: []const u8, len: usize, pub fn next(self: *ReadIterator(T)) ?T { if (self.offset.* < self.len) { @@ -140,7 +140,7 @@ pub fn ReadIterator(comptime T: type) type { }; } -pub inline fn readIterator(T: type, buffer: []u8, amount: usize, offset: *usize) ReadIterator(T) { +pub inline fn readIterator(T: type, buffer: []const u8, amount: usize, offset: *usize) ReadIterator(T) { return ReadIterator(T){ .buffer = buffer, .len = amount * sizeOf(T) + offset.*, @@ -148,7 +148,7 @@ pub inline fn readIterator(T: type, buffer: []u8, amount: usize, offset: *usize) }; } -pub inline fn readNext(T: type, buffer: []u8, offset: *usize) T { +pub inline fn readNext(T: type, buffer: []const u8, offset: *usize) T { const val = read(T, buffer, offset.*); offset.* = offset.* + @bitSizeOf(T) / 8; return val; From 0967ac19c9ac3a99752cf3a8519519ecf1bf85af Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 17:05:40 +0100 Subject: [PATCH 200/449] Node type is uint16 --- native/modify/modify.zig | 2 +- native/types.zig | 8 +-- src/zigTsExports.ts | 120 +++++++++++++++++++-------------------- 3 files changed, 65 insertions(+), 65 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index c07057b7c4..44b3bc058e 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -235,7 +235,7 @@ const UpsertResult = struct { created: bool, }; -inline fn upsertTarget(db: *DbCtx, typeId: u8, typeEntry: Node.Type, data: []u8) !UpsertResult { +inline fn upsertTarget(db: *DbCtx, typeId: u16, typeEntry: Node.Type, data: []u8) !UpsertResult { var j: usize = 0; while (j < data.len) { const prop = utils.readNext(t.ModifyPropHeader, data, &j); diff --git a/native/types.zig b/native/types.zig index fe57e598d7..a2a9a9b6ac 100644 --- a/native/types.zig +++ b/native/types.zig @@ -95,7 +95,7 @@ pub const ModifyHeader = packed struct { pub const ModifyUpdateHeader = packed struct { op: Modify, - type: u8, + type: TypeId, isTmp: bool, _padding: u7, id: u32, @@ -104,7 +104,7 @@ pub const ModifyUpdateHeader = packed struct { pub const ModifyDeleteHeader = packed struct { op: Modify, - type: u8, + type: TypeId, isTmp: bool, _padding: u7, id: u32, @@ -112,13 +112,13 @@ pub const ModifyDeleteHeader = packed struct { pub const ModifyCreateHeader = packed struct { op: Modify, - type: u8, + type: TypeId, size: u32, }; pub const ModifyCreateRingHeader = packed struct { op: Modify, - type: u8, + type: TypeId, maxNodeId: u32, size: u32, }; diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 76156e4515..5ce28ddd85 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -306,13 +306,13 @@ export const pushModifyHeader = ( export type ModifyUpdateHeader = { op: ModifyEnum - type: number + type: TypeId isTmp: boolean id: number size: number } -export const ModifyUpdateHeaderByteSize = 11 +export const ModifyUpdateHeaderByteSize = 12 export const ModifyUpdateHeaderAlignOf = 16 @@ -323,8 +323,8 @@ export const writeModifyUpdateHeader = ( ): number => { buf[offset] = Number(header.op) offset += 1 - buf[offset] = Number(header.type) - offset += 1 + writeUint16(buf, Number(header.type), offset) + offset += 2 buf[offset] = 0 buf[offset] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 buf[offset] |= ((0 >>> 0) & 127) << 1 @@ -340,17 +340,17 @@ export const writeModifyUpdateHeaderProps = { op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { buf[offset] = Number(value) }, - type: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 1] = Number(value) + type: (buf: Uint8Array, value: TypeId, offset: number) => { + writeUint16(buf, Number(value), offset + 1) }, isTmp: (buf: Uint8Array, value: boolean, offset: number) => { - buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + buf[offset + 3] |= (((value ? 1 : 0) >>> 0) & 1) << 0 }, id: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 3) + writeUint32(buf, Number(value), offset + 4) }, size: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 7) + writeUint32(buf, Number(value), offset + 8) }, } @@ -360,20 +360,20 @@ export const readModifyUpdateHeader = ( ): ModifyUpdateHeader => { const value: ModifyUpdateHeader = { op: (buf[offset]) as ModifyEnum, - type: buf[offset + 1], - isTmp: (((buf[offset + 2] >>> 0) & 1)) === 1, - id: readUint32(buf, offset + 3), - size: readUint32(buf, offset + 7), + type: (readUint16(buf, offset + 1)) as TypeId, + isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, + id: readUint32(buf, offset + 4), + size: readUint32(buf, offset + 8), } return value } export const readModifyUpdateHeaderProps = { op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 0) & 1)) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { @@ -388,7 +388,7 @@ export const pushModifyUpdateHeader = ( ): number => { const index = buf.length buf.pushUint8(Number(header.op)) - buf.pushUint8(Number(header.type)) + buf.pushUint16(Number(header.type)) buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 @@ -399,12 +399,12 @@ export const pushModifyUpdateHeader = ( export type ModifyDeleteHeader = { op: ModifyEnum - type: number + type: TypeId isTmp: boolean id: number } -export const ModifyDeleteHeaderByteSize = 7 +export const ModifyDeleteHeaderByteSize = 8 export const ModifyDeleteHeaderAlignOf = 8 @@ -415,8 +415,8 @@ export const writeModifyDeleteHeader = ( ): number => { buf[offset] = Number(header.op) offset += 1 - buf[offset] = Number(header.type) - offset += 1 + writeUint16(buf, Number(header.type), offset) + offset += 2 buf[offset] = 0 buf[offset] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 buf[offset] |= ((0 >>> 0) & 127) << 1 @@ -430,14 +430,14 @@ export const writeModifyDeleteHeaderProps = { op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { buf[offset] = Number(value) }, - type: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 1] = Number(value) + type: (buf: Uint8Array, value: TypeId, offset: number) => { + writeUint16(buf, Number(value), offset + 1) }, isTmp: (buf: Uint8Array, value: boolean, offset: number) => { - buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + buf[offset + 3] |= (((value ? 1 : 0) >>> 0) & 1) << 0 }, id: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 3) + writeUint32(buf, Number(value), offset + 4) }, } @@ -447,18 +447,18 @@ export const readModifyDeleteHeader = ( ): ModifyDeleteHeader => { const value: ModifyDeleteHeader = { op: (buf[offset]) as ModifyEnum, - type: buf[offset + 1], - isTmp: (((buf[offset + 2] >>> 0) & 1)) === 1, - id: readUint32(buf, offset + 3), + type: (readUint16(buf, offset + 1)) as TypeId, + isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, + id: readUint32(buf, offset + 4), } return value } export const readModifyDeleteHeaderProps = { op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 0) & 1)) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), } export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { @@ -473,7 +473,7 @@ export const pushModifyDeleteHeader = ( ): number => { const index = buf.length buf.pushUint8(Number(header.op)) - buf.pushUint8(Number(header.type)) + buf.pushUint16(Number(header.type)) buf.pushUint8(0) buf.view[buf.length - 1] |= (((header.isTmp ? 1 : 0) >>> 0) & 1) << 0 buf.view[buf.length - 1] |= ((0 >>> 0) & 127) << 1 @@ -483,11 +483,11 @@ export const pushModifyDeleteHeader = ( export type ModifyCreateHeader = { op: ModifyEnum - type: number + type: TypeId size: number } -export const ModifyCreateHeaderByteSize = 6 +export const ModifyCreateHeaderByteSize = 7 export const ModifyCreateHeaderAlignOf = 8 @@ -498,8 +498,8 @@ export const writeModifyCreateHeader = ( ): number => { buf[offset] = Number(header.op) offset += 1 - buf[offset] = Number(header.type) - offset += 1 + writeUint16(buf, Number(header.type), offset) + offset += 2 writeUint32(buf, Number(header.size), offset) offset += 4 return offset @@ -509,11 +509,11 @@ export const writeModifyCreateHeaderProps = { op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { buf[offset] = Number(value) }, - type: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 1] = Number(value) + type: (buf: Uint8Array, value: TypeId, offset: number) => { + writeUint16(buf, Number(value), offset + 1) }, size: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 2) + writeUint32(buf, Number(value), offset + 3) }, } @@ -523,16 +523,16 @@ export const readModifyCreateHeader = ( ): ModifyCreateHeader => { const value: ModifyCreateHeader = { op: (buf[offset]) as ModifyEnum, - type: buf[offset + 1], - size: readUint32(buf, offset + 2), + type: (readUint16(buf, offset + 1)) as TypeId, + size: readUint32(buf, offset + 3), } return value } export const readModifyCreateHeaderProps = { op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), } export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { @@ -547,19 +547,19 @@ export const pushModifyCreateHeader = ( ): number => { const index = buf.length buf.pushUint8(Number(header.op)) - buf.pushUint8(Number(header.type)) + buf.pushUint16(Number(header.type)) buf.pushUint32(Number(header.size)) return index } export type ModifyCreateRingHeader = { op: ModifyEnum - type: number + type: TypeId maxNodeId: number size: number } -export const ModifyCreateRingHeaderByteSize = 10 +export const ModifyCreateRingHeaderByteSize = 11 export const ModifyCreateRingHeaderAlignOf = 16 @@ -570,8 +570,8 @@ export const writeModifyCreateRingHeader = ( ): number => { buf[offset] = Number(header.op) offset += 1 - buf[offset] = Number(header.type) - offset += 1 + writeUint16(buf, Number(header.type), offset) + offset += 2 writeUint32(buf, Number(header.maxNodeId), offset) offset += 4 writeUint32(buf, Number(header.size), offset) @@ -583,14 +583,14 @@ export const writeModifyCreateRingHeaderProps = { op: (buf: Uint8Array, value: ModifyEnum, offset: number) => { buf[offset] = Number(value) }, - type: (buf: Uint8Array, value: number, offset: number) => { - buf[offset + 1] = Number(value) + type: (buf: Uint8Array, value: TypeId, offset: number) => { + writeUint16(buf, Number(value), offset + 1) }, maxNodeId: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 2) + writeUint32(buf, Number(value), offset + 3) }, size: (buf: Uint8Array, value: number, offset: number) => { - writeUint32(buf, Number(value), offset + 6) + writeUint32(buf, Number(value), offset + 7) }, } @@ -600,18 +600,18 @@ export const readModifyCreateRingHeader = ( ): ModifyCreateRingHeader => { const value: ModifyCreateRingHeader = { op: (buf[offset]) as ModifyEnum, - type: buf[offset + 1], - maxNodeId: readUint32(buf, offset + 2), - size: readUint32(buf, offset + 6), + type: (readUint16(buf, offset + 1)) as TypeId, + maxNodeId: readUint32(buf, offset + 3), + size: readUint32(buf, offset + 7), } return value } export const readModifyCreateRingHeaderProps = { op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => buf[offset + 1], - maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } export const createModifyCreateRingHeader = (header: ModifyCreateRingHeader): Uint8Array => { @@ -626,7 +626,7 @@ export const pushModifyCreateRingHeader = ( ): number => { const index = buf.length buf.pushUint8(Number(header.op)) - buf.pushUint8(Number(header.type)) + buf.pushUint16(Number(header.type)) buf.pushUint32(Number(header.maxNodeId)) buf.pushUint32(Number(header.size)) return index From a8391f5d31e893fa207639a117487a409eeda42e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 17:07:33 +0100 Subject: [PATCH 201/449] Query comp funcs can treat field as const --- native/query/filter/compare.zig | 16 ++++++++-------- native/query/filter/filter.zig | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/native/query/filter/compare.zig b/native/query/filter/compare.zig index ea481735f5..e2f9508622 100644 --- a/native/query/filter/compare.zig +++ b/native/query/filter/compare.zig @@ -17,7 +17,7 @@ pub const Function = enum(u8) { eqBatchSmall, }; -pub fn eqBatch(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn eqBatch(T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const size = utils.sizeOf(T); const vectorLen = 16 / size; const value = utils.readPtr(T, v, c.start).*; @@ -33,7 +33,7 @@ pub fn eqBatch(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool return false; } -pub fn eqBatchSmall(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn eqBatchSmall(T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const size = utils.sizeOf(T); const vectorLen = 16 / size; const value = utils.readPtr(T, v, c.start).*; @@ -42,37 +42,37 @@ pub fn eqBatchSmall(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) return (std.simd.countElementsWithValue(vec, value) != 0); } -pub fn eq(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn eq(comptime T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const val = utils.readPtr(T, v, c.start).*; const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; return val == target; } -pub fn lt(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn lt(comptime T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const val = utils.readPtr(T, v, c.start).*; const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; return val < target; } -pub fn gt(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn gt(comptime T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const val = utils.readPtr(T, v, c.start).*; const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; return val > target; } -pub fn le(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn le(comptime T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const val = utils.readPtr(T, v, c.start).*; const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; return val <= target; } -pub fn ge(comptime T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn ge(comptime T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const val = utils.readPtr(T, v, c.start).*; const target = utils.readPtr(T, q, i + @alignOf(T) - c.offset).*; return val >= target; } -pub fn range(T: type, q: []u8, v: []u8, i: usize, c: *t.FilterCondition) bool { +pub fn range(T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { const size = utils.sizeOf(T); if (T == f64) { // Floats do not support ignore overflow diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 29d4b48ddc..6362541ee9 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -82,7 +82,7 @@ inline fn compare( T: type, comptime meta: Instruction.OpMeta, q: []u8, - v: []u8, + v: []const u8, index: usize, c: *t.FilterCondition, ) bool { @@ -103,7 +103,7 @@ inline fn compare( pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { var i: usize = 0; var pass: bool = true; - var v: []u8 = undefined; + var v: []const u8 = undefined; var prop: u8 = 255; var nextOrIndex: usize = q.len; while (i < nextOrIndex) { From 6dea17ada99e4478fa80980f9b8b69060ce761a9 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 17:08:00 +0100 Subject: [PATCH 202/449] Add getAliasByNode() --- native/selva/fields.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/native/selva/fields.zig b/native/selva/fields.zig index d4293e2be6..b5f708a36a 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -308,3 +308,14 @@ pub fn getAliasByName(typeEntry: Node.Type, field: u8, aliasName: []u8) ?Node.No // TODO Partials return res.node; } + +pub fn getAliasByNode(typeEntry: Node.Type, node: Node.Node, field: u8) ![]const u8 { + if (selva.c.selva_get_aliases(typeEntry, field)) |aliases| { + if (selva.c.selva_get_alias_by_dest(aliases, Node.getNodeId(node))) |alias| { + var len: usize = undefined; + const name = selva.c.selva_get_alias_name(alias, &len); + return name[0..len]; + } + } + return errors.SelvaError.SELVA_ENOENT; +} From 8a84646e6377649b940b8fb384d18304cd2594ad Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 17:08:28 +0100 Subject: [PATCH 203/449] getType() should also accept a Node --- native/selva/node.zig | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/native/selva/node.zig b/native/selva/node.zig index 9062ed688f..3143841b79 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -11,15 +11,28 @@ const DbCtx = @import("../db/ctx.zig").DbCtx; pub const Type = selva.Type; pub const Node = selva.Node; -pub inline fn getType(ctx: *DbCtx, typeId: t.TypeId) !Type { - const selvaTypeEntry: ?Type = selva.c.selva_get_type_by_index( - ctx.selva.?, - typeId, - ); - if (selvaTypeEntry == null) { - return errors.SelvaError.SELVA_EINTYPE; +pub inline fn getType(ctx: *DbCtx, v: anytype) !Type { + var selvaTypeEntry: ?Type = undefined; + + if (comptime @TypeOf(v) == t.TypeId) { + selvaTypeEntry = selva.c.selva_get_type_by_index( + ctx.selva.?, + v, + ); + } else if (comptime @TypeOf(v) == selva.Node or + @TypeOf(v) == ?selva.Node) { + if (comptime @TypeOf(v) == ?selva.Node) { + if (v == null) { + return errors.SelvaError.SELVA_ENOENT; + } + } + selvaTypeEntry = selva.c.selva_get_type_by_node(ctx.selva.?, v); + } else { + @compileLog("Invalid type: ", @TypeOf(v)); + @compileError("Invalid type"); } - return selvaTypeEntry.?; + + return if (selvaTypeEntry == null) errors.SelvaError.SELVA_EINTYPE else selvaTypeEntry.?; } pub inline fn getRefDstType(ctx: *DbCtx, sch: anytype) !Type { From 19354737825b4576350d06ae1b051e57c483a714 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 17:09:02 +0100 Subject: [PATCH 204/449] Fix filter by alias --- native/query/filter/filter.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 6362541ee9..a84477892c 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -4,6 +4,7 @@ const utils = @import("../../utils.zig"); const Node = @import("../../selva/node.zig"); const Schema = @import("../../selva/schema.zig"); const Fields = @import("../../selva/fields.zig"); +const Selva = @import("../../selva/selva.zig"); const t = @import("../../types.zig"); const Compare = @import("compare.zig"); const Select = @import("select.zig"); @@ -113,7 +114,11 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { if (prop != c.prop) { prop = c.prop; - v = Fields.getRaw(node, c.fieldSchema); + if (c.fieldSchema.type == Selva.c.SELVA_FIELD_TYPE_ALIAS) { + v = try Fields.getAliasByNode(try Node.getType(ctx.db, node), node, c.fieldSchema.field); + } else { + v = Fields.getRaw(node, c.fieldSchema); + } } pass = switch (c.op.compare) { From 63dc36de67654ab053f438786474c852ab5edbc7 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 9 Feb 2026 17:09:13 +0100 Subject: [PATCH 205/449] Cleanup the alias test --- test/alias/alias.ts | 79 +++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 50 deletions(-) diff --git a/test/alias/alias.ts b/test/alias/alias.ts index 46e7505455..a140037fc9 100644 --- a/test/alias/alias.ts +++ b/test/alias/alias.ts @@ -40,27 +40,22 @@ await test('simple', async (t) => { 'One alias', ) - deepEqual((await db.query('user', user2).get()).toObject(), { + deepEqual(await db.query('user', user2).get(), { id: 2, externalId: 'cool2', potato: '', }) - deepEqual( - (await db.query('user').filter('externalId', '=', 'cool').get()).toObject(), - [ - { - id: 1, - externalId: 'cool', - potato: '', - }, - ], - ) + deepEqual(await db.query('user').filter('externalId', '=', 'cool').get(), [ + { + id: 1, + externalId: 'cool', + potato: '', + }, + ]) deepEqual( - ( - await db.query('user').filter('externalId', 'includes', 'cool').get() - ).toObject(), + await db.query('user').filter('externalId', 'includes', 'cool').get(), [ { id: 1, @@ -79,7 +74,7 @@ await test('simple', async (t) => { potato: 'success', }) - deepEqual((await db.query('user', res1).get()).toObject(), { + deepEqual(await db.query('user', res1).get(), { id: 3, externalId: 'potato', potato: 'success', @@ -88,31 +83,25 @@ await test('simple', async (t) => { externalId: 'potato', potato: 'wrong', }) - deepEqual((await db.query('user', res2).get()).toObject(), { + deepEqual(await db.query('user', res2).get(), { id: 3, externalId: 'potato', potato: 'wrong', }) deepEqual( - ( - await db.query('user', { externalId: 'i-dont-exists-haha!' }).get() - ).toObject(), + await db.query('user', { externalId: 'i-dont-exists-haha!' }).get(), null, 'Get non existing alias', ) - deepEqual( - (await db.query('user', 123).get()).toObject(), - null, - 'Get non existing id', - ) + deepEqual(await db.query('user', 123).get(), null, 'Get non existing id') await db.create('user', { potato: 'power', externalId: 'cool', }) - deepEqual(await db.query('user').get().toObject(), [ + deepEqual(await db.query('user').get(), [ { id: 1, externalId: '', potato: '' }, { id: 2, externalId: 'cool2', potato: '' }, { id: 3, externalId: 'potato', potato: 'wrong' }, @@ -159,7 +148,7 @@ await test('alias - references', async (t) => { }) deepEqual( - await db.query('user').include('email', 'friends').get().toObject(), + await db.query('user').include('email', 'friends').get(), [ { id: 1, @@ -185,7 +174,7 @@ await test('alias - references', async (t) => { }) deepEqual( - await db.query('user').include('friends', 'email').get().toObject(), + await db.query('user').include('friends', 'email').get(), [ { id: 1, @@ -205,8 +194,7 @@ await test('alias - references', async (t) => { await db .query('user') .filter('email', 'includes', '2', { lowerCase: true }) - .get() - .toObject(), + .get(), [{ id: 2, name: '2', email: '2@saulx.com' }], 'update 2', ) @@ -241,8 +229,7 @@ await test('Get single node by alias', async (t) => { .query('user', { email: '2@saulx.com', }) - .get() - .toObject(), + .get(), { id: 1, name: '2', @@ -312,8 +299,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -335,8 +321,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -359,8 +344,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -414,7 +398,7 @@ await test('same-name-alias', async (t) => { await db.drain() - deepEqual(await db.query('round').get().toObject(), [ + deepEqual(await db.query('round').get(), [ { id: 1, name: 'semi1' }, { id: 2, name: 'semi2' }, { id: 3, name: 'final' }, @@ -455,7 +439,7 @@ await test('nested alias', async (t) => { }, }) - deepEqual(await db.query('thing').get().toObject(), [ + deepEqual(await db.query('thing').get(), [ { id: 1, obj: { a: 'jibber', b: '' } }, { id: 2, obj: { b: 'flurp', a: '' } }, ]) @@ -522,8 +506,7 @@ await test('Get single node by alias', async (t) => { .query('user', { email: '2@saulx.com', }) - .get() - .toObject(), + .get(), { id: 1, name: '2', @@ -577,8 +560,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -601,8 +583,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -624,8 +605,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -648,8 +628,7 @@ await test('Update existing alias field', async (t) => { .query('user', { email, }) - .get() - .toObject(), + .get(), { id: 1, name: 'nuno', @@ -700,7 +679,7 @@ await test('same-name-alias', async (t) => { await db.drain() - deepEqual(await db.query('round').get().toObject(), [ + deepEqual(await db.query('round').get(), [ { id: 1, name: 'semi1' }, { id: 2, name: 'semi2' }, { id: 3, name: 'final' }, From 3888caf46cfe5d41d0ba824efa98b9fc3b858801 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 13:28:02 -0300 Subject: [PATCH 206/449] cleaning --- native/query/aggregates/aggregates.zig | 6 +++--- native/query/multiple.zig | 2 +- native/query/query.zig | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 06677bde8a..6ddb87d507 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -52,8 +52,8 @@ pub inline fn aggregateProps( var i: usize = 0; while (i < aggDefs.len) { const currentAggDef = utils.readNext(t.AggProp, aggDefs, &i); - utils.debugPrint("currentAggDef: {any}\n", .{currentAggDef}); - utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); + // utils.debugPrint("currentAggDef: {any}\n", .{currentAggDef}); + // utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); var value: []u8 = undefined; if (currentAggDef.aggFunction == t.AggFunction.count) { accumulate(currentAggDef, accumulatorProp, value, hadAccumulated, null, null); @@ -112,7 +112,7 @@ pub inline fn accumulate( switch (aggFunction) { .sum => { writeAs(f64, accumulatorProp, read(f64, accumulatorProp, accumulatorPos) + microbufferToF64(propTypeTag, value, start), accumulatorPos); - utils.debugPrint("❤️ v: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); + // utils.debugPrint("❤️ v: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); }, .avg => { const val = microbufferToF64(propTypeTag, value, start); diff --git a/native/query/multiple.zig b/native/query/multiple.zig index c288d11644..670a464968 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -33,7 +33,7 @@ fn iterator( filter = utils.sliceNext(header.filterSize, q, i); try Filter.prepare(filter, ctx, typeEntry); } - utils.debugPrint("i.* .. i.* + header.includeSize: {d} .. {d}\n", .{ i.*, i.* + header.includeSize }); + // utils.debugPrint("i.* .. i.* + header.includeSize: {d} .. {d}\n", .{ i.*, i.* + header.includeSize }); const nestedQuery = q[i.* .. i.* + header.includeSize]; while (offset > 0) { const node = it.next() orelse return 0; diff --git a/native/query/query.zig b/native/query/query.zig index 2e38c1f381..93427ea06a 100644 --- a/native/query/query.zig +++ b/native/query/query.zig @@ -41,7 +41,7 @@ pub fn getQueryThreaded( const queryId = utils.readNext(u32, buffer, &index); const q = buffer[index .. buffer.len - 8]; // - checksum len - utils.debugPrint("q: {any}\n", .{q}); + // utils.debugPrint("q: {any}\n", .{q}); const op = utils.read(t.OpType, q, 0); _ = try thread.query.result(0, queryId, op); From c875a2884d14f0e2cb54e47cba08354349d55a4f Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 13:28:12 -0300 Subject: [PATCH 207/449] fix variance typo --- src/db-query/ast/ast.ts | 2 +- test/query-ast/aggregates.ts | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 89d82b64d0..8ccdfc75da 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -75,7 +75,7 @@ export type QueryAst = { max?: { props: string[] } min?: { props: string[] } stddev?: { props: string[]; samplingMode?: 'sample' | 'population' } - var?: { props: string[]; samplingMode?: 'sample' | 'population' } + variance?: { props: string[]; samplingMode?: 'sample' | 'population' } groupBy?: { prop: string step?: number | IntervalString diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index e904007a8c..fe47af4822 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -48,7 +48,7 @@ await test('basic', async (t) => { props: ['age'], samplingMode: 'population', }, - var: { + variance: { props: ['age'], }, // count: { props: 'age' }, // not implementd yet @@ -56,11 +56,12 @@ await test('basic', async (t) => { } const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) const result = await db.server.getQueryBuf(ctx.query) - debugBuffer(result) + // debugBuffer(result) const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) + // console.dir(obj, { depth: 10 }) deepEqual( obj, @@ -72,15 +73,7 @@ await test('basic', async (t) => { 'basic accum, no groupby, no refs', ) - // console.dir(obj, { depth: 10 }) - - console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) - - // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') - - // const r = await db.query('user').count().sum('age').get() - // r.debug() - // r.inspect(10) + // console.log(JSON.stringify(obj), readSchemaBuf.byteLength, result.byteLength) }) await test('group by', async (t) => { From 765a978f3beb866e1ab4a9de7e7f3bfaf2c96fad Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 14:35:13 -0300 Subject: [PATCH 208/449] remaining variance typo --- src/db-query/ast/aggregates.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index f920c1a61f..b548063165 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -69,7 +69,7 @@ const isRootCountOnly = (ast: QueryAst) => { !ast.min && !ast.max && !ast.stddev && - !ast.var && + !ast.variance && !ast.harmonicMean && !ast.cardinality ) @@ -212,7 +212,7 @@ export const isAggregateAst = (ast: QueryAst) => { ast.min || ast.max || ast.stddev || - ast.var || + ast.variance || ast.harmonicMean || ast.cardinality ) From dec794219aada96affdc13bab225047a963367c6 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 9 Feb 2026 18:02:21 -0300 Subject: [PATCH 209/449] fixed agg reference result size --- native/query/aggregates/aggregates.zig | 2 -- native/query/aggregates/references.zig | 6 ++---- src/db-client/query/aggregates/toByteCode.ts | 1 - test/aggregate/dev.ts | 14 ++++++++++++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 6ddb87d507..7f1c8b4419 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -126,13 +126,11 @@ pub inline fn accumulate( writeAs(f64, accumulatorProp, sum, accumulatorPos + 8); }, .min => { - // utils.debugPrint("hadAccumulated: {any} {d} {d}\n", .{ hadAccumulated.*, accumulatorPos, microbufferToF64(propTypeTag, value, start) }); if (!hadAccumulated.*) { writeAs(f64, accumulatorProp, microbufferToF64(propTypeTag, value, start), accumulatorPos); } else { writeAs(f64, accumulatorProp, @min(read(f64, accumulatorProp, accumulatorPos), microbufferToF64(propTypeTag, value, start)), accumulatorPos); } - // utils.debugPrint("ficou: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); }, .max => { if (!hadAccumulated.*) { diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index 511e6d199b..c53f2d8252 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -20,8 +20,7 @@ pub inline fn aggregateRefsProps( ) !void { utils.debugPrint("i: {d}\n", .{i.*}); const header = utils.readNext(t.AggRefsHeader, q, i); - // i.* += utils.sizeOf(t.AggRefsHeader); - utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); + // utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); const accumulatorProp = try ctx.db.allocator.alloc(u8, header.accumulatorSize); @memset(accumulatorProp, 0); @@ -29,10 +28,9 @@ pub inline fn aggregateRefsProps( var it = try References.iterator(false, false, ctx.db, from, header.targetProp, fromType); _ = try Aggregates.iterator(ctx, &it, 1000, false, undefined, q[i.*..], accumulatorProp, it.dstType, undefined); // TODO: hllAcc - // should be wrong because changes with each aggFunc and targetProp try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); - try ctx.thread.query.append(@as(u32, @sizeOf(f64))); // falty count and cardinalty is u32 + try ctx.thread.query.append(@as(u32, header.resultsSize)); try Aggregates.finalizeResults(ctx, q[i.*..], accumulatorProp, header.isSamplingSet, 0); } diff --git a/src/db-client/query/aggregates/toByteCode.ts b/src/db-client/query/aggregates/toByteCode.ts index 50c8babbc8..0050d23802 100644 --- a/src/db-client/query/aggregates/toByteCode.ts +++ b/src/db-client/query/aggregates/toByteCode.ts @@ -46,7 +46,6 @@ export const aggregateToBuffer = (def: QueryDef): IntermediateByteCode => { accumulatorSize: def.aggregate.totalAccumulatorSize, isSamplingSet: (def.aggregate?.option?.mode || 'sample') === 'sample', } - let headerBuffer: Uint8Array if (def.type == QueryDefType.References) { diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index c5b5aabfe3..67ffec15bb 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -77,7 +77,8 @@ await test('references', async (t) => { }, trip: { props: { - distance: 'uint16', + distance: 'number', + rate: 'uint8', driver: { ref: 'driver', prop: 'trips', // Points back to the list on driver @@ -93,10 +94,12 @@ await test('references', async (t) => { db.drain() const t1 = db.create('trip', { distance: 523.1, // with uint16 => 523 + rate: 4, driver: d1, }) const t2 = db.create('trip', { distance: 1230, + rate: 2, driver: d1, }) @@ -110,7 +113,14 @@ await test('references', async (t) => { const lala = await db .query('driver') - .include((t) => t('trips').sum('distance').max('distance').avg('distance')) + .include((t) => + t('trips') + .sum('distance') + .avg('distance') + .min('rate') + .sum('rate') + .count(), + ) .get() // console.log(lala.toObject()) From 4fe6b559d7cf7061b307266a84a5752af10428a3 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Tue, 10 Feb 2026 09:42:51 +0100 Subject: [PATCH 210/449] extra tests --- test/query-ast/include.ts | 34 ++++-- test/query-ast/validate.perf.ts | 190 ++++++++++++++++++++++---------- 2 files changed, 160 insertions(+), 64 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 3dc25d180c..431260be19 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -92,12 +92,7 @@ await test('include', async (t) => { // add edges }, // add AND - or: { - // 4 - props: { - y: { ops: [{ op: '=', val: 67 }] }, - }, - }, + and: { // 2 props: { @@ -108,9 +103,31 @@ await test('include', async (t) => { props: { y: { ops: [{ op: '=', val: 3 }] }, }, + // 4 + or: { + props: { + y: { ops: [{ op: '=', val: 4 }] }, + }, + }, }, }, - // ->4 1 ->3 2 ->4 3 + + or: { + // 5 + props: { + y: { ops: [{ op: '=', val: 67 }] }, + }, + }, + + // (y=0 & ((y=10&x=100) | y=3 | y=4)) | y=67 + // 1:y=0 + // 2:y=10&x=100 + // 3:y=3 + // 4:y=4 + // 5:y=67 + // >5 1 >3 2 >4 3 >5 67 + + // > means this is the next evaluation if it does not pass if any does not pass in the group e.g. (y=10) in 2 then its does not pass and return }, props: { // name: { include: {} }, @@ -125,6 +142,9 @@ await test('include', async (t) => { }, }, }, + + // y=4 + // // EDGE // edges: { diff --git a/test/query-ast/validate.perf.ts b/test/query-ast/validate.perf.ts index f41612fcad..178da5ae1e 100644 --- a/test/query-ast/validate.perf.ts +++ b/test/query-ast/validate.perf.ts @@ -1,14 +1,16 @@ import * as v from 'valibot' import { type } from 'arktype' +import test from '../shared/test.js' // ========================================== // 1. DATA GENERATION // ========================================== const validData = { - name: 'Performance Master', - id: 123456789, - isNice: true, - tags: [100, 200, 300, 400, 500], + captchaToken: 'valid_token_123', + metadata: { + votes: { ddi_1: 100, ddi_2: 200, ddi_3: 300, ddi_4: 400 }, + editionId: 999, + }, } // ========================================== @@ -17,59 +19,113 @@ const validData = { function manualValidator(data: any): boolean { if (!data || typeof data !== 'object') return false - if (typeof data.name !== 'string' || data.name.length > 30) return false - - if (typeof data.id !== 'number' || data.id < 0 || data.id > 4000000000) + if (typeof data.captchaToken !== 'string' && data.captchaToken !== null) return false - if (typeof data.isNice !== 'boolean') return false + const metadata = data.metadata + if (!metadata || typeof metadata !== 'object') return false + + if ( + typeof metadata.editionId !== 'number' || + metadata.editionId < 0 || + metadata.editionId > 4294967295 || + !Number.isInteger(metadata.editionId) + ) + return false - const tags = data.tags - if (!Array.isArray(tags) || tags.length > 10) return false - for (let i = 0; i < tags.length; i++) { - const t = tags[i] - if (typeof t !== 'number' || t < 0 || t > 4000) return false + const votes = metadata.votes + if (!votes || typeof votes !== 'object') return false + + for (const key in votes) { + const val = votes[key] + if ( + typeof val !== 'number' || + val < 0 || + val > 4294967295 || + !Number.isInteger(val) + ) + return false } return true } // ========================================== -// 3. VALIBOT SCHEMA +// 3. SCHEMA FACTORIES // ========================================== -const ValibotSchema = v.object({ - name: v.pipe(v.string(), v.maxLength(30)), - id: v.pipe(v.number(), v.minValue(0), v.maxValue(4000000000)), - isNice: v.boolean(), - tags: v.pipe( - v.array(v.pipe(v.number(), v.minValue(0), v.maxValue(4000))), - v.maxLength(10), - ), -}) -// ========================================== -// 4. ARKTYPE SCHEMA -// ========================================== -const ArkTypeSchema = type({ - name: 'string<=30', - id: '0<=number<=4000000000', - isNice: 'boolean', - tags: '(0<=number<=4000)[] <= 10', -}) +const createValibotSchema = () => { + return v.object({ + captchaToken: v.nullable(v.string()), + metadata: v.object({ + editionId: v.pipe( + v.number(), + v.integer(), + v.minValue(0), + v.maxValue(4294967295), + ), + votes: v.record( + v.string(), + v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(4294967295)), + ), + }), + }) +} + +const createArkTypeSchema = () => { + return type({ + captchaToken: 'string|null', + metadata: { + editionId: '0<=number<=4294967295%1', + votes: { + '[string]': '0<=number<=4294967295%1', + }, + }, + }) +} // ========================================== -// 5. SIMPLE BENCH RUNNER +// 4. BENCH RUNNERS // ========================================== -function runBenchmark( +function measureCreation(name: string, factory: () => any, iterations: number) { + if (global.gc) global.gc() + const startMem = process.memoryUsage().heapUsed + const start = process.hrtime.bigint() + + const schemas: any[] = [] + for (let i = 0; i < iterations; i++) { + schemas.push(factory()) + } + + const end = process.hrtime.bigint() + const endMem = process.memoryUsage().heapUsed + const totalTimeNs = Number(end - start) + const totalMemDiff = endMem - startMem + const memPerOp = totalMemDiff / iterations // bytes + + const opsPerSec = (iterations / totalTimeNs) * 1e9 + const formatter = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }) + + // Keep schemas alive to prevent GC during measurement + // console.log(schemas.length) + + console.log( + `${name.padEnd(10)} | Creation: ${formatter.format( + opsPerSec, + )} ops/sec | Mem: ${memPerOp.toFixed(2)} bytes/inst`, + ) +} + +function runValidationBenchmark( name: string, fn: () => void, iterations: number = 500_000, ) { - process.stdout.write(`Running ${name}... `) + process.stdout.write(`Running validation ${name}... `) // 1. Warmup (Trigger JIT optimization) - for (let i = 0; i < 1000; i++) { + for (let i = 0; i < 1e5; i++) { fn() } @@ -87,27 +143,47 @@ function runBenchmark( const opsPerSec = (iterations / totalTimeMs) * 1000 // Format Output - const formatter = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }) + const formatter = new Intl.NumberFormat('en-US', { + maximumFractionDigits: 0, + }) console.log(`${formatter.format(opsPerSec)} ops/sec (${totalTimeMs}ms)`) } -// ========================================== -// 6. EXECUTION -// ========================================== - -console.log('--- STARTING BENCHMARK ---\n') - -// Sanity Checks -if (!manualValidator(validData)) throw new Error('Manual Failed') -if (!v.safeParse(ValibotSchema, validData).success) - throw new Error('Valibot Failed') -if (ArkTypeSchema(validData) instanceof type.errors) - throw new Error('ArkType Failed') - -const ITERATIONS = 10_000_000 - -runBenchmark('Raw JS ', () => manualValidator(validData), ITERATIONS) -runBenchmark('ArkType ', () => ArkTypeSchema(validData), ITERATIONS) -runBenchmark('Valibot ', () => v.parse(ValibotSchema, validData), ITERATIONS) - -console.log('\n--- DONE ---') +test('bench validator', async () => { + const ValibotSchema = createValibotSchema() + const ArkTypeSchema = createArkTypeSchema() + + console.log('--- CREATION BENCHMARK ---\n') + const CREATION_ITERATIONS = 10_000 + measureCreation('ArkType', createArkTypeSchema, CREATION_ITERATIONS) + measureCreation('Valibot', createValibotSchema, CREATION_ITERATIONS) + + console.log('\n--- VALIDATION BENCHMARK ---\n') + + // Sanity Checks + if (!manualValidator(validData)) throw new Error('Manual Failed') + if (!v.safeParse(ValibotSchema, validData).success) + throw new Error('Valibot Failed') + if (ArkTypeSchema(validData) instanceof type.errors) + throw new Error('ArkType Failed') + + const ITERATIONS = 10_000_000 + + runValidationBenchmark( + 'Raw JS ', + () => manualValidator(validData), + ITERATIONS, + ) + runValidationBenchmark( + 'ArkType ', + () => ArkTypeSchema(validData), + ITERATIONS, + ) + runValidationBenchmark( + 'Valibot ', + () => v.parse(ValibotSchema, validData), + ITERATIONS, + ) + + console.log('\n--- DONE ---') +}) From b74944b58e51cdd2abce12ef510eb07546d8bbd9 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Tue, 10 Feb 2026 10:18:58 +0100 Subject: [PATCH 211/449] fix --- native/query/single.zig | 68 ++++++++++++++++--- test/query-ast/aggregates.ts | 1 - test/query-ast/include.ts | 126 ++++------------------------------- test/query/types.ts | 34 +++++----- 4 files changed, 89 insertions(+), 140 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index 8f825e0825..fb54a714bf 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -98,6 +98,60 @@ pub fn reference( i.* += header.includeSize; } +// pub fn referenceEdge( +// ctx: *Query.QueryCtx, +// q: []u8, +// from: Node.Node, +// fromType: Selva.Type, +// i: *usize, +// ) !void { +// const header = utils.readNext(t.QueryHeaderSingleReference, q, i); +// const fs = try Schema.getFieldSchema(fromType, header.prop); +// if (References.getReference(from, fs)) |ref| { +// const typeEntry = try Node.getType(ctx.db, header.typeId); +// const n = Node.getNode(typeEntry, ref.dst); + +// if (n) |node| { +// try ctx.thread.query.append(t.ReadOp.reference); +// try ctx.thread.query.append(header.prop); +// const resultByteSizeIndex = try ctx.thread.query.reserve(4); +// const startIndex = ctx.thread.query.index; +// try ctx.thread.query.append(ref.dst); +// const nestedQuery = q[i.* .. i.* + header.includeSize]; +// try Include.include(node, ctx, nestedQuery, typeEntry); + +// // handle this case +// // if ref.edge == 0 + +// const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); +// const e = Node.getNode(edgeTypeEntry, ref.edge); + +// if (e) |edge| { +// const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; +// try ctx.thread.query.append(t.ReadOp.edge); +// const edgesByteSizeIndex = try ctx.thread.query.reserve(4); +// const edgeStartIndex = ctx.thread.query.index; +// try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); +// ctx.thread.query.writeAs( +// u32, +// @truncate(ctx.thread.query.index - edgeStartIndex), +// edgesByteSizeIndex, +// ); +// } + +// i.* += header.edgeSize + header.includeSize; + +// ctx.thread.query.writeAs( +// u32, +// @truncate(ctx.thread.query.index - startIndex), +// resultByteSizeIndex, +// ); +// } +// } + +// i.* += header.includeSize; +// } + pub fn referenceEdge( ctx: *Query.QueryCtx, q: []u8, @@ -106,6 +160,7 @@ pub fn referenceEdge( i: *usize, ) !void { const header = utils.readNext(t.QueryHeaderSingleReference, q, i); + const fs = try Schema.getFieldSchema(fromType, header.prop); if (References.getReference(from, fs)) |ref| { const typeEntry = try Node.getType(ctx.db, header.typeId); @@ -126,20 +181,15 @@ pub fn referenceEdge( const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); + std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); + if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); - const edgesByteSizeIndex = try ctx.thread.query.reserve(4); - const edgeStartIndex = ctx.thread.query.index; try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); - ctx.thread.query.writeAs( - u32, - @truncate(ctx.thread.query.index - edgeStartIndex), - edgesByteSizeIndex, - ); } - i.* += header.edgeSize + header.includeSize; + // i.* += header.edgeSize; ctx.thread.query.writeAs( u32, @@ -149,5 +199,5 @@ pub fn referenceEdge( } } - i.* += header.includeSize; + i.* += header.includeSize + header.edgeSize; } diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index fe47af4822..fa505c58a7 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -15,7 +15,6 @@ await test('basic', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - // const client = await testDb(t, { const client = await db.setSchema({ types: { user: { diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index d15fc1f2a8..0ea2ec0c13 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,4 +1,3 @@ -import { getTypeDefs } from '../../dist/schema/defs/getTypeDefs.js' import { QueryAst } from '../../src/db-query/ast/ast.js' import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { @@ -7,7 +6,6 @@ import { } from '../../src/protocol/index.js' import { BasedDb, debugBuffer } from '../../src/sdk.js' import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' - import test from '../shared/test.js' await test('include', async (t) => { @@ -46,8 +44,6 @@ await test('include', async (t) => { }, }) - // console.log(getTypeDefs(client.schema!).get('user')?.main) - const a = client.create('user', { name: 'AAAAAAAAAA', y: 67, @@ -58,55 +54,40 @@ await test('include', async (t) => { }, }) - // const b = client.create('user', { - // name: 'BBBBBBBBB', - // y: 67, - // x: true, - // flap: 9999, - // cook: { - // cookie: 1234, - // }, - // }) - await client.create('user', { name: 'CCCCCCCCC', cook: { cookie: 1234, }, y: 0, - mrFriend: { id: a, $level: 99 }, - // friends: [{ id: a, $level: 250 }, b], + // mrFriend: { id: a, $level: 99 }, }) - await db.drain() - - console.log('-------') + // await client.create('user', { + // name: 'DDDDDDDD', + // cook: { + // cookie: 1234, + // }, + // y: 4, + // mrFriend: { id: a, $level: 99 }, + // }) - // let d = Date.now() + await db.drain() const ast: QueryAst = { type: 'user', filter: { props: { - // 1 y: { ops: [{ op: '=', val: 0 }] }, - // add reference - // add references - // add edges }, - // add AND - and: { - // 2 props: { y: { ops: [{ op: '=', val: 10 }] }, }, or: { - // 3 props: { y: { ops: [{ op: '=', val: 3 }] }, }, - // 4 or: { props: { y: { ops: [{ op: '=', val: 4 }] }, @@ -114,27 +95,18 @@ await test('include', async (t) => { }, }, }, - or: { - // 5 props: { y: { ops: [{ op: '=', val: 67 }] }, }, }, + }, - // (y=0 & ((y=10&x=100) | y=3 | y=4)) | y=67 - // 1:y=0 - // 2:y=10&x=100 - // 3:y=3 - // 4:y=4 - // 5:y=67 - // >5 1 >3 2 >4 3 >5 67 + // (y == 0 && (y == 10 || y == 3 || y == 4)) || y == 67 - // > means this is the next evaluation if it does not pass if any does not pass in the group e.g. (y=10) in 2 then its does not pass and return - }, props: { - // name: { include: {} }, y: { include: {} }, + // edge is broken in read mrFriend: { props: { y: { include: {} }, @@ -145,83 +117,11 @@ await test('include', async (t) => { }, }, }, - - // y=4 - - // // EDGE - - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, - // }, - // fix friends - // friends: { - // props: { - // name: { include: {} }, - // }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, - // }, }, } - // single ref edge filter - // sort - // variable filters - - // in js - // meta include, text include (localized: true) - const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) - // TODO - - // const bla = schema({ - // email: { type: 'string', format: 'email' }, - // subject: { type: 'string', max: 100 }, - // body: 'string' - // }) - - // const sendEmail = (x: bla) => { - // return sendgrid.sendEmail('beerdejim@gmail.com') - // } - - // filter - // AND + or (x.or.y).or(z) - // reference + edge - // refernces filters? + select - // now parsing in filters - // finish all operators (exist ,!nexist) how to handle for fixed? - // var filters - // like - // references includes - // includes - //. single ref or includes - //. eq STRING crc32 this is a seperate op /w branch check /w - - // include - // JS - // references select - // TEXT - make this localized true - // meta - - // SORT in js - - // subscriptions MULTI + references - // subscriptions in modify - // subscription NOW reschedule - // now parsingio - - // Based-server / client - // ctx .get bug - - // console.dir(ctx, { depth: 10 }) - const result = await db.server.getQueryBuf(ctx.query) debugBuffer(result) diff --git a/test/query/types.ts b/test/query/types.ts index 5157c57e14..8622e90b76 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -101,7 +101,7 @@ await test('query types', async (t) => { const i32: number = everything.i32 const u32: number = everything.u32 const b: boolean = everything.b - const txt: string = everything.txt + // const txt: string = everything.txt const js: any = everything.js const ts: number = everything.ts const bin: Uint8Array = everything.bin @@ -150,7 +150,7 @@ await test('query types', async (t) => { const res = data[0] // references - const myRef: { id: number } = res.myRef + // const myRef: { id: number } = res.myRef const myRefs: { id: number }[] = res.myRefs const id: number = res.id @@ -172,7 +172,7 @@ await test('query types', async (t) => { const res = data[0] const myEnum: 'a' | 'b' = res.myEnum - const myRef: { id: number } = res.myRef + // const myRef: { id: number } = res.myRef const myRefs: { id: number }[] = res.myRefs // Other scalars missing @@ -219,17 +219,17 @@ await test('query types', async (t) => { const query = db.query2('everything', 1).include('*', 'myRefs') const data = await query.get() - if ('n' in data) { - // Check it's a single item (not array) - const n: number = data.n - const myRefs: { id: number }[] = data.myRefs + // if ('n' in data) { + // // Check it's a single item (not array) + // const n: number = data.n + // const myRefs: { id: number }[] = data.myRefs - // @ts-expect-error - data.map + // // @ts-expect-error + // data.map - // @ts-expect-error - const myRef: number = data.myRef - } + // // @ts-expect-error + // const myRef: number = data.myRef + // } } { @@ -237,11 +237,11 @@ await test('query types', async (t) => { .query2('everything', 1) .include((select) => select('myRefs').include('isNice')) const data = await query.get() - if ('myRefs' in data) { - for (const item of data.myRefs) { - const isNice: boolean = item.isNice - } - } + // if ('myRefs' in data) { + // for (const item of data.myRefs) { + // const isNice: boolean = item.isNice + // } + // } } { From dc19bb26f89afba25ce12fa24233c91579f97841 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Tue, 10 Feb 2026 10:19:49 +0100 Subject: [PATCH 212/449] repair single ref/edge --- native/query/single.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index fb54a714bf..7535359a4c 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -189,8 +189,6 @@ pub fn referenceEdge( try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); } - // i.* += header.edgeSize; - ctx.thread.query.writeAs( u32, @truncate(ctx.thread.query.index - startIndex), From 9320b61c71a692a7edf8ffb25fbdaff1a7c8c34d Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Tue, 10 Feb 2026 10:38:01 +0100 Subject: [PATCH 213/449] re-add edge size --- native/query/single.zig | 14 ++++++++++++++ test/query-ast/include.ts | 11 +---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/native/query/single.zig b/native/query/single.zig index 7535359a4c..7f98af2f10 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -183,10 +183,24 @@ pub fn referenceEdge( std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); + // dont think this is nessecary + if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); + + const edgesByteSizeIndex = try ctx.thread.query.reserve(4); + const edgeStartIndex = ctx.thread.query.index; + try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); + + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - edgeStartIndex), + edgesByteSizeIndex, + ); + } else { + std.log.err("singe ref edge -> WRONG EDGE HAS TO BE THERE!\n", .{}); } ctx.thread.query.writeAs( diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 0ea2ec0c13..8c3066f2de 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -60,18 +60,9 @@ await test('include', async (t) => { cookie: 1234, }, y: 0, - // mrFriend: { id: a, $level: 99 }, + mrFriend: { id: a, $level: 99 }, }) - // await client.create('user', { - // name: 'DDDDDDDD', - // cook: { - // cookie: 1234, - // }, - // y: 4, - // mrFriend: { id: a, $level: 99 }, - // }) - await db.drain() const ast: QueryAst = { From 5a8f2e2ae96a2be3e3d507b52d545ba6b08f2c44 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Tue, 10 Feb 2026 10:43:05 +0100 Subject: [PATCH 214/449] fix include size --- native/query/single.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/query/single.zig b/native/query/single.zig index 7f98af2f10..4656bf26c7 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -181,7 +181,7 @@ pub fn referenceEdge( const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); - std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); + // std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); // dont think this is nessecary From f636425d40576bb72a8d8881f1c4961fcea8cab4 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 10 Feb 2026 08:21:24 -0300 Subject: [PATCH 215/449] uncommenting tests --- native/query/aggregates/references.zig | 2 +- test/aggregate/basic.ts | 903 +++++++++++++------------ 2 files changed, 474 insertions(+), 431 deletions(-) diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index c53f2d8252..22736c56f6 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -28,9 +28,9 @@ pub inline fn aggregateRefsProps( var it = try References.iterator(false, false, ctx.db, from, header.targetProp, fromType); _ = try Aggregates.iterator(ctx, &it, 1000, false, undefined, q[i.*..], accumulatorProp, it.dstType, undefined); // TODO: hllAcc + try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); try ctx.thread.query.append(@as(u32, header.resultsSize)); - try Aggregates.finalizeResults(ctx, q[i.*..], accumulatorProp, header.isSamplingSet, 0); } diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index c2d5bef0f5..344bfd91af 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -73,7 +73,7 @@ await test('sum top level', async (t) => { // deepEqual( // await db // .query('vote') - // .filter('country', '=', 'aa') + // .filter('country', '=', 'aa') // string filter not implemented yet // .sum('NL') // .get() // .toObject(), @@ -94,7 +94,7 @@ await test('sum top level', async (t) => { // deepEqual( // await db // .query('vote') - // .filter('country', '=', 'zz') + // .filter('country', '=', 'zz') // string filter not implemented yet // .sum('NL') // .get() // .toObject(), @@ -160,7 +160,7 @@ await test('top level count', async (t) => { AU: 15, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1] }) + const s = db.create('sequence', { votes: [nl1, nl2, au1] }) db.drain() db.create('sequence', { votes: nl1 }) db.create('sequence', { votes: nl2 }) @@ -177,7 +177,7 @@ await test('top level count', async (t) => { // deepEqual( // await db // .query('vote') - // .filter('country', '=', 'aa') + // .filter('country', '=', 'aa') // string filter not implemented yet // .count() // .get() // .toObject(), @@ -186,7 +186,7 @@ await test('top level count', async (t) => { // ) deepEqual( - await db.query('vote').include('IT').count().get(), + await db.query('vote').include('IT').count().get().toObject(), { count: 3 }, 'count, top level, ignoring include', ) @@ -194,7 +194,7 @@ await test('top level count', async (t) => { // deepEqual( // await db // .query('vote') - // .filter('country', '=', 'zz') + // .filter('country', '=', 'zz') // string filter not implemented yet // .count() // .get() // .toObject(), @@ -203,13 +203,13 @@ await test('top level count', async (t) => { // ) deepEqual( - await db.query('vote').filter('NL', '=', 20).count().get(), + await db.query('vote').filter('NL', '=', 20).count().get().toObject(), { count: 1 }, 'count, with filtering an int value', ) deepEqual( - await db.query('vote').filter('NL', '>', 1e6).count().get(), + await db.query('vote').filter('NL', '>', 1e6).count().get().toObject(), { count: 0 }, 'count, with no match filtering, int value', ) @@ -275,7 +275,7 @@ await test('two phase accumulation', async (t) => { country: 'Brazil', NL: 50, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) + const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) db.drain() db.create('sequence', { votes: nl1 }) db.create('sequence', { votes: nl2 }) @@ -284,7 +284,7 @@ await test('two phase accumulation', async (t) => { db.create('sequence', { votes: br1 }) deepEqual( - await db.query('vote').stddev('NL', { mode: 'sample' }).get(), + await db.query('vote').stddev('NL', { mode: 'sample' }).get().toObject(), { NL: { stddev: 15.56598856481656 }, }, @@ -292,7 +292,7 @@ await test('two phase accumulation', async (t) => { ) deepEqual( - await db.query('vote').stddev('NL', { mode: 'sample' }).get(), + await db.query('vote').stddev('NL', { mode: 'sample' }).get().toObject(), { NL: { stddev: 15.56598856481656 }, }, @@ -300,7 +300,11 @@ await test('two phase accumulation', async (t) => { ) deepEqual( - await db.query('vote').stddev('NL', { mode: 'population' }).get(), + await db + .query('vote') + .stddev('NL', { mode: 'population' }) + .get() + .toObject(), { NL: { stddev: 13.922643427165687 }, }, @@ -336,49 +340,49 @@ await test('two phase accumulation', async (t) => { 'stddev, top level, groupBy', ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').stddev('NL', { mode: 'population' })) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // NL: { stddev: 13.922643427165687 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').stddev('NL', { mode: 'population' })) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // NL: { stddev: 13.922643427165687 }, // }, - // ], - // 'stddev, branched References, no groupBy', - // ) + // }, + // ], + // 'stddev, branched References, no groupBy', + // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), - // ) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // Brazil: { - // NL: { stddev: 0 }, - // }, - // bb: { - // NL: { stddev: 6.5 }, - // }, - // aa: { - // NL: { stddev: 2.5 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => + // q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), + // ) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // Brazil: { + // NL: { stddev: 0 }, + // }, + // bb: { + // NL: { stddev: 6.5 }, + // }, + // aa: { + // NL: { stddev: 2.5 }, // }, // }, - // ], - // 'stddev, branched References, groupBy', - // ) + // }, + // ], + // 'stddev, branched References, groupBy', + // ) }) await test('numeric types', async (t) => { @@ -457,7 +461,7 @@ await test('numeric types', async (t) => { PL: -50, FI: -50.999, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) + // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) db.drain() db.create('sequence', { votes: nl1 }) db.create('sequence', { votes: nl2 }) @@ -466,7 +470,7 @@ await test('numeric types', async (t) => { db.create('sequence', { votes: br1 }) deepEqual( - await db.query('vote').groupBy('region').get(), + await db.query('vote').groupBy('region').get().toObject(), { bb: {}, aa: {}, @@ -476,7 +480,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db.query('vote').sum('NL', 'FI').groupBy('region').get(), + await db.query('vote').sum('NL', 'FI').groupBy('region').get().toObject(), { bb: { NL: { sum: 33 }, @@ -494,7 +498,7 @@ await test('numeric types', async (t) => { 'sum, main, group by', ) deepEqual( - await db.query('vote').count().groupBy('region').get(), + await db.query('vote').count().groupBy('region').get().toObject(), { bb: { count: 2, @@ -509,7 +513,12 @@ await test('numeric types', async (t) => { 'count, main, group by', ) deepEqual( - await db.query('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), + await db + .query('vote') + .avg('NL', 'PT', 'FI') + .groupBy('region') + .get() + .toObject(), { bb: { NL: { avg: 16.5 }, @@ -534,7 +543,8 @@ await test('numeric types', async (t) => { .query('vote') .harmonicMean('NL', 'PT', 'FI') .groupBy('region') - .get(), + .get() + .toObject(), { bb: { NL: { hmean: 13.93939393939394 }, @@ -559,7 +569,8 @@ await test('numeric types', async (t) => { .query('vote') .stddev('NL', 'PL', { mode: 'population' }) .groupBy('region') - .get(), + .get() + .toObject(), { bb: { NL: { stddev: 6.5 }, @@ -577,7 +588,12 @@ await test('numeric types', async (t) => { 'stddev, main, group by, pop', ) deepEqual( - await db.query('vote').stddev('NL', 'PL').groupBy('region').get(), + await db + .query('vote') + .stddev('NL', 'PL') + .groupBy('region') + .get() + .toObject(), { bb: { NL: { stddev: 9.192388155425117 }, @@ -599,7 +615,8 @@ await test('numeric types', async (t) => { .query('vote') .var('NL', 'PL', { mode: 'population' }) .groupBy('region') - .get(), + .get() + .toObject(), { bb: { NL: { variance: 42.25 }, @@ -621,7 +638,8 @@ await test('numeric types', async (t) => { .query('vote') .var('NL', 'PL', { mode: 'sample' }) .groupBy('region') - .get(), + .get() + .toObject(), { bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, @@ -630,7 +648,7 @@ await test('numeric types', async (t) => { 'variance, main, group by, sample', ) deepEqual( - await db.query('vote').var('NL', 'PL').groupBy('region').get(), + await db.query('vote').var('NL', 'PL').groupBy('region').get().toObject(), { bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, @@ -639,7 +657,12 @@ await test('numeric types', async (t) => { 'variance, main, group by, default (sample)', ) deepEqual( - await db.query('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + await db + .query('vote') + .max('NL', 'NO', 'PT', 'FI') + .groupBy('region') + .get() + .toObject(), { bb: { NL: { max: 23 }, @@ -663,7 +686,12 @@ await test('numeric types', async (t) => { 'max, main, group by', ) deepEqual( - await db.query('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + await db + .query('vote') + .min('NL', 'NO', 'PT', 'FI') + .groupBy('region') + .get() + .toObject(), { bb: { NL: { min: 10 }, @@ -687,405 +715,420 @@ await test('numeric types', async (t) => { 'min, main, group by', ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').sum('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // NL: { sum: 176 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').sum('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // NL: { sum: 176 }, // }, - // ], - // 'references, not grouped', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').avg('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // NL: { avg: 35.2 }, - // }, + // }, + // ], + // 'references, not grouped', + // ) + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').avg('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // NL: { avg: 35.2 }, // }, - // ], - // 'avg, references, not grouped', - // ) + // }, + // ], + // 'avg, references, not grouped', + // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').harmonicMean('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // NL: { hmean: 24.18565978675536 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').harmonicMean('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // NL: { hmean: 24.18565978675536 }, // }, - // ], - // 'harmonic_mean, references, not grouped', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').sum('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { sum: 33 }, - // }, - // aa: { - // NL: { sum: 93 }, - // }, - // Great: { - // NL: { sum: 50 }, - // }, + // }, + // ], + // 'harmonic_mean, references, not grouped', + // ) + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').groupBy('region').sum('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { sum: 33 }, + // }, + // aa: { + // NL: { sum: 93 }, + // }, + // Great: { + // NL: { sum: 50 }, // }, // }, - // ], - // 'sum, references, group by', - // ) + // }, + // ], + // 'sum, references, group by', + // ) // // await db.query('vote').groupBy('sequence').sum('NL').get().inspect() - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').count()) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { count: 2 }, - // aa: { count: 2 }, - // Great: { count: 1 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').groupBy('region').count()) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { count: 2 }, + // aa: { count: 2 }, + // Great: { count: 1 }, // }, - // ], - // 'count, references, group by', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').groupBy('region').stddev('NL', { mode: 'population' }), - // ) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { stddev: 6.5 }, - // }, - // aa: { - // NL: { stddev: 3.5 }, - // }, - // Great: { - // NL: { stddev: 0 }, - // }, + // }, + // ], + // 'count, references, group by', + // ) + // deepEqual( + // await db + // .query('sequence') + // .include((q) => + // q('votes').groupBy('region').stddev('NL', { mode: 'population' }), + // ) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { stddev: 6.5 }, + // }, + // aa: { + // NL: { stddev: 3.5 }, + // }, + // Great: { + // NL: { stddev: 0 }, // }, // }, - // ], - // 'stddev, references, group by', - // ) + // }, + // ], + // 'stddev, references, group by', + // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').stddev('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { stddev: 9.192388155425117 } }, - // aa: { NL: { stddev: 4.949747468305833 } }, - // Great: { NL: { stddev: 0 } }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').groupBy('region').stddev('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { stddev: 9.192388155425117 } }, + // aa: { NL: { stddev: 4.949747468305833 } }, + // Great: { NL: { stddev: 0 } }, // }, - // ], - // 'stddev, references, group by', - // ) + // }, + // ], + // 'stddev, references, group by', + // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').groupBy('region').var('NL', { mode: 'population' }), - // ) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { variance: 42.25 }, - // }, - // aa: { - // NL: { variance: 12.25 }, - // }, - // Great: { - // NL: { variance: 0 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => + // q('votes').groupBy('region').var('NL', { mode: 'population' }), + // ) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { variance: 42.25 }, // }, - // }, - // ], - // 'variance, references, group by, pop', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').groupBy('region').var('NL', { mode: 'sample' }), - // ) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { variance: 84.5 } }, - // aa: { NL: { variance: 24.5 } }, - // Great: { NL: { variance: 0 } }, + // aa: { + // NL: { variance: 12.25 }, // }, - // }, - // ], - // 'variance, references, group by, sample', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').var('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { variance: 84.5 } }, - // aa: { NL: { variance: 24.5 } }, - // Great: { NL: { variance: 0 } }, + // Great: { + // NL: { variance: 0 }, // }, // }, - // ], - // 'variance, references, group by, defaul (sample)', - // ) + // }, + // ], + // 'variance, references, group by, pop', + // ) + // deepEqual( + // await db + // .query('sequence') + // .include((q) => + // q('votes').groupBy('region').var('NL', { mode: 'sample' }), + // ) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { variance: 84.5 } }, + // aa: { NL: { variance: 24.5 } }, + // Great: { NL: { variance: 0 } }, + // }, + // }, + // ], + // 'variance, references, group by, sample', + // ) + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').groupBy('region').var('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { variance: 84.5 } }, + // aa: { NL: { variance: 24.5 } }, + // Great: { NL: { variance: 0 } }, + // }, + // }, + // ], + // 'variance, references, group by, defaul (sample)', + // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').avg('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { avg: 16.5 }, - // }, - // aa: { - // NL: { avg: 46.5 }, - // }, - // Great: { - // NL: { avg: 50 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').groupBy('region').avg('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { avg: 16.5 }, + // }, + // aa: { + // NL: { avg: 46.5 }, + // }, + // Great: { + // NL: { avg: 50 }, // }, // }, - // ], - // 'avg, references, group by', - // ) + // }, + // ], + // 'avg, references, group by', + // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').harmonicMean('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { hmean: 13.93939393939394 }, - // }, - // aa: { - // NL: { hmean: 46.236559139784944 }, - // }, - // Great: { - // NL: { hmean: 50 }, - // }, + // deepEqual( + // await db + // .query('sequence') + // .include((q) => q('votes').groupBy('region').harmonicMean('NL')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { hmean: 13.93939393939394 }, + // }, + // aa: { + // NL: { hmean: 46.236559139784944 }, + // }, + // Great: { + // NL: { hmean: 50 }, // }, // }, - // ], - // 'harmonic_mean, references, group by', - // ) + // }, + // ], + // 'harmonic_mean, references, group by', + // ) }) -// await test.skip('fixed length strings', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// }) -// await db.start({ clean: true }) -// t.after(() => db.stop()) +await test('fixed length strings', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => db.stop()) -// await db.setSchema({ -// types: { -// product: { -// name: { type: 'string', maxBytes: 10 }, -// flap: 'number', -// }, -// shelve: { -// code: { type: 'string', maxBytes: 4 }, -// products: { -// items: { -// ref: 'product', -// prop: 'product', -// }, -// }, -// }, -// }, -// }) + await db.setSchema({ + types: { + product: { + name: { type: 'string', maxBytes: 10 }, + flap: 'number', + }, + shelve: { + code: { type: 'string', maxBytes: 4 }, + products: { + items: { + ref: 'product', + prop: 'product', + }, + }, + }, + }, + }) -// const rnd = fastPrng() -// for (let i = 0; i < 100; i++) { -// let p = db.create('product', { -// name: `lala ${rnd(0, 10)}`, -// flap: Math.random() * 100, -// }) -// db.create('shelve', { -// code: `S${rnd(0, 10)}`, -// products: [p], -// }) -// } - -// equal( -// Number( -// Object.keys( -// await db -// .query('product') -// .include('*') -// .avg('flap') -// .groupBy('name') -// .get() -// .toObject(), -// )[0].substring(4, 6), -// ) < 100, -// true, -// 'fixed length strings on main', -// ) - -// equal( -// Number( -// Object.keys( -// await db -// .query('shelve') -// .include((q) => q('products').avg('flap').groupBy('name')) -// .get() -// .toObject(), -// )[0].substring(4, 6), -// ) < 100, -// true, -// 'fixed length strings on references', -// ) -// }) + const rnd = fastPrng() + for (let i = 0; i < 100; i++) { + let p = db.create('product', { + name: `lala ${rnd(0, 10)}`, + flap: Math.random() * 100, + }) + db.create('shelve', { + code: `S${rnd(0, 10)}`, + products: [p], + }) + } -// await test('range', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// }) -// await db.start({ clean: true }) -// t.after(() => db.stop()) + equal( + Number( + Object.keys( + await db + .query('product') + .include('*') + .avg('flap') + .groupBy('name') + .get() + .toObject(), + )[0].substring(4, 6), + ) < 100, + true, + 'fixed length strings on main', + ) -// const ter = ['lala', 'lele', 'lili'] + // equal( + // Number( + // Object.keys( + // await db + // .query('shelve') + // .include((q) => q('products').avg('flap').groupBy('name')) + // .get() + // .toObject(), + // )[0].substring(4, 6), + // ) < 100, + // true, + // 'fixed length strings on references', + // ) +}) -// await db.setSchema({ -// types: { -// job: { -// day: 'timestamp', -// tip: 'number', -// employee: { -// ref: 'employee', -// prop: 'employee', -// }, -// }, -// employee: { -// name: 'string', -// area: { -// items: { ref: 'territory', prop: 'territory' }, -// }, -// }, -// territory: { -// name: ter, -// flap: 'number', -// state: { -// ref: 'state', -// prop: 'state', -// }, -// }, -// state: { -// name: 'string', -// }, -// }, -// }) +await test('range', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => db.stop()) -// const rnd = fastPrng() -// for (let i = 0; i < 10; i++) { -// const d = new Date('11/11/2024 11:00-3') -// db.create('job', { -// day: new Date(d.getTime() + Math.random() * 1e7), -// tip: Math.random() * 20, -// }) -// const s = db.create('state', { -// name: `statelala ${rnd(0, 2)}`, -// }) -// const t = db.create('territory', { -// name: ter[rnd(0, ter.length - 1)], -// flap: Math.random() * 100, -// state: s, -// }) -// db.create('employee', { -// name: `emplala ${rnd(0, 10)}`, -// area: [t], -// }) -// } - -// deepEqual( -// Object.keys( -// await db -// .query('job') -// .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) -// .avg('tip') -// .range(0, 2) -// .get() -// .toObject(), -// ).length, -// 2, -// 'range group by main', -// ) - -// deepEqual( -// Object.keys( -// await db -// .query('employee') -// .include((q) => q('area').groupBy('name').sum('flap'), '*') -// .range(0, 2) -// .get() -// .toObject(), -// ).length, -// 2, -// 'range group by references', -// ) -// }) + const ter = ['lala', 'lele', 'lili'] + + await db.setSchema({ + types: { + job: { + day: 'timestamp', + tip: 'number', + employee: { + ref: 'employee', + prop: 'employee', + }, + }, + employee: { + name: 'string', + area: { + items: { ref: 'territory', prop: 'territory' }, + }, + }, + territory: { + name: ter, + flap: 'number', + state: { + ref: 'state', + prop: 'state', + }, + }, + state: { + name: 'string', + }, + }, + }) + + const rnd = fastPrng() + for (let i = 0; i < 10; i++) { + const d = new Date('11/11/2024 11:00-3') + db.create('job', { + day: new Date(d.getTime() + Math.random() * 1e7), + tip: Math.random() * 20, + }) + const s = db.create('state', { + name: `statelala ${rnd(0, 2)}`, + }) + db.drain() + const t = db.create('territory', { + name: ter[rnd(0, ter.length - 1)], + flap: Math.random() * 100, + state: s, + }) + db.drain() + db.create('employee', { + name: `emplala ${rnd(0, 10)}`, + area: [t], + }) + db.drain() + } + + deepEqual( + Object.keys( + await db + .query('job') + .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) + .avg('tip') + .range(0, 2) + .get() + .toObject(), + ).length, + 2, + 'range group by main', + ) + + // deepEqual( + // Object.keys( + // await db + // .query('employee') + // .include((q) => q('area').groupBy('name').sum('flap'), '*') + // .range(0, 2) + // .get() + // .toObject(), + // ).length, + // 2, + // 'range group by references', + // ) +}) // await test('count props', async (t) => { // const db = new BasedDb({ From 17e6f23a033b33649b4e6064746c822719955e71 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 11:40:31 +0100 Subject: [PATCH 216/449] Rename some struct fields for clarity --- clibs/include/selva/types.h | 4 ++-- clibs/lib/selva/alias.c | 12 ++++++------ clibs/lib/selva/colvec.c | 10 +++++----- clibs/lib/selva/db.c | 4 ++-- clibs/lib/selva/fields.c | 2 +- clibs/lib/selva/include/db.h | 2 +- clibs/lib/selva/io/dump.c | 12 ++++++------ clibs/lib/selva/schema.c | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/clibs/include/selva/types.h b/clibs/include/selva/types.h index e5a5a7c703..ad6e8d0539 100644 --- a/clibs/include/selva/types.h +++ b/clibs/include/selva/types.h @@ -100,8 +100,8 @@ struct SelvaFieldsSchema { }; struct SelvaNodeSchema { - size_t nr_aliases; /*!< Number of alias fields in this type. */ - size_t nr_colvecs; /*!< Number of columnar vector fields. */ + size_t nr_alias_fields; /*!< Number of alias fields in this type. */ + size_t nr_colvec_fields; /*!< Number of columnar vector fields. */ struct SelvaFieldsSchema fields_schema; /* Nothing must be put after this line. */ }; diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index b8e8fdf857..abb1cce6cc 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -14,7 +14,7 @@ void selva_init_aliases(struct SelvaTypeEntry *type) const struct SelvaFieldsSchema *fields_schema = &type->ns.fields_schema; const size_t nr_fields = fields_schema->nr_fields; - type->aliases = selva_malloc(type->ns.nr_aliases * sizeof(struct SelvaAliases)); + type->aliases = selva_malloc(type->ns.nr_alias_fields * sizeof(struct SelvaAliases)); for (size_t i = 0; i < nr_fields; i++) { const struct SelvaFieldSchema *fs = &fields_schema->field_schemas[i]; @@ -22,7 +22,7 @@ void selva_init_aliases(struct SelvaTypeEntry *type) if (fs->type == SELVA_FIELD_TYPE_ALIAS) { struct SelvaAliases *field_aliases = &type->aliases[fs->alias_index]; - assert(fs->alias_index < type->ns.nr_aliases); + assert(fs->alias_index < type->ns.nr_alias_fields); field_aliases->field = fs->field; RB_INIT(&field_aliases->alias_by_name); RB_INIT(&field_aliases->alias_by_dest); @@ -36,13 +36,13 @@ void selva_destroy_aliases(struct SelvaTypeEntry *type) /* * We assume that all the aliases in the aliases structs have been freed already. */ - for (size_t i = 0; i < type->ns.nr_aliases; i++) { + for (size_t i = 0; i < type->ns.nr_alias_fields; i++) { assert(type->aliases->nr_aliases == 0); } #endif selva_free(type->aliases); - type->ns.nr_aliases = 0; + type->ns.nr_alias_fields = 0; type->aliases = nullptr; } @@ -187,7 +187,7 @@ const char *selva_get_alias_name(const struct SelvaAlias *alias, size_t *len) struct SelvaAliases *selva_get_aliases(struct SelvaTypeEntry *type, field_t field) { - const size_t nr_aliases = type->ns.nr_aliases; + const size_t nr_aliases = type->ns.nr_alias_fields; for (size_t i = 0; i < nr_aliases; i++) { if (type->aliases[i].field == field) { @@ -200,7 +200,7 @@ struct SelvaAliases *selva_get_aliases(struct SelvaTypeEntry *type, field_t fiel void selva_remove_all_aliases(struct SelvaTypeEntry *type, node_id_t node_id) { - const size_t nr_aliases = type->ns.nr_aliases; + const size_t nr_aliases = type->ns.nr_alias_fields; for (size_t i = 0; i < nr_aliases; i++) { selva_del_alias_by_dest(&type->aliases[i], node_id); diff --git a/clibs/lib/selva/colvec.c b/clibs/lib/selva/colvec.c index c9aef81d3e..f21d4021d2 100644 --- a/clibs/lib/selva/colvec.c +++ b/clibs/lib/selva/colvec.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024-2025 SAULX + * Copyright (c) 2024-2026 SAULX * SPDX-License-Identifier: MIT * * A colvec is a columnar vector field in Selva. Specifically a colvec structure @@ -29,7 +29,7 @@ static inline size_t colvec_slab_off(size_t block_capacity, size_t vec_size, nod void colvec_init_te(struct SelvaTypeEntry *te) { - size_t nr_colvecs = te->ns.nr_colvecs; + size_t nr_colvecs = te->ns.nr_colvec_fields; struct SelvaNodeSchema *ns = &te->ns; size_t nr_blocks = te->blocks->len; size_t block_capacity = selva_get_block_capacity(te); @@ -48,7 +48,7 @@ void colvec_init_te(struct SelvaTypeEntry *te) size_t ci = fs->colvec.index; size_t slab_size = block_capacity * fs->colvec.vec_len * fs->colvec.comp_size; - assert(ci < ns->nr_colvecs); + assert(ci < ns->nr_colvec_fields); te->col_fields.colvec[ci] = (struct SelvaColvec){ .field = i, @@ -62,7 +62,7 @@ void colvec_init_te(struct SelvaTypeEntry *te) void colvec_deinit_te(struct SelvaTypeEntry *te) { - for (size_t i = 0; i < te->ns.nr_colvecs; i++) { + for (size_t i = 0; i < te->ns.nr_colvec_fields; i++) { struct SelvaColvec *colvec = &te->col_fields.colvec[i]; block_id_t blocks_len = te->blocks->len; @@ -100,7 +100,7 @@ void colvec_init_node(struct SelvaTypeEntry *te, struct SelvaNode *node) /* * Initialize each col field of this node. */ - for (size_t i = 0; i < te->ns.nr_colvecs; i++) { + for (size_t i = 0; i < te->ns.nr_colvec_fields; i++) { struct SelvaColvec *colvec = &te->col_fields.colvec[i]; const struct SelvaFieldSchema *fs = get_fs_by_fields_schema_field(&te->ns.fields_schema, colvec->field); uint8_t *slab = colvec_init_slab(colvec, block_i); diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 8da54a96b7..5884eda58c 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -805,7 +805,7 @@ extern inline node_type_t selva_get_node_type(const struct SelvaNode *node); */ static void hash_aliases(selva_hash_state_t *hash_state, struct SelvaTypeEntry *type, node_id_t dest) { - for (size_t i = 0; i < type->ns.nr_aliases; i++) { + for (size_t i = 0; i < type->ns.nr_alias_fields; i++) { struct SelvaAliases *aliases = &type->aliases[i]; const struct SelvaAlias *alias; struct SelvaAlias find = { @@ -827,7 +827,7 @@ static void hash_col_fields(struct SelvaTypeEntry *type, node_id_t node_id, selv /* * colvec fields. */ - for (size_t i = 0; i < type->ns.nr_colvecs; i++) { + for (size_t i = 0; i < type->ns.nr_colvec_fields; i++) { struct SelvaColvec *colvec = &type->col_fields.colvec[i]; colvec_hash_update(type, node_id, colvec, tmp_hash_state); diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 624dc20dc8..9d92bc969c 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -2081,7 +2081,7 @@ static void selva_fields_init(struct SelvaTypeEntry *te, struct SelvaFields *fie void selva_fields_init_node(struct SelvaTypeEntry *te, struct SelvaNode *node, bool set_defaults) { selva_fields_init(te, &node->fields, set_defaults); - if (te->ns.nr_colvecs > 0) { + if (te->ns.nr_colvec_fields > 0) { colvec_init_node(te, node); } } diff --git a/clibs/lib/selva/include/db.h b/clibs/lib/selva/include/db.h index b166dd3d63..d75ff82276 100644 --- a/clibs/lib/selva/include/db.h +++ b/clibs/lib/selva/include/db.h @@ -91,7 +91,7 @@ struct SelvaTypeEntry { struct SelvaAliasesByName alias_by_name; struct SelvaAliasesByDest alias_by_dest; size_t nr_aliases; /*!< Number of aliases by name. */ - } *aliases __pcounted_by(ns.nr_aliases); + } *aliases __pcounted_by(ns.nr_alias_fields); size_t nr_nodes; /*!< Number of nodes of this type. */ struct mempool nodepool; /*!< Pool for struct SelvaNode of this type. */ diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index e775aea6c0..d936569707 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -315,9 +315,9 @@ static void save_aliases(struct selva_io *io, struct SelvaDb *db) write_dump_magic(io, DUMP_MAGIC_ALIASES); RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { - const sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; + const sdb_nr_aliases_t nr_fields = te->ns.nr_alias_fields; - for (size_t i = 0; i < nr_aliases; i++) { + for (size_t i = 0; i < nr_fields; i++) { struct SelvaAliases *aliases = &te->aliases[i]; sdb_nr_aliases_t nr_aliases_by_name = aliases->nr_aliases; struct SelvaAlias *alias; @@ -411,7 +411,7 @@ static void selva_dump_save_colvec(struct selva_io *io, struct SelvaDb *, struct io->sdb_write(&block_i, sizeof(block_i), 1, io); static_assert(sizeof(block_i) == sizeof(uint32_t)); - for (size_t i = 0; i < te->ns.nr_colvecs; i++) { + for (size_t i = 0; i < te->ns.nr_colvec_fields; i++) { struct SelvaColvec *colvec = &te->col_fields.colvec[i]; uint8_t *slab = (uint8_t *)colvec->v[block_i]; uint8_t slab_present = !!slab; @@ -829,7 +829,7 @@ static int load_colvec(struct selva_io *io, struct SelvaTypeEntry *te) io->sdb_read(&block_i, sizeof(block_i), 1, io); static_assert(sizeof(block_i) == sizeof(uint32_t)); - for (size_t i = 0; i < te->ns.nr_colvecs; i++) { + for (size_t i = 0; i < te->ns.nr_colvec_fields; i++) { uint8_t slab_present; io->sdb_read(&slab_present, sizeof(slab_present), 1, io); @@ -911,9 +911,9 @@ static int load_aliases(struct selva_io *io, struct SelvaDb *db) } RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { - const sdb_nr_aliases_t nr_aliases = te->ns.nr_aliases; + const sdb_nr_aliases_t nr_fields = te->ns.nr_alias_fields; - for (sdb_nr_aliases_t i = 0; i < nr_aliases; i++) { + for (sdb_nr_aliases_t i = 0; i < nr_fields; i++) { sdb_nr_aliases_t nr_aliases_by_name; io->sdb_read(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index c638d67930..946c5e8b1b 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -499,8 +499,8 @@ int schemabuf_parse_ns(struct SelvaNodeSchema *ns, const uint8_t *buf, size_t le } int err = parse2(&ctx, fields_schema, buf + SCHEMA_MIN_SIZE, len - SCHEMA_MIN_SIZE); - ns->nr_aliases = ctx.alias_index; - ns->nr_colvecs = ctx.colvec_index; + ns->nr_alias_fields = ctx.alias_index; + ns->nr_colvec_fields = ctx.colvec_index; return err; } From 3d73d0582bbc6f0eea888c2e738ff0235f03ea93 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 12:22:37 +0100 Subject: [PATCH 217/449] Cleanup --- clibs/lib/selva/alias.c | 23 +++++++++++++---------- clibs/lib/selva/io/dump.c | 6 +++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index abb1cce6cc..1114b148e1 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -58,11 +58,6 @@ static struct SelvaAlias *insert_alias_by_name(struct SelvaAliases *aliases, str return old_alias; } -static inline void remove_alias_by_dest(struct SelvaAliases *aliases, struct SelvaAlias *alias) -{ - RB_REMOVE(SelvaAliasesByDest, &aliases->alias_by_dest, alias); -} - static inline void remove_alias_by_name(struct SelvaAliases *aliases, struct SelvaAlias *alias) { struct SelvaAlias *removed = RB_REMOVE(SelvaAliasesByName, &aliases->alias_by_name, alias); @@ -70,6 +65,16 @@ static inline void remove_alias_by_name(struct SelvaAliases *aliases, struct Sel aliases->nr_aliases--; } +static inline struct SelvaAlias *insert_alias_by_dest(struct SelvaAliases *aliases, struct SelvaAlias *alias) +{ + return RB_INSERT(SelvaAliasesByDest, &aliases->alias_by_dest, alias); +} + +static inline void remove_alias_by_dest(struct SelvaAliases *aliases, struct SelvaAlias *alias) +{ + (void)RB_REMOVE(SelvaAliasesByDest, &aliases->alias_by_dest, alias); +} + static void del_alias(struct SelvaAliases *aliases, struct SelvaAlias *alias) { remove_alias_by_name(aliases, alias); @@ -93,7 +98,7 @@ node_id_t selva_set_alias_p(struct SelvaAliases *aliases, struct SelvaAlias *new del_alias(aliases, old_alias); } - while ((old_by_dest = RB_INSERT(SelvaAliasesByDest, &aliases->alias_by_dest, new_alias))) { + while ((old_by_dest = insert_alias_by_dest(aliases, new_alias))) { del_alias(aliases, old_by_dest); } @@ -137,11 +142,9 @@ void selva_del_alias_by_dest(struct SelvaAliases *aliases, node_id_t dest) }; struct SelvaAlias *alias = RB_FIND(SelvaAliasesByDest, &aliases->alias_by_dest, &find); - if (!alias) { - return; + if (alias) { + del_alias(aliases, alias); } - - del_alias(aliases, alias); } struct SelvaNodeRes selva_get_alias(struct SelvaTypeEntry *type, struct SelvaAliases *aliases, const char *name_str, size_t name_len) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index d936569707..0570f6c42b 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -315,7 +315,7 @@ static void save_aliases(struct selva_io *io, struct SelvaDb *db) write_dump_magic(io, DUMP_MAGIC_ALIASES); RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { - const sdb_nr_aliases_t nr_fields = te->ns.nr_alias_fields; + const size_t nr_fields = te->ns.nr_alias_fields; for (size_t i = 0; i < nr_fields; i++) { struct SelvaAliases *aliases = &te->aliases[i]; @@ -911,9 +911,9 @@ static int load_aliases(struct selva_io *io, struct SelvaDb *db) } RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { - const sdb_nr_aliases_t nr_fields = te->ns.nr_alias_fields; + const size_t nr_fields = te->ns.nr_alias_fields; - for (sdb_nr_aliases_t i = 0; i < nr_fields; i++) { + for (size_t i = 0; i < nr_fields; i++) { sdb_nr_aliases_t nr_aliases_by_name; io->sdb_read(&nr_aliases_by_name, sizeof(nr_aliases_by_name), 1, io); From da55450e7c9e2883b80ce7c43fd8a5ee51b7ef80 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 12:23:23 +0100 Subject: [PATCH 218/449] nr_aliases must be initialized Ref FDN-1878 --- clibs/lib/selva/alias.c | 1 + 1 file changed, 1 insertion(+) diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index 1114b148e1..a1053d2725 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -24,6 +24,7 @@ void selva_init_aliases(struct SelvaTypeEntry *type) assert(fs->alias_index < type->ns.nr_alias_fields); field_aliases->field = fs->field; + field_aliases->nr_aliases = 0; RB_INIT(&field_aliases->alias_by_name); RB_INIT(&field_aliases->alias_by_dest); } From d6d173fda22e65dfc0c44ce93e56a2718b8d1fff Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 10 Feb 2026 09:40:27 -0300 Subject: [PATCH 219/449] uncommenting ref tests --- test/aggregate/basic.ts | 639 ++++++++++++++++++++-------------------- 1 file changed, 320 insertions(+), 319 deletions(-) diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 344bfd91af..7b382a3c62 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -61,7 +61,7 @@ await test('sum top level', async (t) => { db.create('sequence', { votes: nl2 }) db.create('sequence', { votes: au1 }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1] }) + // const s = db.create('sequence', { votes: [nl1, nl2, au1] }) // create with an array not implemented yet // top level ---------------------------------- deepEqual( @@ -275,7 +275,7 @@ await test('two phase accumulation', async (t) => { country: 'Brazil', NL: 50, }) - const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) + // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) // create multiple with an array is not implemented yet db.drain() db.create('sequence', { votes: nl1 }) db.create('sequence', { votes: nl2 }) @@ -340,49 +340,49 @@ await test('two phase accumulation', async (t) => { 'stddev, top level, groupBy', ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').stddev('NL', { mode: 'population' })) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // NL: { stddev: 13.922643427165687 }, - // }, - // }, - // ], - // 'stddev, branched References, no groupBy', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').stddev('NL', { mode: 'population' })) + .get() + .toObject(), + [ + { + id: 1, + votes: { + NL: { stddev: 13.922643427165687 }, + }, + }, + ], + 'stddev, branched References, no groupBy', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), - // ) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // Brazil: { - // NL: { stddev: 0 }, - // }, - // bb: { - // NL: { stddev: 6.5 }, - // }, - // aa: { - // NL: { stddev: 2.5 }, - // }, - // }, - // }, - // ], - // 'stddev, branched References, groupBy', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => + q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), + ) + .get() + .toObject(), + [ + { + id: 1, + votes: { + Brazil: { + NL: { stddev: 0 }, + }, + bb: { + NL: { stddev: 6.5 }, + }, + aa: { + NL: { stddev: 2.5 }, + }, + }, + }, + ], + 'stddev, branched References, groupBy', + ) }) await test('numeric types', async (t) => { @@ -461,7 +461,7 @@ await test('numeric types', async (t) => { PL: -50, FI: -50.999, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) + // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) // create with an array not implemented yet db.drain() db.create('sequence', { votes: nl1 }) db.create('sequence', { votes: nl2 }) @@ -715,260 +715,260 @@ await test('numeric types', async (t) => { 'min, main, group by', ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').sum('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // NL: { sum: 176 }, - // }, - // }, - // ], - // 'references, not grouped', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').avg('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // NL: { avg: 35.2 }, - // }, - // }, - // ], - // 'avg, references, not grouped', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').sum('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + NL: { sum: 176 }, + }, + }, + ], + 'references, not grouped', + ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').avg('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + NL: { avg: 35.2 }, + }, + }, + ], + 'avg, references, not grouped', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').harmonicMean('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // NL: { hmean: 24.18565978675536 }, - // }, - // }, - // ], - // 'harmonic_mean, references, not grouped', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').sum('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { sum: 33 }, - // }, - // aa: { - // NL: { sum: 93 }, - // }, - // Great: { - // NL: { sum: 50 }, - // }, - // }, - // }, - // ], - // 'sum, references, group by', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').harmonicMean('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + NL: { hmean: 24.18565978675536 }, + }, + }, + ], + 'harmonic_mean, references, not grouped', + ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').groupBy('region').sum('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { + NL: { sum: 33 }, + }, + aa: { + NL: { sum: 93 }, + }, + Great: { + NL: { sum: 50 }, + }, + }, + }, + ], + 'sum, references, group by', + ) - // // await db.query('vote').groupBy('sequence').sum('NL').get().inspect() + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').groupBy('region').count()) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { count: 2 }, + aa: { count: 2 }, + Great: { count: 1 }, + }, + }, + ], + 'count, references, group by', + ) + deepEqual( + await db + .query('sequence') + .include((q) => + q('votes').groupBy('region').stddev('NL', { mode: 'population' }), + ) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { + NL: { stddev: 6.5 }, + }, + aa: { + NL: { stddev: 3.5 }, + }, + Great: { + NL: { stddev: 0 }, + }, + }, + }, + ], + 'stddev, references, group by', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').count()) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { count: 2 }, - // aa: { count: 2 }, - // Great: { count: 1 }, - // }, - // }, - // ], - // 'count, references, group by', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').groupBy('region').stddev('NL', { mode: 'population' }), - // ) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { stddev: 6.5 }, - // }, - // aa: { - // NL: { stddev: 3.5 }, - // }, - // Great: { - // NL: { stddev: 0 }, - // }, - // }, - // }, - // ], - // 'stddev, references, group by', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').groupBy('region').stddev('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { NL: { stddev: 9.192388155425117 } }, + aa: { NL: { stddev: 4.949747468305833 } }, + Great: { NL: { stddev: 0 } }, + }, + }, + ], + 'stddev, references, group by', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').stddev('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { stddev: 9.192388155425117 } }, - // aa: { NL: { stddev: 4.949747468305833 } }, - // Great: { NL: { stddev: 0 } }, - // }, - // }, - // ], - // 'stddev, references, group by', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => + q('votes').groupBy('region').var('NL', { mode: 'population' }), + ) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { + NL: { variance: 42.25 }, + }, + aa: { + NL: { variance: 12.25 }, + }, + Great: { + NL: { variance: 0 }, + }, + }, + }, + ], + 'variance, references, group by, pop', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').groupBy('region').var('NL', { mode: 'population' }), - // ) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { variance: 42.25 }, - // }, - // aa: { - // NL: { variance: 12.25 }, - // }, - // Great: { - // NL: { variance: 0 }, - // }, - // }, - // }, - // ], - // 'variance, references, group by, pop', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => - // q('votes').groupBy('region').var('NL', { mode: 'sample' }), - // ) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { variance: 84.5 } }, - // aa: { NL: { variance: 24.5 } }, - // Great: { NL: { variance: 0 } }, - // }, - // }, - // ], - // 'variance, references, group by, sample', - // ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').var('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { variance: 84.5 } }, - // aa: { NL: { variance: 24.5 } }, - // Great: { NL: { variance: 0 } }, - // }, - // }, - // ], - // 'variance, references, group by, defaul (sample)', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => + q('votes').groupBy('region').var('NL', { mode: 'sample' }), + ) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { NL: { variance: 84.5 } }, + aa: { NL: { variance: 24.5 } }, + Great: { NL: { variance: 0 } }, + }, + }, + ], + 'variance, references, group by, sample', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').avg('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { avg: 16.5 }, - // }, - // aa: { - // NL: { avg: 46.5 }, - // }, - // Great: { - // NL: { avg: 50 }, - // }, - // }, - // }, - // ], - // 'avg, references, group by', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').groupBy('region').var('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { NL: { variance: 84.5 } }, + aa: { NL: { variance: 24.5 } }, + Great: { NL: { variance: 0 } }, + }, + }, + ], + 'variance, references, group by, defaul (sample)', + ) - // deepEqual( - // await db - // .query('sequence') - // .include((q) => q('votes').groupBy('region').harmonicMean('NL')) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { hmean: 13.93939393939394 }, - // }, - // aa: { - // NL: { hmean: 46.236559139784944 }, - // }, - // Great: { - // NL: { hmean: 50 }, - // }, - // }, - // }, - // ], - // 'harmonic_mean, references, group by', - // ) + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').groupBy('region').avg('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { + NL: { avg: 16.5 }, + }, + aa: { + NL: { avg: 46.5 }, + }, + Great: { + NL: { avg: 50 }, + }, + }, + }, + ], + 'avg, references, group by', + ) + + deepEqual( + await db + .query('sequence') + .include((q) => q('votes').groupBy('region').harmonicMean('NL')) + .get() + .toObject(), + [ + { + id: 1, + votes: { + bb: { + NL: { hmean: 13.93939393939394 }, + }, + aa: { + NL: { hmean: 46.236559139784944 }, + }, + Great: { + NL: { hmean: 50 }, + }, + }, + }, + ], + 'harmonic_mean, references, group by', + ) }) await test('fixed length strings', async (t) => { @@ -1002,9 +1002,10 @@ await test('fixed length strings', async (t) => { name: `lala ${rnd(0, 10)}`, flap: Math.random() * 100, }) + db.drain() db.create('shelve', { code: `S${rnd(0, 10)}`, - products: [p], + products: p, }) } @@ -1024,19 +1025,19 @@ await test('fixed length strings', async (t) => { 'fixed length strings on main', ) - // equal( - // Number( - // Object.keys( - // await db - // .query('shelve') - // .include((q) => q('products').avg('flap').groupBy('name')) - // .get() - // .toObject(), - // )[0].substring(4, 6), - // ) < 100, - // true, - // 'fixed length strings on references', - // ) + equal( + Number( + Object.keys( + await db + .query('shelve') + .include((q) => q('products').avg('flap').groupBy('name')) + .get() + .toObject(), + )[0].substring(4, 6), + ) < 100, + true, + 'fixed length strings on references', + ) }) await test('range', async (t) => { @@ -1097,7 +1098,7 @@ await test('range', async (t) => { db.drain() db.create('employee', { name: `emplala ${rnd(0, 10)}`, - area: [t], + area: t, }) db.drain() } @@ -1116,18 +1117,18 @@ await test('range', async (t) => { 'range group by main', ) - // deepEqual( - // Object.keys( - // await db - // .query('employee') - // .include((q) => q('area').groupBy('name').sum('flap'), '*') - // .range(0, 2) - // .get() - // .toObject(), - // ).length, - // 2, - // 'range group by references', - // ) + deepEqual( + Object.keys( + await db + .query('employee') + .include((q) => q('area').groupBy('name').sum('flap'), '*') + .range(0, 2) + .get() + .toObject(), + ).length, + 2, + 'range group by references', + ) }) // await test('count props', async (t) => { From 2f9230d46b469776dba8b6481bf4b6bfee8254d9 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 15:04:25 +0100 Subject: [PATCH 220/449] Fix typing for t --- test/shared/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shared/index.ts b/test/shared/index.ts index 74584f03cb..6cd5736f4c 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -83,7 +83,7 @@ export interface BasedTestDb } export const testDb = async ( - t, + t: Parameters[1]>[0], schema: S & ValidateSchema, ): Promise> => { const db = new BasedDb({ path: t.tmp }) From 1c3d6266974dc2d6b5ebdd3dd9e5fa4ad9578efb Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 15:17:42 +0100 Subject: [PATCH 221/449] .toObject() is unnecessary here --- test/capped.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/capped.ts b/test/capped.ts index e035f9b8cd..ac81ad4d19 100644 --- a/test/capped.ts +++ b/test/capped.ts @@ -36,7 +36,7 @@ await test('capped type', async (t) => { wind: 50, }) - deepEqual((await db.query('meas').get()).toObject(), [ + deepEqual(await db.query('meas').get(), [ { id: 1, temperature: 0, humidity: 99, wind: 10 }, { id: 2, temperature: 1, humidity: 98, wind: 10 }, { id: 3, temperature: 2, humidity: 97, wind: 50 }, @@ -47,7 +47,7 @@ await test('capped type', async (t) => { humidity: 1, }) - deepEqual((await db.query('meas').get()).toObject(), [ + deepEqual(await db.query('meas').get(), [ { id: 1, temperature: -100, humidity: 1, wind: 10 }, { id: 2, temperature: 1, humidity: 98, wind: 10 }, { id: 3, temperature: 2, humidity: 97, wind: 50 }, @@ -63,7 +63,7 @@ await test('capped type', async (t) => { humidity: 1, }) - deepEqual((await db.query('meas').get()).toObject(), [ + deepEqual(await db.query('meas').get(), [ { id: 1, temperature: -100, humidity: 1, wind: 10 }, { id: 2, temperature: -50, humidity: 1, wind: 5 }, { id: 3, temperature: -40, humidity: 1, wind: 10 }, @@ -95,7 +95,7 @@ await test('capped type', async (t) => { humidity: 1, }) - deepEqual((await db.query('meas').get()).toObject(), [ + deepEqual(await db.query('meas').get(), [ { id: 1, temperature: -40, humidity: 1, wind: 10 }, { id: 2, temperature: -50, humidity: 1, wind: 10 }, { id: 3, temperature: -40, humidity: 1, wind: 10 }, From 006f3877705d39fe02b419cc958296132a13ce1c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 15:31:11 +0100 Subject: [PATCH 222/449] Fix typing of deepEqual() --- test/shared/assert.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/shared/assert.ts b/test/shared/assert.ts index d23a07597e..0dd15421aa 100644 --- a/test/shared/assert.ts +++ b/test/shared/assert.ts @@ -5,8 +5,7 @@ import { BasedQueryResponse } from '../../src/db-client/query/BasedQueryResponse import { PropTypeInverse } from '../../src/zigTsExports.js' export { perf } from './perf.js' -// add fn -export const deepEqual = (a: A, b: NoInfer, msg?: string) => { +export const deepEqual = (a: A, b: NoInfer : A>, msg?: string) => { if (a instanceof BasedQueryResponse) { a = a.toObject() } @@ -25,9 +24,9 @@ ${util.inspect(a, { depth: 10, maxStringLength: 60 })} } } -export const notEqual = (a, b, msg?: string) => { +export const notEqual = (a: any, b: any, msg?: string) => { if (uDeepEqual(a, b)) { - const m = `${msg || 'Should not be equal:'} + const m = `${msg || 'Should not be equal:'} ------------------ A --------------------- ${util.inspect(a, { depth: 10, maxStringLength: 60 })} ------------------ B --------------------- From 8539eb301e2f9ed8ed4f9dd54ebb7503e9c9287e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 15:43:25 +0100 Subject: [PATCH 223/449] prettier --- test/shared/assert.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/shared/assert.ts b/test/shared/assert.ts index 0dd15421aa..ac82359254 100644 --- a/test/shared/assert.ts +++ b/test/shared/assert.ts @@ -5,7 +5,11 @@ import { BasedQueryResponse } from '../../src/db-client/query/BasedQueryResponse import { PropTypeInverse } from '../../src/zigTsExports.js' export { perf } from './perf.js' -export const deepEqual = (a: A, b: NoInfer : A>, msg?: string) => { +export const deepEqual = ( + a: A, + b: NoInfer : A>, + msg?: string, +) => { if (a instanceof BasedQueryResponse) { a = a.toObject() } From 344e2afc6e946660e448e2faa94cf4c741f8f436 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 10 Feb 2026 15:47:53 +0100 Subject: [PATCH 224/449] .toObject() is unnecessary --- test/binary.ts | 4 +- test/delete.perf.ts | 6 +- test/delete.ts | 20 +- test/edges/edges.ts | 182 +++++------ test/enum.ts | 18 +- test/filter/or.ts | 46 ++- test/filter/string.ts | 30 +- test/number.ts | 12 +- test/range.ts | 19 +- test/references/references.ts | 579 ++++++++++++++++------------------ test/save/save.ts | 40 +-- test/save/saveRange.ts | 77 ++--- test/sort/sort.ts | 20 +- test/string.ts | 152 ++++----- test/update.ts | 22 +- test/validation/validation.ts | 13 +- 16 files changed, 540 insertions(+), 700 deletions(-) diff --git a/test/binary.ts b/test/binary.ts index ea68816ca2..b6baddf487 100644 --- a/test/binary.ts +++ b/test/binary.ts @@ -29,7 +29,7 @@ await test('simple', async (t) => { await db.drain() deepEqual( - (await db.query('user').get()).toObject(), + await db.query('user').get(), [ { id: 1, @@ -43,7 +43,7 @@ await test('simple', async (t) => { file: new Uint8Array([1, 2, 3, 4]), }) - deepEqual((await db.query('user', id).get()).toObject(), { + deepEqual(await db.query('user', id).get(), { id, file: new Uint8Array([1, 2, 3, 4]), }) diff --git a/test/delete.perf.ts b/test/delete.perf.ts index 3e05b466ae..5742651e2a 100644 --- a/test/delete.perf.ts +++ b/test/delete.perf.ts @@ -55,7 +55,7 @@ await test('delete performance', async (t) => { }, 'delete 1M users') assert(t0 < 400, 'delete 1M users') - deepEqual((await db.query('user').get()).toObject(), []) + deepEqual(await db.query('user').get(), []) const amountArticles = 1e6 const articles: any[] = [] @@ -76,7 +76,7 @@ await test('delete performance', async (t) => { }, 'delete 1M articles') assert(t2 < 400, 'delete 1M users') - deepEqual((await db.query('article').get()).toObject(), []) + deepEqual(await db.query('article').get(), []) const articles2: any[] = [] @@ -111,5 +111,5 @@ await test('delete performance', async (t) => { } await db.drain() }, 'delete 1M articles - again') // < 400 - deepEqual((await db.query('article').get()).toObject(), []) + deepEqual(await db.query('article').get(), []) }) diff --git a/test/delete.ts b/test/delete.ts index 623b0565bc..b15bc6264b 100644 --- a/test/delete.ts +++ b/test/delete.ts @@ -38,11 +38,11 @@ await test('delete', async (t) => { await db.delete('user', simple) await db.drain() - deepEqual((await db.query('user').get()).toObject(), []) + deepEqual(await db.query('user').get(), []) const nurp = db.create('nurp', {}) await db.drain() - deepEqual((await db.query('nurp').include('email').get()).toObject(), [ + deepEqual(await db.query('nurp').include('email').get(), [ { email: '', id: 1, @@ -52,7 +52,7 @@ await test('delete', async (t) => { db.delete('nurp', nurp) await db.drain() - deepEqual((await db.query('user').include('email').get()).toObject(), []) + deepEqual(await db.query('user').include('email').get(), []) const nurp2 = db.create('nurp', { email: 'flippie' }) await db.drain() @@ -62,7 +62,7 @@ await test('delete', async (t) => { }) await db.drain() - deepEqual((await db.query('nurp').include('email').get()).toObject(), [ + deepEqual(await db.query('nurp').include('email').get(), [ { email: '', id: 2, @@ -104,12 +104,12 @@ await test('non existing 1', async (t) => { db.delete('user', simple) await db.drain() - deepEqual((await db.query('user').get()).toObject(), []) + deepEqual(await db.query('user').get(), []) const nurp = db.create('nurp', {}) await db.drain() - deepEqual((await db.query('nurp').include('email').get()).toObject(), [ + deepEqual(await db.query('nurp').include('email').get(), [ { email: '', id: 1, @@ -157,13 +157,13 @@ await test('non existing 2', async (t) => { await db.delete('user', simple) - deepEqual((await db.query('user').get()).toObject(), []) + deepEqual(await db.query('user').get(), []) db.create('nurp', {}) await db.drain() - deepEqual((await db.query('nurp').include('email').get()).toObject(), [ + deepEqual(await db.query('nurp').include('email').get(), [ { email: '', id: 1, @@ -223,6 +223,6 @@ await test('save', async (t) => { t.after(() => db2.destroy(), true) - deepEqual(await db2.query('user').include('id').get().toObject(), [{ id: 2 }]) - deepEqual(await db.query('user').include('id').get().toObject(), [{ id: 2 }]) + deepEqual(await db2.query('user').include('id').get(), [{ id: 2 }]) + deepEqual(await db.query('user').include('id').get(), [{ id: 2 }]) }) diff --git a/test/edges/edges.ts b/test/edges/edges.ts index 523655b6ac..5a272d51c2 100644 --- a/test/edges/edges.ts +++ b/test/edges/edges.ts @@ -114,12 +114,10 @@ await test('multiple references', async (t) => { // ) deepEqual( - ( - await db - .query('article') - .include('contributors.$role', 'contributors.$bigString') - .get() - ).toObject(), + await db + .query('article') + .include('contributors.$role', 'contributors.$bigString') + .get(), [ { id: artStrudel, @@ -132,52 +130,41 @@ await test('multiple references', async (t) => { ], ) - deepEqual( - ( - await db.query('article').include('contributors.$rating').get() - ).toObject(), - [ - { - id: artStrudel, - contributors: [{ id: 1, $rating: 5 }], - }, - { - id: artItaly, - contributors: [{ id: 1, $rating: 0 }], - }, - ], - ) + deepEqual(await db.query('article').include('contributors.$rating').get(), [ + { + id: artStrudel, + contributors: [{ id: 1, $rating: 5 }], + }, + { + id: artItaly, + contributors: [{ id: 1, $rating: 0 }], + }, + ]) - deepEqual( - (await db.query('article').include('contributors.$lang').get()).toObject(), - [ - { - id: 1, - contributors: [{ id: 1, $lang: 'en' }], - }, - { - id: 2, - contributors: [{ id: 1 }], - }, - ], - ) + deepEqual(await db.query('article').include('contributors.$lang').get(), [ + { + id: 1, + contributors: [{ id: 1, $lang: 'en' }], + }, + { + id: 2, + contributors: [{ id: 1 }], + }, + ]) - deepEqual( - (await db.query('article').include('contributors.$on').get()).toObject(), - [ - { - id: 1, - contributors: [{ id: 1, $on: true }], - }, - { - id: 2, - contributors: [{ id: 1, $on: false }], - }, - ], - ) + deepEqual(await db.query('article').include('contributors.$on').get(), [ + { + id: 1, + contributors: [{ id: 1, $on: true }], + }, + { + id: 2, + contributors: [{ id: 1, $on: false }], + }, + ]) deepEqual( - (await db.query('article').include('contributors.$file').get()).toObject(), + await db.query('article').include('contributors.$file').get(), [ { id: 1, @@ -204,14 +191,12 @@ await test('multiple references', async (t) => { } deepEqual( - ( - await db - .query('article') - .include((s) => - s('contributors').filter('$role', '=', 'writer').include('$role'), - ) - .get() - ).toObject(), + await db + .query('article') + .include((s) => + s('contributors').filter('$role', '=', 'writer').include('$role'), + ) + .get(), [ { id: 1, @@ -230,16 +215,14 @@ await test('multiple references', async (t) => { ) deepEqual( - ( - await db - .query('article') - .include((s) => - s('contributors') - .filter('$bigString', '=', italy) - .include('$bigString'), - ) - .get() - ).toObject(), + await db + .query('article') + .include((s) => + s('contributors') + .filter('$bigString', '=', italy) + .include('$bigString'), + ) + .get(), [ { id: 1, @@ -267,8 +250,7 @@ await test('multiple references', async (t) => { await db .query('article', lastArticle) .include('contributors.$rating') - .get() - .toObject(), + .get(), { id: 5, contributors: [{ id: 2, $rating: 2 }, { id: 3 }, { id: 1 }], @@ -353,19 +335,16 @@ await test('single reference', async (t) => { author: { id: mrDrol, $role: 'boss' }, }) - deepEqual( - (await db.query('article').include('author.$role', '*').get()).toObject(), - [ - { + deepEqual(await db.query('article').include('author.$role', '*').get(), [ + { + id: 1, + name: 'This is a nice article', + author: { id: 1, - name: 'This is a nice article', - author: { - id: 1, - $role: 'boss', - }, + $role: 'boss', }, - ], - ) + }, + ]) await db.create('article', { name: 'This is a nice article with mr drol as writer', @@ -373,13 +352,11 @@ await test('single reference', async (t) => { }) deepEqual( - ( - await db - .query('article') - .include('author.$role', '*') - .filter('author.$role', '=', 'boss') - .get() - ).toObject(), + await db + .query('article') + .include('author.$role', '*') + .filter('author.$role', '=', 'boss') + .get(), [ { id: 1, @@ -397,25 +374,22 @@ await test('single reference', async (t) => { author: { id: mrDrol, $msg: sentence }, }) - deepEqual( - (await db.query('article').include('author.$msg', '*').get()).toObject(), - [ - { id: 1, name: 'This is a nice article', author: { id: 1 } }, - { - id: 2, - name: 'This is a nice article with mr drol as writer', - author: { id: 1 }, - }, - { - id: 3, - name: 'Power article', - author: { - id: 1, - $msg: sentence, - }, + deepEqual(await db.query('article').include('author.$msg', '*').get(), [ + { id: 1, name: 'This is a nice article', author: { id: 1 } }, + { + id: 2, + name: 'This is a nice article with mr drol as writer', + author: { id: 1 }, + }, + { + id: 3, + name: 'Power article', + author: { + id: 1, + $msg: sentence, }, - ], - ) + }, + ]) }) await test('preserve fields', async (t) => { diff --git a/test/enum.ts b/test/enum.ts index 5e981e5f16..9cd21b6edc 100644 --- a/test/enum.ts +++ b/test/enum.ts @@ -37,7 +37,7 @@ await test('enum', async (t) => { db.create('user', {}) - deepEqual((await db.query('user').include('fancyness').get()).toObject(), [ + deepEqual(await db.query('user').include('fancyness').get(), [ { id: 1, fancyness: 'mid' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -45,13 +45,11 @@ await test('enum', async (t) => { ]) deepEqual( - ( - await db - .query('user') - .include('fancyness') - .filter('fancyness', '=', 'fire') - .get() - ).toObject(), + await db + .query('user') + .include('fancyness') + .filter('fancyness', '=', 'fire') + .get(), [ { id: 2, fancyness: 'fire' }, { id: 4, fancyness: 'fire' }, @@ -62,7 +60,7 @@ await test('enum', async (t) => { fancyness: 'beta', }) - deepEqual((await db.query('user').include('fancyness').get()).toObject(), [ + deepEqual(await db.query('user').include('fancyness').get(), [ { id: 1, fancyness: 'beta' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -84,7 +82,7 @@ await test('enum', async (t) => { }), ) - deepEqual((await db.query('user').include('fancyness').get()).toObject(), [ + deepEqual(await db.query('user').include('fancyness').get(), [ { id: 1, fancyness: 'fire' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, diff --git a/test/filter/or.ts b/test/filter/or.ts index 2351ec72df..ae18f0aa50 100644 --- a/test/filter/or.ts +++ b/test/filter/or.ts @@ -24,9 +24,7 @@ await test('filter or', async (t) => { } deepEqual( - ( - await db.query('user').filter('nr', '>', 8).or('nr', '<', 1).get() - ).toObject(), + await db.query('user').filter('nr', '>', 8).or('nr', '<', 1).get(), [ { id: 1, @@ -41,15 +39,13 @@ await test('filter or', async (t) => { ) deepEqual( - ( - await db - .query('user') - .filter('nr', '>', 8) - .or((t) => { - t.filter('nr', '<', 1).or('nr', '=', 5) - }) - .get() - ).toObject(), + await db + .query('user') + .filter('nr', '>', 8) + .or((t) => { + t.filter('nr', '<', 1).or('nr', '=', 5) + }) + .get(), [ { id: 1, @@ -68,14 +64,12 @@ await test('filter or', async (t) => { ) deepEqual( - ( - await db - .query('user') - .filter('nr', '>', 8) - .or('nr', '<', 1) - .or('nr', '=', 5) - .get() - ).toObject(), + await db + .query('user') + .filter('nr', '>', 8) + .or('nr', '<', 1) + .or('nr', '=', 5) + .get(), [ { id: 1, @@ -94,13 +88,11 @@ await test('filter or', async (t) => { ) deepEqual( - ( - await db - .query('user') - .filter('nr', '>', 8) - .or(() => {}) - .get() - ).toObject(), + await db + .query('user') + .filter('nr', '>', 8) + .or(() => {}) + .get(), [ { id: 10, diff --git a/test/filter/string.ts b/test/filter/string.ts index 3a3dfbdcf3..888e63fbe4 100644 --- a/test/filter/string.ts +++ b/test/filter/string.ts @@ -76,14 +76,12 @@ await test('variable size (string/binary)', async (t) => { await db.drain() deepEqual( - ( await db .query('article') .filter('stuff', '=', ENCODER.encode('#' + 2)) .include('name', 'stuff', 'derp', 'type') .range(0, 10) - .get() - ).toObject(), + .get(), [ { id: 3, @@ -296,8 +294,7 @@ await test('has uncompressed', async (t) => { .filter('headline', 'includes', 'pager') .include('id', 'headline') .range(0, 1e3) - .get() - .then((v) => v.toObject()), + .get(), [ { id: 501, @@ -316,8 +313,7 @@ await test('has uncompressed', async (t) => { .filter('headline', 'includes', 'Pager', { lowerCase: true }) .include('id', 'headline') .range(0, 1e3) - .get() - .then((v) => v.toObject()), + .get(), [ { id: 501, @@ -336,8 +332,7 @@ await test('has uncompressed', async (t) => { .filter('headline', 'includes', 'refugee', { lowerCase: true }) .include('id', 'headline') .range(0, 1e3) - .get() - .then((v) => v.toObject()), + .get(), [], ) @@ -347,8 +342,7 @@ await test('has uncompressed', async (t) => { .filter('headline', 'includes', 'gaza', { lowerCase: true }) .include('id', 'headline') .range(0, 1e3) - .get() - .then((v) => v.toObject()), + .get(), [ { id: 801, @@ -385,18 +379,16 @@ await test('main has (string/binary)', async (t) => { stuff, derp: new Uint8Array([1, 2, 3, 4]), } - deepEqual((await db.query('article').get()).toObject(), [derpResult]) + deepEqual(await db.query('article').get(), [derpResult]) deepEqual( - (await db.query('article').filter('stuff', '=', stuff).get()).toObject(), + await db.query('article').filter('stuff', '=', stuff).get(), [derpResult], ) deepEqual( - ( await db .query('article') .filter('derp', 'includes', new Uint8Array([4])) - .get() - ).toObject(), + .get(), [derpResult], ) }) @@ -527,8 +519,7 @@ await test('has OR uncompressed', async (t) => { .filter('title', 'includes', ['gaza', 'tubbies'], { lowerCase: true }) .include('id', 'title') .range(0, 1e3) - .get() - .then((v) => v.toObject()), + .get(), [ { id: 501, @@ -544,8 +535,7 @@ await test('has OR uncompressed', async (t) => { .filter('title', 'includes', ['crisis', 'refugee'], { lowerCase: true }) .include('id', 'title') .range(0, 1e3) - .get() - .then((v) => v.toObject()), + .get(), [], ) }) diff --git a/test/number.ts b/test/number.ts index f732a67c5e..1195c97204 100644 --- a/test/number.ts +++ b/test/number.ts @@ -70,7 +70,7 @@ await test('basic', async (t) => { await db.drain() // will become async deepEqual( - (await db.query('user').get()).toObject(), + await db.query('user').get(), payloads.map((payload, index) => { return { id: index + 1, @@ -102,7 +102,7 @@ await test('basic', async (t) => { }, }) - deepEqual((await db.query('user', newThing).get()).toObject(), { + deepEqual(await db.query('user', newThing).get(), { id: newThing, number: 12, int8: 12, @@ -137,7 +137,7 @@ await test('basic', async (t) => { }, }) - deepEqual((await db.query('user', newThing).get()).toObject(), { + deepEqual(await db.query('user', newThing).get(), { id: newThing, number: 13, int8: 14, @@ -154,7 +154,7 @@ await test('basic', async (t) => { }, }) - deepEqual((await db.query('user', newThing).get()).toObject(), { + deepEqual(await db.query('user', newThing).get(), { id: newThing, number: 13, int8: 14, @@ -171,7 +171,7 @@ await test('basic', async (t) => { }, }) - deepEqual((await db.query('user', newThing).get()).toObject(), { + deepEqual(await db.query('user', newThing).get(), { id: newThing, number: 13, int8: 14, @@ -186,7 +186,7 @@ await test('basic', async (t) => { uint16: 100, }) - deepEqual((await db.query('user', newThing).get()).toObject(), { + deepEqual(await db.query('user', newThing).get(), { id: newThing, number: 13, int8: 14, diff --git a/test/range.ts b/test/range.ts index 51216f4b3d..2f009dd116 100644 --- a/test/range.ts +++ b/test/range.ts @@ -62,18 +62,13 @@ await test('range', async (t) => { await db.drain() - const result = await db.query('user').include('nr').range(1, 2).get() - - deepEqual(result.toObject(), [{ id: 2, nr: 2 }]) - - const result2 = await db - .query('user') - .include('nr') - .sort('email') - .range(1, 2) - .get() - - deepEqual(result2.toObject(), [{ id: 2, nr: 2 }]) + deepEqual(await db.query('user').include('nr').range(1, 2).get(), [ + { id: 2, nr: 2 }, + ]) + deepEqual( + await db.query('user').include('nr').sort('email').range(1, 2).get(), + [{ id: 2, nr: 2 }], + ) }) await test('default range: 1000', async (t) => { diff --git a/test/references/references.ts b/test/references/references.ts index 42f424238d..9a0d161b46 100644 --- a/test/references/references.ts +++ b/test/references/references.ts @@ -62,36 +62,30 @@ await test('references', async (t) => { await db.drain() - deepEqual( - (await db.query('article').include('contributors.name').get()).toObject(), - [ - { - id: await strudelArticle, - contributors: [{ id: await mrSnurp, name: 'Mr snurp' }], - }, - { - id: await piArticle, - contributors: [ - { id: await mrSnurp, name: 'Mr snurp' }, - { id: await flippie, name: 'Flippie' }, - ], - }, - ], - ) + deepEqual(await db.query('article').include('contributors.name').get(), [ + { + id: await strudelArticle, + contributors: [{ id: await mrSnurp, name: 'Mr snurp' }], + }, + { + id: await piArticle, + contributors: [ + { id: await mrSnurp, name: 'Mr snurp' }, + { id: await flippie, name: 'Flippie' }, + ], + }, + ]) - deepEqual( - (await db.query('user').include('articles.name').get()).toObject(), - [ - { - id: 1, - articles: [ - { id: 1, name: 'The wonders of Strudel' }, - { id: 2, name: 'Apple Pie is a Lie' }, - ], - }, - { id: 2, articles: [{ id: 2, name: 'Apple Pie is a Lie' }] }, - ], - ) + deepEqual(await db.query('user').include('articles.name').get(), [ + { + id: 1, + articles: [ + { id: 1, name: 'The wonders of Strudel' }, + { id: 2, name: 'Apple Pie is a Lie' }, + ], + }, + { id: 2, articles: [{ id: 2, name: 'Apple Pie is a Lie' }] }, + ]) }) await test('one to many', async (t) => { @@ -143,7 +137,7 @@ await test('one to many', async (t) => { } await db.drain() - deepEqual((await db.query('user').include('resources').get()).toObject(), [ + deepEqual(await db.query('user').include('resources').get(), [ { id: 1, resources: [ @@ -171,32 +165,29 @@ await test('one to many', async (t) => { }, ]) - deepEqual( - (await db.query('user').include('resources.name').get()).toObject(), - [ - { - id: 1, - resources: [ - { - id: 1, - name: 'thing 0', - }, - { - id: 2, - name: 'thing 1', - }, - { - id: 3, - name: 'thing 2', - }, - { - id: 4, - name: 'thing 3', - }, - ], - }, - ], - ) + deepEqual(await db.query('user').include('resources.name').get(), [ + { + id: 1, + resources: [ + { + id: 1, + name: 'thing 0', + }, + { + id: 2, + name: 'thing 1', + }, + { + id: 3, + name: 'thing 2', + }, + { + id: 4, + name: 'thing 3', + }, + ], + }, + ]) }) await test('one to many really', async (t) => { @@ -240,166 +231,148 @@ await test('one to many really', async (t) => { resources: [cpu, kbd, mouse, fd], }) await db.drain() - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 1, - name: 'cpu', - }, - { - id: 2, - name: 'keyboard', - }, - { - id: 3, - name: 'mouse', - }, - { - id: 4, - name: 'floppy', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 1, + name: 'cpu', + }, + { + id: 2, + name: 'keyboard', + }, + { + id: 3, + name: 'mouse', + }, + { + id: 4, + name: 'floppy', + }, + ], + }) await db.update('user', user, { resources: [cpu, kbd, mouse], }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 1, - name: 'cpu', - }, - { - id: 2, - name: 'keyboard', - }, - { - id: 3, - name: 'mouse', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 1, + name: 'cpu', + }, + { + id: 2, + name: 'keyboard', + }, + { + id: 3, + name: 'mouse', + }, + ], + }) await db.update('user', user, { resources: [cpu, kbd, mouse], }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 1, - name: 'cpu', - }, - { - id: 2, - name: 'keyboard', - }, - { - id: 3, - name: 'mouse', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 1, + name: 'cpu', + }, + { + id: 2, + name: 'keyboard', + }, + { + id: 3, + name: 'mouse', + }, + ], + }) await db.update('user', user, { resources: [cpu, kbd, mouse, fd], }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 1, - name: 'cpu', - }, - { - id: 2, - name: 'keyboard', - }, - { - id: 3, - name: 'mouse', - }, - { - id: 4, - name: 'floppy', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 1, + name: 'cpu', + }, + { + id: 2, + name: 'keyboard', + }, + { + id: 3, + name: 'mouse', + }, + { + id: 4, + name: 'floppy', + }, + ], + }) await db.update('user', user, { resources: [kbd, cpu, fd, mouse], }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 2, - name: 'keyboard', - }, - { - id: 1, - name: 'cpu', - }, - { - id: 4, - name: 'floppy', - }, - { - id: 3, - name: 'mouse', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 2, + name: 'keyboard', + }, + { + id: 1, + name: 'cpu', + }, + { + id: 4, + name: 'floppy', + }, + { + id: 3, + name: 'mouse', + }, + ], + }) const joy = await db.create('resource', { name: 'joystick', owner: user }) await db.update('resource', joy, { owner: user }) await db.update('resource', joy, { owner: user }) await db.update('resource', joy, { owner: user }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 2, - name: 'keyboard', - }, - { - id: 1, - name: 'cpu', - }, - { - id: 4, - name: 'floppy', - }, - { - id: 3, - name: 'mouse', - }, - { - id: 5, - name: 'joystick', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 2, + name: 'keyboard', + }, + { + id: 1, + name: 'cpu', + }, + { + id: 4, + name: 'floppy', + }, + { + id: 3, + name: 'mouse', + }, + { + id: 5, + name: 'joystick', + }, + ], + }) await db.update('user', user, { resources: [kbd, cpu, fd, mouse], @@ -409,68 +382,62 @@ await test('one to many really', async (t) => { update: [joy], }, }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 2, - name: 'keyboard', - }, - { - id: 1, - name: 'cpu', - }, - { - id: 4, - name: 'floppy', - }, - { - id: 3, - name: 'mouse', - }, - { - id: 5, - name: 'joystick', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 2, + name: 'keyboard', + }, + { + id: 1, + name: 'cpu', + }, + { + id: 4, + name: 'floppy', + }, + { + id: 3, + name: 'mouse', + }, + { + id: 5, + name: 'joystick', + }, + ], + }) await db.update('user', user, { resources: { update: [joy, kbd, cpu, fd, mouse], }, }) - deepEqual( - await db.query('user', user).include('resources').get().toObject(), - { - id: 1, - resources: [ - { - id: 2, - name: 'keyboard', - }, - { - id: 1, - name: 'cpu', - }, - { - id: 4, - name: 'floppy', - }, - { - id: 3, - name: 'mouse', - }, - { - id: 5, - name: 'joystick', - }, - ], - }, - ) + deepEqual(await db.query('user', user).include('resources').get(), { + id: 1, + resources: [ + { + id: 2, + name: 'keyboard', + }, + { + id: 1, + name: 'cpu', + }, + { + id: 4, + name: 'floppy', + }, + { + id: 3, + name: 'mouse', + }, + { + id: 5, + name: 'joystick', + }, + ], + }) }) await test('update', async (t) => { @@ -529,20 +496,17 @@ await test('update', async (t) => { contributors: [flippie], }) await db.drain() - deepEqual( - (await db.query('article').include('contributors.name').get()).toObject(), - [ - { - id: 1, - contributors: [ - { - name: 'Flippie', - id: await flippie, - }, - ], - }, - ], - ) + deepEqual(await db.query('article').include('contributors.name').get(), [ + { + id: 1, + contributors: [ + { + name: 'Flippie', + id: await flippie, + }, + ], + }, + ]) await wait(1000) }) @@ -611,9 +575,7 @@ await test('filter', async (t) => { await db.drain() deepEqual( - ( - await db.query('article', strudelArticle).include('contributors').get() - ).toObject(), + await db.query('article', strudelArticle).include('contributors').get(), { id: 1, contributors: [ @@ -627,14 +589,10 @@ await test('filter', async (t) => { ) deepEqual( - ( - await db - .query('article', strudelArticle) - .include((q) => - q('contributors').include('name').filter('flap', '>', 25), - ) - .get() - ).toObject(), + await db + .query('article', strudelArticle) + .include((q) => q('contributors').include('name').filter('flap', '>', 25)) + .get(), { id: 1, contributors: [ @@ -646,17 +604,15 @@ await test('filter', async (t) => { ) deepEqual( - ( - await db - .query('article', strudelArticle) - .include((q) => { - q('contributors').include('flap') - q('contributors').include('name') - q('contributors').filter('flap', '>', 25) - q('contributors').filter('flap', '<', 35) - }) - .get() - ).toObject(), + await db + .query('article', strudelArticle) + .include((q) => { + q('contributors').include('flap') + q('contributors').include('name') + q('contributors').filter('flap', '>', 25) + q('contributors').filter('flap', '<', 35) + }) + .get(), { id: 1, contributors: [{ id: 3, name: 'Derpie', flap: 30 }], @@ -665,18 +621,16 @@ await test('filter', async (t) => { ) deepEqual( - ( - await db - .query('article', strudelArticle) - .include((select) => { - select('contributors') - .include('name') - .include('flap') - .filter('flap', '>', 25) - .sort('flap', 'desc') - }) - .get() - ).toObject(), + await db + .query('article', strudelArticle) + .include((select) => { + select('contributors') + .include('name') + .include('flap') + .filter('flap', '>', 25) + .sort('flap', 'desc') + }) + .get(), { id: 1, contributors: [ @@ -890,18 +844,15 @@ await test('single ref save and load', async (t) => { invitedBy: 2, }) - deepEqual( - await db.query('user').include('email', 'invitedBy').get().toObject(), - [ - { id: 1, email: '1@saulx.com', invitedBy: null }, - { id: 2, email: '2@saulx.com', invitedBy: null }, - { - id: 3, - email: '3@saulx.com', - invitedBy: { id: 2, email: '2@saulx.com', name: '' }, - }, - ], - ) + deepEqual(await db.query('user').include('email', 'invitedBy').get(), [ + { id: 1, email: '1@saulx.com', invitedBy: null }, + { id: 2, email: '2@saulx.com', invitedBy: null }, + { + id: 3, + email: '3@saulx.com', + invitedBy: { id: 2, email: '2@saulx.com', name: '' }, + }, + ]) await db.stop() }) @@ -964,8 +915,8 @@ await test('single2many - update refs', async (t) => { reviews: [review1, review2, review3], }) - const products = await db.query('product').include('*', '**').get().toObject() - const reviews = await db.query('review').include('*', '**').get().toObject() + const products = await db.query('product').include('*', '**').get() + const reviews = await db.query('review').include('*', '**').get() deepEqual(products, [ { id: 1, reviews: [] }, diff --git a/test/save/save.ts b/test/save/save.ts index dc510424b0..5718c43c46 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -352,8 +352,8 @@ await test.skip('db is drained before save', async (t) => { await db2.start() deepEqual( - await db2.query('person').include('name', 'books').get().toObject(), - await db.query('person').include('name', 'books').get().toObject(), + await db2.query('person').include('name', 'books').get(), + await db.query('person').include('name', 'books').get(), ) }) @@ -406,10 +406,7 @@ await test('create', async (t) => { await db2.start() t.after(() => db2.destroy()) - deepEqual( - await db2.query('person').get().toObject(), - await db.query('person').get().toObject(), - ) + deepEqual(await db2.query('person').get(), await db.query('person').get()) }) await test('upsert', async (t) => { @@ -500,10 +497,7 @@ await test('alias blocks', async (t) => { await db2.start() t.after(() => db2.destroy()) - deepEqual( - await db2.query('person').get().toObject(), - await db.query('person').get().toObject(), - ) + deepEqual(await db2.query('person').get(), await db.query('person').get()) }) await test('simulated periodic save', async (t) => { @@ -618,8 +612,7 @@ await test('simulated periodic save', async (t) => { .query('person') .filter('alias', 'includes', 'slim') .include('alias', 'name') - .get() - .toObject(), + .get(), [{ id: 1, alias: 'slim', name: 'Shady' }], ) deepEqual( @@ -627,8 +620,7 @@ await test('simulated periodic save', async (t) => { .query('person') .filter('alias', 'includes', 'slim') .include('alias', 'name') - .get() - .toObject(), + .get(), [{ id: 1, alias: 'slim', name: 'Shady' }], ) @@ -638,8 +630,7 @@ await test('simulated periodic save', async (t) => { .query('person') .filter('alias', 'includes', 'slick') .include('alias', 'name') - .get() - .toObject(), + .get(), [{ id: 6, alias: 'slick', name: 'Slide' }], ) deepEqual( @@ -647,8 +638,7 @@ await test('simulated periodic save', async (t) => { .query('person') .filter('alias', 'includes', 'slick') .include('alias', 'name') - .get() - .toObject(), + .get(), [{ id: 6, alias: 'slick', name: 'Slide' }], ) @@ -658,8 +648,7 @@ await test('simulated periodic save', async (t) => { .query('person') .filter('alias', 'includes', 'boss') .include('alias', 'name') - .get() - .toObject(), + .get(), [{ id: 5, name: 'Steve', alias: 'boss' }], ) deepEqual( @@ -667,19 +656,14 @@ await test('simulated periodic save', async (t) => { .query('person') .filter('alias', 'includes', 'boss') .include('alias', 'name') - .get() - .toObject(), + .get(), [{ id: 5, name: 'Steve', alias: 'boss' }], ) // All have the same books deepEqual( - await db2 - .query('person') - .include('name', 'alias', 'books') - .get() - .toObject(), - await db.query('person').include('name', 'alias', 'books').get().toObject(), + await db2.query('person').include('name', 'alias', 'books').get(), + await db.query('person').include('name', 'alias', 'books').get(), ) }) diff --git a/test/save/saveRange.ts b/test/save/saveRange.ts index 78c979d8b3..386415a58e 100644 --- a/test/save/saveRange.ts +++ b/test/save/saveRange.ts @@ -59,15 +59,12 @@ await test('save simple range', async (t) => { age: 1337, }) await db.drain() - deepEqual( - (await db.query('user').include('age').range(0, 1).get()).toObject(), - [ - { - id: 1, - age: 1337, - }, - ], - ) + deepEqual(await db.query('user').include('age').range(0, 1).get(), [ + { + id: 1, + age: 1337, + }, + ]) const save2_start = performance.now() await db.save() @@ -107,23 +104,18 @@ await test('save simple range', async (t) => { notEqual(firstHash, secondHash) equal(secondHash, thirdHash) + deepEqual(await newDb.query('user').include('age').range(0, 1).get(), [ + { + id: 1, + age: 1337, + }, + ]) deepEqual( - (await newDb.query('user').include('age').range(0, 1).get()).toObject(), - [ - { - id: 1, - age: 1337, - }, - ], - ) - deepEqual( - ( - await newDb - .query('user') - .include('age') - .range(200000, 200000 + 1) - .get() - ).toObject(), + await newDb + .query('user') + .include('age') + .range(200000, 200000 + 1) + .get(), [ { id: 200001, @@ -132,28 +124,23 @@ await test('save simple range', async (t) => { ], ) - deepEqual( - (await newDb.query('user').include('name').range(0, 2).get()).toObject(), - [ - { - id: 1, - name: 'mr flop 1', - }, - { - id: 2, - name: 'mr flop 2', - }, - ], - ) + deepEqual(await newDb.query('user').include('name').range(0, 2).get(), [ + { + id: 1, + name: 'mr flop 1', + }, + { + id: 2, + name: 'mr flop 2', + }, + ]) deepEqual( - ( - await newDb - .query('user') - .include('name') - .range(200_000, 200_000 + 2) - .get() - ).toObject(), + await newDb + .query('user') + .include('name') + .range(200_000, 200_000 + 2) + .get(), [ { id: 200001, diff --git a/test/sort/sort.ts b/test/sort/sort.ts index 6f9436b1bf..93b1eb35f3 100644 --- a/test/sort/sort.ts +++ b/test/sort/sort.ts @@ -58,9 +58,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - ( - await db.query('user').sort('age', 'desc').include('email', 'age').get() - ).toObject(), + await db.query('user').sort('age', 'desc').include('email', 'age').get(), [ { id: 1, email: 'blap@blap.blap.blap', age: 201 }, { id: 4, email: 'nurp@nurp.nurp.nurp', age: 200 }, @@ -72,9 +70,7 @@ await test('basic', async (t) => { ) deepEqual( - ( - await db.query('user').sort('age', 'asc').include('email', 'age').get() - ).toObject(), + await db.query('user').sort('age', 'asc').include('email', 'age').get(), [ { id: 5, email: 'z@z.z', age: 1 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -492,9 +488,7 @@ await test('sort - from start (1M items)', async (t) => { t.after(() => newDb.destroy()) deepEqual( - ( - await newDb.query('user').include('name').sort('name').range(0, 2).get() - ).toObject(), + await newDb.query('user').include('name').sort('name').range(0, 2).get(), [ { id: 3, @@ -548,7 +542,7 @@ await test('unset value on create', async (t) => { const id5 = await db.create('dialog', {}) deepEqual( - await db.query('dialog').sort('fun', 'desc').get().toObject(), + await db.query('dialog').sort('fun', 'desc').get(), [ { id: 3, @@ -574,7 +568,7 @@ await test('unset value on create', async (t) => { 'first', ) - deepEqual(await db.query('dialog').sort('fun', 'desc').get().toObject(), [ + deepEqual(await db.query('dialog').sort('fun', 'desc').get(), [ { id: 3, fun: '3' }, { id: 2, fun: '2' }, { id: 1, fun: '1' }, @@ -586,7 +580,7 @@ await test('unset value on create', async (t) => { fun: '0', }) - deepEqual(await db.query('dialog').sort('fun', 'desc').get().toObject(), [ + deepEqual(await db.query('dialog').sort('fun', 'desc').get(), [ { id: 3, fun: '3', @@ -612,7 +606,7 @@ await test('unset value on create', async (t) => { db.delete('dialog', id5) await db.drain() - deepEqual(await db.query('dialog').sort('fun', 'desc').get().toObject(), [ + deepEqual(await db.query('dialog').sort('fun', 'desc').get(), [ { id: 3, fun: '3', diff --git a/test/string.ts b/test/string.ts index 7a0a7b452b..2678339de2 100644 --- a/test/string.ts +++ b/test/string.ts @@ -46,45 +46,37 @@ await test('simple', async (t) => { await db.drain() - deepEqual( - (await db.query('user').include('name', 'snurp').get()).toObject(), - [ - { - id: 1, - snurp: 'derp derp', - name: '', - }, - ], - ) + deepEqual(await db.query('user').include('name', 'snurp').get(), [ + { + id: 1, + snurp: 'derp derp', + name: '', + }, + ]) - deepEqual( - (await db.query('user').include('name', 'snurp', 'age').get()).toObject(), - [ - { - id: 1, - age: 99, - snurp: 'derp derp', - name: '', - }, - ], - ) + deepEqual(await db.query('user').include('name', 'snurp', 'age').get(), [ + { + id: 1, + age: 99, + snurp: 'derp derp', + name: '', + }, + ]) deepEqual( - ( - await db - .query('user') - .include( - 'name', - 'snurp', - 'age', - 'email', - 'flap', - 'burp', - 'location.x', - 'location.y', - ) - .get() - ).toObject(), + await db + .query('user') + .include( + 'name', + 'snurp', + 'age', + 'email', + 'flap', + 'burp', + 'location.x', + 'location.y', + ) + .get(), [ { id: 1, @@ -99,19 +91,16 @@ await test('simple', async (t) => { ], ) - deepEqual( - (await db.query('user').include('location.label').get()).toObject(), - [ - { - id: 1, - location: { - label: 'BLA BLA', - }, + deepEqual(await db.query('user').include('location.label').get(), [ + { + id: 1, + location: { + label: 'BLA BLA', }, - ], - ) + }, + ]) - deepEqual((await db.query('user').include('location').get()).toObject(), [ + deepEqual(await db.query('user').include('location').get(), [ { id: 1, location: { @@ -122,36 +111,31 @@ await test('simple', async (t) => { }, ]) - deepEqual( - (await db.query('user').include('location', 'burp').get()).toObject(), - [ - { - id: 1, - burp: 66, - location: { - x: 0, - y: 0, - label: 'BLA BLA', - }, + deepEqual(await db.query('user').include('location', 'burp').get(), [ + { + id: 1, + burp: 66, + location: { + x: 0, + y: 0, + label: 'BLA BLA', }, - ], - ) + }, + ]) deepEqual( - ( - await db - .query('user') - .include( - 'age', - 'email', - 'flap', - 'burp', - 'location.x', - 'location.y', - 'location.label', - ) - .get() - ).toObject(), + await db + .query('user') + .include( + 'age', + 'email', + 'flap', + 'burp', + 'location.x', + 'location.y', + 'location.label', + ) + .get(), [ { id: 1, @@ -164,7 +148,7 @@ await test('simple', async (t) => { ], ) - deepEqual((await db.query('user').get()).toObject(), [ + deepEqual(await db.query('user').get(), [ { id: 1, name: '', @@ -282,13 +266,11 @@ await test('string + refs', async (t) => { await db.drain() deepEqual( - ( - await db - .query('simple') - .include('user.name', 'user.myBlup.name') - .range(0, 1) - .get() - ).toObject(), + await db + .query('simple') + .include('user.name', 'user.myBlup.name') + .range(0, 1) + .get(), [ { id: 1, @@ -311,9 +293,7 @@ await test('string + refs', async (t) => { await db.drain() deepEqual( - ( - await db.query('simple').include('user.name', 'user.myBlup.name').get() - ).toObject(), + await db.query('simple').include('user.name', 'user.myBlup.name').get(), [ { id: 1, @@ -453,7 +433,7 @@ await test('Big string', async (t) => { await db.drain() deepEqual( - (await db.query('file').get()).toObject(), + await db.query('file').get(), [ { id: 1, diff --git a/test/update.ts b/test/update.ts index 5a6325b732..10d9216e8d 100644 --- a/test/update.ts +++ b/test/update.ts @@ -23,7 +23,7 @@ await test('update with payload.id', async (t) => { body: 'xxx', }) - deepEqual(await db.query('article').get().toObject(), [ + deepEqual(await db.query('article').get(), [ { id: 1, body: 'xxx', @@ -82,7 +82,7 @@ await test('update', async (t) => { await db.drain() - deepEqual((await db.query('snurp').get()).toObject(), [ + deepEqual(await db.query('snurp').get(), [ { a: 1, b: 2, @@ -127,7 +127,7 @@ await test('update', async (t) => { await db.drain() - deepEqual((await db.query('snurp').get()).toObject(), [ + deepEqual(await db.query('snurp').get(), [ { a: 1, b: 2, @@ -156,7 +156,7 @@ await test('update', async (t) => { await db.drain() - deepEqual((await db.query('snurp', 2).get()).toObject(), { + deepEqual(await db.query('snurp', 2).get(), { a: 0, b: 0, c: 0, @@ -170,7 +170,7 @@ await test('update', async (t) => { }) // for individual queries combine them - deepEqual((await db.query('snurp', [2, 1]).get()).toObject(), [ + deepEqual(await db.query('snurp', [2, 1]).get(), [ { a: 1, b: 2, @@ -219,13 +219,11 @@ await test('update', async (t) => { equal((await db.query('snurp', ids).range(10, 110).get()).length, 100) deepEqual( - ( - await db - .query('snurp', ids) - .range(1e5, 1e5 + 2) - .sort('a', 'desc') - .get() - ).toObject(), + await db + .query('snurp', ids) + .range(1e5, 1e5 + 2) + .sort('a', 'desc') + .get(), [ { id: 900000, diff --git a/test/validation/validation.ts b/test/validation/validation.ts index 6beb4bf5f7..39a2652c9f 100644 --- a/test/validation/validation.ts +++ b/test/validation/validation.ts @@ -674,8 +674,7 @@ await test('query', async (t) => { .query('user') .filter('name', 'includes', '') .include('name') - .get() - .toObject(), + .get(), allData, 'skip empty string', ) @@ -685,8 +684,7 @@ await test('query', async (t) => { .query('user', []) .filter('name', 'includes', '') .include('name') - .get() - .toObject(), + .get(), [], 'ignore empty ids', ) @@ -696,8 +694,7 @@ await test('query', async (t) => { .query('user') .filter('friend.description.en', '=', undefined) .include('name') - .get() - .toObject(), + .get(), allData, 'skip undefined', ) @@ -837,7 +834,7 @@ await test('query - no schema', async (t) => { }, false) await db.schemaIsSet() - deepEqual(await db.query('user').get().toObject(), []) + deepEqual(await db.query('user').get(), []) }) await test('minmax', async (t) => { @@ -868,7 +865,7 @@ await test('minmax', async (t) => { number: 0.5, }) - deepEqual(await db.query('user', id).get().toObject(), { + deepEqual(await db.query('user', id).get(), { name: 'luigi', number: 0.5, id, From 75c4544c32880aa4e3817eabc767f0500e901b87 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 10 Feb 2026 18:14:02 -0300 Subject: [PATCH 225/449] added group BY references --- native/query/aggregates/aggregates.zig | 10 +- native/query/aggregates/group.zig | 24 ++- native/query/aggregates/references.zig | 29 +++- native/query/multiple.zig | 2 +- test/aggregate/deep.ts | 204 +++++++++++++------------ 5 files changed, 161 insertions(+), 108 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 7f1c8b4419..6b182e0313 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -53,23 +53,23 @@ pub inline fn aggregateProps( while (i < aggDefs.len) { const currentAggDef = utils.readNext(t.AggProp, aggDefs, &i); // utils.debugPrint("currentAggDef: {any}\n", .{currentAggDef}); - // utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); + utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); var value: []u8 = undefined; if (currentAggDef.aggFunction == t.AggFunction.count) { accumulate(currentAggDef, accumulatorProp, value, hadAccumulated, null, null); } else { if (currentAggDef.propId != t.MAIN_PROP and currentAggDef.aggFunction != t.AggFunction.cardinality) { - i += @sizeOf(t.AggProp); + i += utils.sizeOf(t.AggProp); continue; } const propSchema = Schema.getFieldSchema(typeEntry, currentAggDef.propId) catch { - i += @sizeOf(t.AggProp); + i += utils.sizeOf(t.AggProp); continue; }; if (currentAggDef.aggFunction == t.AggFunction.cardinality) { const hllValue = Selva.c.selva_fields_get_selva_string(node, propSchema) orelse null; if (hllValue == null) { - i += @sizeOf(t.AggProp); + i += utils.sizeOf(t.AggProp); continue; } if (!hadAccumulated.*) { @@ -112,7 +112,7 @@ pub inline fn accumulate( switch (aggFunction) { .sum => { writeAs(f64, accumulatorProp, read(f64, accumulatorProp, accumulatorPos) + microbufferToF64(propTypeTag, value, start), accumulatorPos); - // utils.debugPrint("❤️ v: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); + utils.debugPrint("❤️ v: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); }, .avg => { const val = microbufferToF64(propTypeTag, value, start); diff --git a/native/query/aggregates/group.zig b/native/query/aggregates/group.zig index 9e296a74e4..18bb7c195f 100644 --- a/native/query/aggregates/group.zig +++ b/native/query/aggregates/group.zig @@ -90,7 +90,7 @@ inline fn aggregatePropsWithGroupBy( var keyValue: []u8 = undefined; const propSchema = Schema.getFieldSchema(typeEntry, currentKeyPropDef.propId) catch { - i += @sizeOf(t.GroupByKeyProp); + i += utils.sizeOf(t.GroupByKeyProp); return; }; @@ -133,3 +133,25 @@ pub inline fn finalizeGroupResults( try Aggregates.finalizeResults(ctx, aggDefs, accumulatorProp, header.isSamplingSet, @bitSizeOf(t.GroupByKeyProp) / 8); } } + +pub inline fn finalizeRefsGroupResults( + ctx: *Query.QueryCtx, + groupByHashMap: *GroupByHashMap, + header: t.AggRefsHeader, + aggDefs: []u8, +) !void { + var it = groupByHashMap.iterator(); + + while (it.next()) |entry| { + const key = entry.key_ptr.*; + const keyLen: u16 = @intCast(key.len); + if (key.len > 0) { + try ctx.thread.query.append(keyLen); + try ctx.thread.query.append(key); + } + + const accumulatorProp = entry.value_ptr.*; + + try Aggregates.finalizeResults(ctx, aggDefs, accumulatorProp, header.isSamplingSet, @bitSizeOf(t.GroupByKeyProp) / 8); + } +} diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index 22736c56f6..9c6f603b75 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -8,6 +8,8 @@ const utils = @import("../../utils.zig"); const t = @import("../../types.zig"); const Aggregates = @import("./aggregates.zig"); const References = @import("../../selva/references.zig"); +const GroupBy = @import("./group.zig"); +const GroupByHashMap = @import("./hashMap.zig").GroupByHashMap; const errors = @import("../../errors.zig"); const accumulate = Aggregates.accumulate; @@ -18,19 +20,32 @@ pub inline fn aggregateRefsProps( fromType: Selva.Type, i: *usize, ) !void { - utils.debugPrint("i: {d}\n", .{i.*}); + // utils.debugPrint("i: {d}\n", .{i.*}); const header = utils.readNext(t.AggRefsHeader, q, i); - // utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); + utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); const accumulatorProp = try ctx.db.allocator.alloc(u8, header.accumulatorSize); @memset(accumulatorProp, 0); defer ctx.db.allocator.free(accumulatorProp); + // filter + var it = try References.iterator(false, false, ctx.db, from, header.targetProp, fromType); - _ = try Aggregates.iterator(ctx, &it, 1000, false, undefined, q[i.*..], accumulatorProp, it.dstType, undefined); // TODO: hllAcc + if (header.hasGroupBy) { + var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); + defer groupByHashMap.deinit(); + const ct = try GroupBy.iterator(ctx, &groupByHashMap, &it, 1000, false, undefined, q[i.*..], header.accumulatorSize, it.dstType, undefined); // TODO: hllAcc + + try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); + try ctx.thread.query.append(header.targetProp); + try ctx.thread.query.append(@as(u32, ct * utils.sizeOf(t.AggProp) + utils.sizeOf(t.AggRefsHeader) + header.filterSize)); + try GroupBy.finalizeRefsGroupResults(ctx, &groupByHashMap, header, q[i.*..]); + } else { + _ = try Aggregates.iterator(ctx, &it, 1000, false, undefined, q[i.*..], accumulatorProp, it.dstType, undefined); // TODO: hllAcc - try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); - try ctx.thread.query.append(header.targetProp); - try ctx.thread.query.append(@as(u32, header.resultsSize)); - try Aggregates.finalizeResults(ctx, q[i.*..], accumulatorProp, header.isSamplingSet, 0); + try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); + try ctx.thread.query.append(header.targetProp); + try ctx.thread.query.append(@as(u32, header.resultsSize)); // MV: recheck + try Aggregates.finalizeResults(ctx, q[i.*..], accumulatorProp, header.isSamplingSet, 0); + } } diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 670a464968..a44be1e8ed 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -33,7 +33,7 @@ fn iterator( filter = utils.sliceNext(header.filterSize, q, i); try Filter.prepare(filter, ctx, typeEntry); } - // utils.debugPrint("i.* .. i.* + header.includeSize: {d} .. {d}\n", .{ i.*, i.* + header.includeSize }); + // utils.debugPrint("i.* .. i.* + header.includeSize: {d} .. {d} = {d}\n", .{ i.*, i.* + header.includeSize, header.includeSize }); const nestedQuery = q[i.* .. i.* + header.includeSize]; while (offset > 0) { const node = it.next() orelse return 0; diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index d1a0217bdc..b71e5908a8 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -8,7 +8,7 @@ import test from '../shared/test.js' import { throws, deepEqual } from '../shared/assert.js' import { fastPrng } from '../../src/utils/index.js' -await test.skip('sum branched includes', async (t) => { +await test('sum branched includes', async (t) => { const db = new BasedDb({ path: t.tmp, maxModifySize: 1e6, @@ -60,7 +60,8 @@ await test.skip('sum branched includes', async (t) => { country: 'aa', AU: 15, }) - const s = db.create('sequence', { votes: [nl1, nl2, au1] }) + db.drain() + const s = db.create('sequence', { votes: [nl1, nl2, au1] }) // create multiple with an array is not implemented yet deepEqual( await db @@ -94,20 +95,20 @@ await test.skip('sum branched includes', async (t) => { 'branched include, references, groupBy', ) - deepEqual( - await db - .query('sequence') - .include((select) => { - select('votes').filter('country', '=', 'aa').sum('NL', 'AU') - }) - .get() - .toObject(), - [{ id: 1, votes: { NL: { sum: 20 }, AU: { sum: 15 } } }], - 'branched include, references, filtered, groupBy', - ) + // deepEqual( + // await db + // .query('sequence') + // .include((select) => { + // select('votes').filter('country', '=', 'aa').sum('NL', 'AU') // string filter not implemented and also filter in refs group not implemented + // }) + // .get() + // .toObject(), + // [{ id: 1, votes: { NL: { sum: 20 }, AU: { sum: 15 } } }], + // 'branched include, references, filtered, groupBy', + // ) }) -await test.skip('count branched includes', async (t) => { +await test('count branched includes', async (t) => { const db = new BasedDb({ path: t.tmp, maxModifySize: 1e6, @@ -159,6 +160,7 @@ await test.skip('count branched includes', async (t) => { country: 'aa', AU: 15, }) + db.drain() const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( @@ -193,20 +195,20 @@ await test.skip('count branched includes', async (t) => { 'branched include, references, groupBy', ) - deepEqual( - await db - .query('sequence') - .include((select) => { - select('votes').filter('country', '=', 'aa').count() - }) - .get() - .toObject(), - [{ id: 1, votes: { count: 2 } }], - 'count, branched include, references, filtered', - ) + // deepEqual( + // await db + // .query('sequence') + // .include((select) => { + // select('votes').filter('country', '=', 'aa').count() // string filter not implemented and also filter in refs group not implemented + // }) + // .get() + // .toObject(), + // [{ id: 1, votes: { count: 2 } }], + // 'count, branched include, references, filtered', + // ) }) -await test.skip('agg on references', async (t) => { +await test('agg on references', async (t) => { const db = new BasedDb({ path: t.tmp, maxModifySize: 1e6, @@ -275,6 +277,8 @@ await test.skip('agg on references', async (t) => { gamesPlayed: 9, }) + db.drain() + const t1 = db.create('team', { teamName: 'Grêmio', city: 'Porto Alegre', @@ -309,45 +313,46 @@ await test.skip('agg on references', async (t) => { select('players').groupBy('position').sum('goalsScored', 'gamesPlayed') }) .get() + result.inspect() - deepEqual( - result.toObject(), - [ - { - id: 1, - teamName: 'Grêmio', - city: 'Porto Alegre', - players: { - Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) - Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) - }, - }, - { - id: 2, - teamName: 'Ajax', - city: 'Amsterdam', - players: { - Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) - Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) - }, - }, - { - id: 3, - teamName: 'Boca Juniors', - city: 'Buenos Aires', - players: {}, // does anybody wants to play for Boca? - }, - { - id: 4, - teamName: 'Barcelona', - city: 'Barcelona', - players: { - Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski - }, - }, - ], - 'Include parent props, with referenced items grouped by their own prop, and aggregations', - ) + // deepEqual( + // result.toObject(), + // [ + // { + // id: 1, + // teamName: 'Grêmio', + // city: 'Porto Alegre', + // players: { + // Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) + // Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) + // }, + // }, + // { + // id: 2, + // teamName: 'Ajax', + // city: 'Amsterdam', + // players: { + // Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) + // Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) + // }, + // }, + // { + // id: 3, + // teamName: 'Boca Juniors', + // city: 'Buenos Aires', + // players: {}, // does anybody wants to play for Boca? + // }, + // { + // id: 4, + // teamName: 'Barcelona', + // city: 'Barcelona', + // players: { + // Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski + // }, + // }, + // ], + // 'Include parent props, with referenced items grouped by their own prop, and aggregations', + // ) }) await test('enums', async (t) => { @@ -397,7 +402,7 @@ await test('enums', async (t) => { }) deepEqual( - await db.query('beer').avg('price').groupBy('type').get(), + await db.query('beer').avg('price').groupBy('type').get().toObject(), { Tripel: { price: { avg: 11.85 }, @@ -410,7 +415,12 @@ await test('enums', async (t) => { ) deepEqual( - await db.query('beer').harmonicMean('price').groupBy('type').get(), + await db + .query('beer') + .harmonicMean('price') + .groupBy('type') + .get() + .toObject(), { Tripel: { price: { hmean: 11.839662447257384 }, @@ -469,7 +479,8 @@ await test.skip('refs with enums ', async (t) => { await db .query('actor') .include((q) => q('movies').groupBy('genre').count()) - .get(), + .get() + .toObject(), [ { id: 1, @@ -560,13 +571,13 @@ await test('cardinality', async (t) => { }) deepEqual( - await db.query('lunch').cardinality('Mon').get(), + await db.query('lunch').cardinality('Mon').get().toObject(), { Mon: { cardinality: 7 } }, 'main cardinality no group by', ) deepEqual( - await db.query('lunch').cardinality('Mon').groupBy('week').get(), + await db.query('lunch').cardinality('Mon').groupBy('week').get().toObject(), { 27: { Mon: { cardinality: 5 }, @@ -676,6 +687,7 @@ await test('group by reference ids', async (t) => { model: 'VW Beatle', year: 1989, }) + db.drain() const t1 = db.create('trip', { distance: 523.1, vehicle: v2, @@ -689,7 +701,7 @@ await test('group by reference ids', async (t) => { }) deepEqual( - await db.query('driver').sum('rank').groupBy('vehicle').get(), + await db.query('driver').sum('rank').groupBy('vehicle').get().toObject(), { 2: { rank: { sum: 5 }, @@ -698,26 +710,27 @@ await test('group by reference ids', async (t) => { 'group by reference id', ) - // deepEqual( - // await db - // .query('driver') - // .include((q) => q('trips').groupBy('vehicle').max('distance')) - // .include('*') - // .get(), - // [ - // { - // id: 1, - // rank: 5, - // name: 'Luc Ferry', - // trips: { - // 2: { - // distance: { max: 523.1 }, - // }, - // }, - // }, - // ], - // 'brached query with nested group by reference id', - // ) + deepEqual( + await db + .query('driver') + .include((q) => q('trips').groupBy('vehicle').max('distance')) + .include('*') + .get() + .toObject(), + [ + { + id: 1, + rank: 5, + name: 'Luc Ferry', + trips: { + 2: { + distance: { max: 523.1 }, + }, + }, + }, + ], + 'branched query with nested group by reference id', + ) }) await test.skip('nested references', async (t) => { @@ -763,7 +776,7 @@ await test.skip('nested references', async (t) => { // await db.query('user').include('*', '**').get().inspect(10) deepEqual( - await db.query('user').sum('friends.strong').get(), + await db.query('user').sum('friends.strong').get().toObject(), { strong: { sum: 7, @@ -883,7 +896,8 @@ await test.skip('edges aggregation', async (t) => { await db .query('movie') .include((q) => q('actors').max('$rating')) - .get(), + .get() + .toObject(), [ { id: 1, @@ -909,7 +923,8 @@ await test.skip('edges aggregation', async (t) => { await db .query('movie') .include((q) => q('actors').max('$rating').sum('$hating')) - .get(), + .get() + .toObject(), [ { id: 1, @@ -941,7 +956,8 @@ await test.skip('edges aggregation', async (t) => { await db .query('movie') .include((q) => q('actors').max('$rating', '$hating')) - .get(), + .get() + .toObject(), [ { id: 1, From cf6542fdaf3063372a63adcf7270bec23846131a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 11 Feb 2026 12:43:58 +0100 Subject: [PATCH 226/449] Lock selva schemat at startup Ref FDN-1880 --- clibs/include/selva/db.h | 12 +-- clibs/lib/selva/db.c | 168 +++++++++++++++++++------------- clibs/lib/selva/include/io.h | 1 + clibs/lib/selva/io/dump.c | 57 ----------- native/db/lifeTime.zig | 12 ++- native/thread/worker/worker.zig | 23 ----- native/types.zig | 2 - src/db-server/index.ts | 18 ++-- src/db-server/migrate/index.ts | 3 +- src/db-server/schema.ts | 158 ++++++++---------------------- src/db-server/start.ts | 66 +++++++------ src/index.ts | 1 - src/native.ts | 4 +- src/zigTsExports.ts | 6 -- 14 files changed, 204 insertions(+), 327 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 612d72b94e..4820737ebf 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -51,7 +51,7 @@ struct selva_dump_common_data { * Create a new DB instance. */ SELVA_EXPORT -struct SelvaDb *selva_db_create(void); +struct SelvaDb *selva_db_create(size_t len, uint8_t schema[len]); /** * Destroy a DB instance. @@ -65,13 +65,6 @@ void selva_db_destroy(struct SelvaDb *db) __attribute__((nonnull)); SELVA_EXPORT int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname_len) __attribute__((nonnull)); -/** - * Create a new node type with a schema. - * @param type must not exist before. - */ -SELVA_EXPORT -int selva_db_create_type(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) __attribute__((nonnull)); - /** * Save the common/shared data of the database. */ @@ -90,6 +83,9 @@ int selva_dump_load_common(struct SelvaDb *db, struct selva_dump_common_data *co SELVA_EXPORT int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block_i, char *errlog_buf, size_t errlog_size) __attribute__((nonnull)); +SELVA_EXPORT +node_type_t selva_get_max_type(const struct SelvaDb *db) __attribute__((nonnull)); + /** * Find a type by type id. */ diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 5884eda58c..d6da229103 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -165,7 +165,78 @@ static uint32_t te_slab_size(void) return slab_size; } -struct SelvaDb *selva_db_create(void) +static bool eq_type_exists(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) +{ + struct SelvaTypeEntry *te; + + te = selva_get_type_by_index(db, type); + return (te && te->schema_len == schema_len && !memcmp(te->schema_buf, schema_buf, schema_len)); +} + +static void clone_schema_buf(struct SelvaTypeEntry *te, const uint8_t *schema_buf, size_t schema_len) +{ + te->schema_buf = selva_malloc(schema_len); + memcpy(te->schema_buf, schema_buf, schema_len); + te->schema_len = schema_len; +} + +static int selva_db_create_type(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) +{ + struct schema_info nfo; + int err; + + if (eq_type_exists(db, type, schema_buf, schema_len)) { + return SELVA_EEXIST; + } + + err = schemabuf_get_info(&nfo, schema_buf, schema_len); + if (err) { + return err; + } + + if (nfo.block_capacity == 0) { + return SELVA_EINVAL; + } + + if (nfo.nr_fields > SELVA_FIELDS_MAX) { + /* schema too large. */ + return SELVA_ENOBUFS; + } + + struct SelvaTypeEntry *te = mempool_get(&db->types.pool); + +#if 0 + fprintf(stderr, "schema_buf: [ "); + for (size_t i = 0; i < schema_len; i++) { + fprintf(stderr, "%x, ", schema_buf[i]); + } + fprintf(stderr, "]\n"); +#endif + + memset(te, 0, sizeof(*te)); + te->type = type; + err = schemabuf_parse_ns(&te->ns, schema_buf, schema_len, db->sdb_version ?: SELVA_SDB_VERSION); + if (err) { + mempool_return(&db->types.pool, te); + return err; + } + + clone_schema_buf(te, schema_buf, schema_len); + te->blocks = alloc_blocks(nfo.block_capacity); + selva_init_aliases(te); + colvec_init_te(te); + + const size_t node_size = sizeof_wflex(struct SelvaNode, fields.fields_map, nfo.nr_fields); + mempool_init2(&te->nodepool, NODEPOOL_SLAB_SIZE, node_size, alignof(size_t), MEMPOOL_ADV_RANDOM | MEMPOOL_ADV_HP_SOFT); + + if (RB_INSERT(SelvaTypeEntryIndex, &db->types.index, te)) { + db_panic("Schema update not supported"); + } + db->types.count++; + return 0; +} + +struct SelvaDb *selva_db_create(size_t len, uint8_t schema[len]) { struct SelvaDb *db = selva_calloc(1, sizeof(*db)); @@ -175,7 +246,31 @@ struct SelvaDb *selva_db_create(void) db->dirfd = AT_FDCWD; selva_expire_init(&db->expiring); + for (size_t i = 0; i < len;) { + struct { + node_type_t type; + uint32_t len; + } __packed desc; + int err; + + if (unlikely(len - i < sizeof(desc))) { + fprintf(stderr, "%s schema too short\n", __func__); + goto fail; + } + memcpy(&desc, schema + i, sizeof(desc)); + + err = selva_db_create_type(db, desc.type, schema + i + sizeof(desc), desc.len); + i += sizeof(desc) + desc.len; + if (err) { + fprintf(stderr, "%s failed to create type %u: %s\n", __func__, desc.type, selva_strerror(err)); + goto fail; + } + } + return db; +fail: + selva_db_destroy(db); + return nullptr; } int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname_len) @@ -291,14 +386,6 @@ void selva_db_destroy(struct SelvaDb *db) selva_free(db); } -static bool eq_type_exists(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) -{ - struct SelvaTypeEntry *te; - - te = selva_get_type_by_index(db, type); - return (te && te->schema_len == schema_len && !memcmp(te->schema_buf, schema_buf, schema_len)); -} - /** * Alloc .blocks in a type entry. */ @@ -353,67 +440,12 @@ void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, } } -static void clone_schema_buf(struct SelvaTypeEntry *te, const uint8_t *schema_buf, size_t schema_len) -{ - te->schema_buf = selva_malloc(schema_len); - memcpy(te->schema_buf, schema_buf, schema_len); - te->schema_len = schema_len; -} - -int selva_db_create_type(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) +node_type_t selva_get_max_type(const struct SelvaDb *db) { - struct schema_info nfo; - int err; - - if (eq_type_exists(db, type, schema_buf, schema_len)) { - return SELVA_EEXIST; - } - - err = schemabuf_get_info(&nfo, schema_buf, schema_len); - if (err) { - return err; - } - - if (nfo.block_capacity == 0) { - return SELVA_EINVAL; - } - - if (nfo.nr_fields > SELVA_FIELDS_MAX) { - /* schema too large. */ - return SELVA_ENOBUFS; - } - - struct SelvaTypeEntry *te = mempool_get(&db->types.pool); - -#if 0 - fprintf(stderr, "schema_buf: [ "); - for (size_t i = 0; i < schema_len; i++) { - fprintf(stderr, "%x, ", schema_buf[i]); - } - fprintf(stderr, "]\n"); -#endif - - memset(te, 0, sizeof(*te)); - te->type = type; - err = schemabuf_parse_ns(&te->ns, schema_buf, schema_len, db->sdb_version ?: SELVA_SDB_VERSION); - if (err) { - mempool_return(&db->types.pool, te); - return err; - } - - clone_schema_buf(te, schema_buf, schema_len); - te->blocks = alloc_blocks(nfo.block_capacity); - selva_init_aliases(te); - colvec_init_te(te); - - const size_t node_size = sizeof_wflex(struct SelvaNode, fields.fields_map, nfo.nr_fields); - mempool_init2(&te->nodepool, NODEPOOL_SLAB_SIZE, node_size, alignof(size_t), MEMPOOL_ADV_RANDOM | MEMPOOL_ADV_HP_SOFT); + struct SelvaTypeEntry *te; - if (RB_INSERT(SelvaTypeEntryIndex, &db->types.index, te)) { - db_panic("Schema update not supported"); - } - db->types.count++; - return 0; + te = RB_MAX(SelvaTypeEntryIndex, (typeof_unqual(db->types.index) *)&db->types.index); + return te ? te->type : 0; } struct SelvaTypeEntry *selva_get_type_by_index(const struct SelvaDb *db, node_type_t type) diff --git a/clibs/lib/selva/include/io.h b/clibs/lib/selva/include/io.h index 39b19f401b..04570631e9 100644 --- a/clibs/lib/selva/include/io.h +++ b/clibs/lib/selva/include/io.h @@ -40,6 +40,7 @@ * - Save block writelog in common.sdb * - Remove support for earlier SDB versions * - Moved aliases saving from each node to saving all aliases at once + * - Removed schema from common.sdb */ #define SELVA_SDB_VERSION 8 diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 0570f6c42b..6be551bff4 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -37,7 +37,6 @@ /* * Pick 32-bit primes for these. */ -#define DUMP_MAGIC_SCHEMA 3360690301 /* common.sdb */ #define DUMP_MAGIC_EXPIRE 2147483647 /* common.sdb */ #define DUMP_MAGIC_ALIASES 4019181209 /* common.sdb */ #define DUMP_MAGIC_COMMON_IDS 2974848157 /* common.sdb */ @@ -263,24 +262,6 @@ static void save_node(struct selva_io *io, struct SelvaDb *db, struct SelvaNode save_node_fields(io, schema, node); } -static void save_schema(struct selva_io *io, struct SelvaDb *db) -{ - const sdb_nr_types_t nr_types = db->types.count; - struct SelvaTypeEntry *te; - - write_dump_magic(io, DUMP_MAGIC_SCHEMA); - io->sdb_write(&nr_types, sizeof(nr_types), 1, io); - - RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { - node_type_t type = te->type; - const sdb_arr_len_t schema_len = te->schema_len; - - io->sdb_write(&type, sizeof(type), 1, io); - io->sdb_write(&schema_len, sizeof(schema_len), 1, io); - io->sdb_write(te->schema_buf, sizeof(te->schema_buf[0]), te->schema_len, io); - } -} - static void save_expire(struct selva_io *io, struct SelvaDb *db) { @@ -386,7 +367,6 @@ int selva_dump_save_common(struct SelvaDb *db, struct selva_dump_common_data *co /* * Save all the common data here that can't be split up. */ - save_schema(&io, db); save_expire(&io, db); save_aliases(&io, db); save_common_ids(&io, com->ids_data, com->ids_len); @@ -522,42 +502,6 @@ int selva_dump_save_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i return err; } -__attribute__((warn_unused_result)) -static int load_schema(struct selva_io *io, struct SelvaDb *db) -{ - sdb_nr_types_t nr_types; - - if (!read_dump_magic(io, DUMP_MAGIC_SCHEMA)) { - selva_io_errlog(io, "Invalid schema magic"); - return SELVA_EINVAL; - } - - if (io->sdb_read(&nr_types, sizeof(nr_types), 1, io) != 1) { - selva_io_errlog(io, "nr_types schema"); - return SELVA_EINVAL; - } - - for (size_t i = 0; i < nr_types; i++) { - node_type_t type; - __selva_autofree uint8_t *schema_buf; - sdb_arr_len_t schema_len; - int err; - - io->sdb_read(&type, sizeof(type), 1, io); - io->sdb_read(&schema_len, sizeof(schema_len), 1, io); - schema_buf = selva_malloc(schema_len); - io->sdb_read(schema_buf, sizeof(schema_buf[0]), schema_len, io); - - err = selva_db_create_type(db, type, schema_buf, schema_len); - if (err) { - selva_io_errlog(io, "Failed to create a node type entry: %s", selva_strerror(err)); - return SELVA_EINVAL; - } - } - - return 0; -} - __attribute__((warn_unused_result)) static int load_string(struct selva_io *io, struct selva_string *s, const struct sdb_string_meta *meta) { @@ -1016,7 +960,6 @@ int selva_dump_load_common(struct SelvaDb *db, struct selva_dump_common_data *co db->sdb_version = io.sdb_version; - err = load_schema(&io, db); err = err ?: load_expire(&io, db); err = err ?: load_aliases(&io, db); err = err ?: load_common_ids(&io, com); diff --git a/native/db/lifeTime.zig b/native/db/lifeTime.zig index 4405e20b97..b44172a812 100644 --- a/native/db/lifeTime.zig +++ b/native/db/lifeTime.zig @@ -3,6 +3,7 @@ const std = @import("std"); const napi = @import("../napi.zig"); const dump = @import("../selva/dump.zig"); const selva = @import("../selva/selva.zig").c; +const jemalloc = @import("../jemalloc.zig"); const dbCtx = @import("ctx.zig"); pub fn start(env: napi.Env, info: napi.Info) callconv(.c) napi.Value { @@ -20,12 +21,19 @@ pub fn stop(napi_env: napi.Env, info: napi.Info) callconv(.c) napi.Value { fn startInternal(env: napi.Env, info: napi.Info) !napi.Value { // does this make double things with valgrind? Ask marco dbCtx.init(); - const args = try napi.getArgs(3, env, info); + const args = try napi.getArgs(4, env, info); const fsPath = try napi.get([]u8, env, args[1]); const nrThreads = try napi.get(u16, env, args[2]); const ctx = try dbCtx.createDbCtx(env, args[0], fsPath, nrThreads); - ctx.selva = selva.selva_db_create(); + const selvaSchema = try napi.get([]u8, env, args[3]); + + ctx.selva = selva.selva_db_create(selvaSchema.len, selvaSchema.ptr); + if (ctx.selva == null) { + return errors.jsThrow(env, "Failed to create a db"); + } _ = selva.selva_db_chdir(ctx.selva, fsPath.ptr, fsPath.len); // TODO Handle error? + ctx.ids = jemalloc.alloc(u32, selva.selva_get_max_type(ctx.selva)); + var externalNapi: napi.Value = undefined; ctx.initialized = true; _ = napi.c.napi_create_external(env, ctx, null, null, &externalNapi); diff --git a/native/thread/worker/worker.zig b/native/thread/worker/worker.zig index 1373ba4d2a..3d55094ce3 100644 --- a/native/thread/worker/worker.zig +++ b/native/thread/worker/worker.zig @@ -112,33 +112,10 @@ pub fn worker(threads: *Thread.Threads, thread: *common.Thread) !void { .loadBlock => try dump.loadBlock(thread, threads.ctx, m, op), .unloadBlock => try dump.unloadBlock(thread, threads.ctx, m, op), .loadCommon => try dump.loadCommon(thread, threads.ctx, m, op), - - .createType => { - const typeCode = utils.read(u32, m, 0); - const resp = try thread.modify.result(4, typeCode, op); - const schema = m[5..m.len]; - const err = selva.selva_db_create_type( - threads.ctx.selva, - @truncate(typeCode), - schema.ptr, - schema.len, - ); - utils.write(resp, err, 0); - }, // .subscribe => { // _ = try thread.modify.result(0, utils.read(u32, m, 0), op); // }, // .unsubscribe => try Subscription.unsubscribe(threads.ctx, m, thread), - .setSchemaIds => { - _ = try thread.modify.result(0, utils.read(u32, m, 0), op); - if (threads.ctx.ids.len > 0) { - jemalloc.free(threads.ctx.ids); - threads.ctx.ids = &[_]u32{}; - } - threads.ctx.ids = jemalloc.alloc(u32, (m.len - 5) / @sizeOf(u32)); - const ids = m[5..m.len]; - utils.byteCopy(threads.ctx.ids, ids, 0); - }, else => {}, } // this is not always nessecary e.g. subscribe does not need this diff --git a/native/types.zig b/native/types.zig index a2a9a9b6ac..4ef91e628f 100644 --- a/native/types.zig +++ b/native/types.zig @@ -38,8 +38,6 @@ pub const OpType = enum(u8) { loadBlock = 128, unloadBlock = 129, loadCommon = 130, - createType = 131, - setSchemaIds = 132, emptyMod = 133, // -------------------- diff --git a/src/db-server/index.ts b/src/db-server/index.ts index 0dd287b29c..1def58296f 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -1,17 +1,13 @@ import native from '../native.js' import { rm } from 'node:fs/promises' -import { start, StartOpts } from './start.js' +import { realStart, start, StartOpts } from './start.js' import { migrate } from './migrate/index.js' import { debugServer } from '../utils/debug.js' import { DbShared } from '../shared/DbBase.js' -import { - setNativeSchema, - setSchemaOnServer, - writeSchemaFile, -} from './schema.js' +import { writeSchemaFile, } from './schema.js' import { save, SaveOpts } from './blocks.js' -import { OpType, OpTypeEnum, OpTypeInverse } from '../zigTsExports.js' +import { OpType, OpTypeEnum } from '../zigTsExports.js' import { MAX_ID, type SchemaMigrateFns, @@ -203,7 +199,7 @@ export class DbServer extends DbShared { schema: SchemaOut, transformFns?: SchemaMigrateFns, ): Promise { - if (this.stopped || !this.dbCtxExternal) { + if (this.stopped) { throw new Error('Db is stopped') } @@ -220,8 +216,10 @@ export class DbServer extends DbShared { await migrate(this, this.schema, schema, transformFns) return this.schema.hash } - setSchemaOnServer(this, schema) - await setNativeSchema(this, schema) + if (this.dbCtxExternal) { + throw new Error('Db is already running') + } + realStart(this, schema) await writeSchemaFile(this, schema) process.nextTick(() => { diff --git a/src/db-server/migrate/index.ts b/src/db-server/migrate/index.ts index 329bab2c00..f6330c8db6 100644 --- a/src/db-server/migrate/index.ts +++ b/src/db-server/migrate/index.ts @@ -5,7 +5,6 @@ import native from '../../native.js' import { DbServer } from '../index.js' import { fileURLToPath } from 'url' import { - setNativeSchema, setSchemaOnServer, writeSchemaFile, } from '../schema.js' @@ -123,7 +122,7 @@ export const migrate = async ( } setSchemaOnServer(tmpDb.server, toSchema) - await setNativeSchema(tmpDb.server, toSchema) + //await setNativeSchema(tmpDb.server, toSchema) if (abort()) { await tmpDb.destroy() diff --git a/src/db-server/schema.ts b/src/db-server/schema.ts index edb92a79e6..5e8b532b8d 100644 --- a/src/db-server/schema.ts +++ b/src/db-server/schema.ts @@ -1,17 +1,12 @@ import { DbServer } from './index.js' import { join } from 'node:path' import { writeFile } from 'node:fs/promises' -import native, { idGenerator } from '../native.js' -import { readUint32, writeUint32 } from '../utils/index.js' import { LangCode, Modify, - OpType, - PropType, PropTypeSelva, pushSelvaSchemaHeader, pushSelvaSchemaMicroBuffer, - type PropTypeEnum, } from '../zigTsExports.js' import { BLOCK_CAPACITY_DEFAULT, @@ -22,26 +17,6 @@ import { import { SCHEMA_FILE } from '../index.js' import { getTypeDefs, propIndexOffset } from '../schema/defs/getTypeDefs.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' -import { TypeDef } from '../schema/defs/index.js' - -const schemaOpId = idGenerator() - -function setSchemaIds(db: DbServer, ids: Uint32Array): Promise { - const id = schemaOpId.next().value - - const msg = new Uint8Array(5 + ids.byteLength) - - writeUint32(msg, id, 0) - msg[4] = OpType.setSchemaIds - msg.set(new Uint8Array(ids.buffer, ids.byteOffset), 5) - - return new Promise((resolve) => { - db.addOpOnceListener(OpType.setSchemaIds, id, () => { - resolve() - }) - native.modify(msg, db.dbCtxExternal) - }) -} export const setSchemaOnServer = async ( server: DbServer, @@ -64,105 +39,58 @@ export const writeSchemaFile = async (server: DbServer, schema: SchemaOut) => { } } -export async function createSelvaType( - server: DbServer, - typeDef: TypeDef, - schema: Uint8Array, -): Promise { - const msg = new Uint8Array(5 + schema.byteLength) - - writeUint32(msg, typeDef.id, 0) +export const makeNativeSchema = (schema: SchemaOut): Uint8Array => { + const buf = new AutoSizedUint8Array() + const typeDefs = getTypeDefs(schema) - msg[4] = OpType.createType - msg.set(schema, 5) - return new Promise((resolve, reject) => { - server.addOpOnceListener( - OpType.createType, - typeDef.id, - (buf: Uint8Array) => { - const err = readUint32(buf, 0) - if (err) { - const errMsg = `Create type '${typeDef.name}' (${typeDef.id}) failed: ${native.selvaStrerror(err)}` - server.emit('error', errMsg) - reject(new Error(errMsg)) - } else { - resolve() - } - server.keepRefAliveTillThisPoint(msg) - }, - ) - native.modify(msg, server.dbCtxExternal) - }) -} + for (const typeDef of typeDefs.values()) { + let nrFixedFields = 1 + let nrVirtualFields = 0 -const supportedDefaults = new Set([ - PropType.binary, - PropType.string, - PropType.text, - PropType.vector, - PropType.json, // same as binary (Uint8Array) -]) -/** - * Set schema used in native code. - * This function should be only called when a new schema is set to an empty DB - * instance. If a `common.sdb` file is loaded then calling this function isn't - * necessary because `common.sdb` already contains the required schema. - */ -export const setNativeSchema = async (server: DbServer, schema: SchemaOut) => { - const typeDefs = getTypeDefs(schema) - let maxTypeId = 0 - await Promise.all( - typeDefs.values().map((typeDef) => { - const buf = new AutoSizedUint8Array() - maxTypeId = Math.max(maxTypeId, typeDef.id) - let nrFixedFields = 1 - let nrVirtualFields = 0 + buf.pushUint16(typeDef.id) + const typeLenIndex = buf.reserveUint32() + const startIndex = buf.length - for (const prop of typeDef.separate) { - const offset = propIndexOffset(prop) - if (offset < 0) { - nrFixedFields++ - } else if (offset > 0) { - nrVirtualFields++ - } + for (const prop of typeDef.separate) { + const offset = propIndexOffset(prop) + if (offset < 0) { + nrFixedFields++ + } else if (offset > 0) { + nrVirtualFields++ } + } - pushSelvaSchemaHeader(buf, { - blockCapacity: typeDef.schema.blockCapacity || BLOCK_CAPACITY_DEFAULT, - nrFields: 1 + typeDef.separate.length, - nrFixedFields, - nrVirtualFields, - sdbVersion: 8, - }) - - // handle main - const mainLen = typeDef.main.reduce((len, { size }) => len + size, 0) - pushSelvaSchemaMicroBuffer(buf, { - type: PropTypeSelva.microBuffer, - len: mainLen, - hasDefault: 1, - }) + pushSelvaSchemaHeader(buf, { + blockCapacity: typeDef.schema.blockCapacity || BLOCK_CAPACITY_DEFAULT, + nrFields: 1 + typeDef.separate.length, + nrFixedFields, + nrVirtualFields, + sdbVersion: 8, + }) - for (const prop of typeDef.main) { - if ('default' in prop.schema && prop.schema.default) { - prop.pushValue(buf, prop.schema.default, Modify.create, LangCode.none) - } else { - buf.fill(0, buf.length, buf.length + prop.size) - } - } + // handle main + const mainLen = typeDef.main.reduce((len, { size }) => len + size, 0) + pushSelvaSchemaMicroBuffer(buf, { + type: PropTypeSelva.microBuffer, + len: mainLen, + hasDefault: 1, + }) - // handle separate - for (const prop of typeDef.separate) { - prop.pushSelvaSchema(buf) + for (const prop of typeDef.main) { + if ('default' in prop.schema && prop.schema.default) { + prop.pushValue(buf, prop.schema.default, Modify.create, LangCode.none) + } else { + buf.fill(0, buf.length, buf.length + prop.size) } + } - return createSelvaType(server, typeDef, buf.view) - }), - ) - - await setSchemaIds(server, new Uint32Array(maxTypeId)) + // handle separate + for (const prop of typeDef.separate) { + prop.pushSelvaSchema(buf) + } - if (server.fileSystemPath) { - server.save({ skipDirtyCheck: true }).catch(console.error) + buf.writeUint32(buf.length - startIndex, typeLenIndex) } + + return buf.view } diff --git a/src/db-server/start.ts b/src/db-server/start.ts index 6cdbc5ae5e..a25e70bf8d 100644 --- a/src/db-server/start.ts +++ b/src/db-server/start.ts @@ -5,14 +5,15 @@ import { rm, mkdir, readFile } from 'node:fs/promises' import { join } from 'node:path' import { loadCommon, } from './blocks.js' import { readUint32, wait } from '../utils/index.js' -import { setSchemaOnServer } from './schema.js' +import { setSchemaOnServer, makeNativeSchema } from './schema.js' import { OpTypeEnum, BridgeResponseEnum, BridgeResponse, } from '../zigTsExports.js' import { deSerialize } from '../schema/serialize.js' -import { SCHEMA_FILE, SCHEMA_FILE_DEPRECATED } from '../index.js' +import { SCHEMA_FILE } from '../index.js' +import { SchemaOut } from '../schema.js' export type StartOpts = { clean?: boolean @@ -71,16 +72,7 @@ const handleModifyResponse = (db: DbServer, arr: ArrayBuffer) => { } } -export async function start(db: DbServer, opts?: StartOpts) { - const path = db.fileSystemPath - const noop = () => {} - - if (opts?.clean) { - await rm(path, { recursive: true, force: true }).catch(noop) - } - - await mkdir(path, { recursive: true }).catch(noop) - +export async function realStart(db: DbServer, schema: SchemaOut) { let nrThreads: number nrThreads = ((nrThreads = availableParallelism()), nrThreads < 2 ? 2 : nrThreads - 1) @@ -94,37 +86,49 @@ export async function start(db: DbServer, opts?: StartOpts) { } else if (id === BridgeResponse.flushModify) { handleModifyResponse(db, buffer) } - }, db.fileSystemPath, nrThreads) + }, db.fileSystemPath, nrThreads, makeNativeSchema(schema)) - // Load the common dump try { + setSchemaOnServer(db, schema) await loadCommon(db) - - // Load schema - const schema = await readFile(join(path, SCHEMA_FILE)).catch(noop) - if (schema) { - const s = deSerialize(schema) - setSchemaOnServer(db, s) - } else { - const schemaJson = await readFile(join(path, SCHEMA_FILE_DEPRECATED)) - if (schemaJson) { - setSchemaOnServer(db, JSON.parse(schemaJson.toString())) - } - } } catch (e) { console.error(e.message) } - // use timeout - if (db.saveIntervalInSeconds && db.saveIntervalInSeconds > 0) { - db.saveInterval ??= setInterval(() => { - db.save() - }, db.saveIntervalInSeconds * 1e3) + if (db.fileSystemPath) { + db.save({ skipDirtyCheck: true }).catch(console.error) + + // use timeout + if (db.saveIntervalInSeconds && db.saveIntervalInSeconds > 0) { + db.saveInterval ??= setInterval(() => { + db.save() + }, db.saveIntervalInSeconds * 1e3) + } } if (db.schema) { db.emit('schema', db.schema) } +} + +export async function start(db: DbServer, opts?: StartOpts) { + const path = db.fileSystemPath + const noop = () => {} + + if (opts?.clean) { + await rm(path, { recursive: true, force: true }).catch(noop) + } + + await mkdir(path, { recursive: true }).catch(noop) + + try { + const schema = await readFile(join(path, SCHEMA_FILE)) + realStart(db, deSerialize(schema)) + } catch (e) { + if (e.code !== 'ENOENT') { + throw new Error('Schema read failed', { cause: e }) + } + } if (opts?.delayInMs) { db.delayInMs = opts.delayInMs diff --git a/src/index.ts b/src/index.ts index 7f74cf5883..6f4ecaa4e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,6 @@ export * from './db-client/query/BasedQueryResponse.js' export * from './db-client/hooks.js' export { BasedModify } from './db-client/modify/index.js' -export const SCHEMA_FILE_DEPRECATED = 'schema.json' export const SCHEMA_FILE = 'schema.bin' export const COMMON_SDB_FILE = 'common.sdb' diff --git a/src/native.ts b/src/native.ts index 211ece8bf6..e8555a29bc 100644 --- a/src/native.ts +++ b/src/native.ts @@ -35,10 +35,10 @@ const native = { return db.modify(q, dbCtx) }, - start: (bridge: (id: number, payload: any) => void, fsPath: string, nrThreads: number) => { + start: (bridge: (id: number, payload: any) => void, fsPath: string, nrThreads: number, selvaSchema: Uint8Array) => { const fsPathBuf = new Uint8Array(db.stringByteLength(fsPath)) ENCODER.encodeInto(fsPath, fsPathBuf) - return db.start(bridge, fsPathBuf, nrThreads) + return db.start(bridge, fsPathBuf, nrThreads, selvaSchema) }, stop: (dbCtx: any) => { diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 5ce28ddd85..8b4efd9a0d 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -59,8 +59,6 @@ export const OpType = { loadBlock: 128, unloadBlock: 129, loadCommon: 130, - createType: 131, - setSchemaIds: 132, emptyMod: 133, noOp: 255, } as const @@ -86,8 +84,6 @@ export const OpTypeInverse = { 128: 'loadBlock', 129: 'unloadBlock', 130: 'loadCommon', - 131: 'createType', - 132: 'setSchemaIds', 133: 'emptyMod', 255: 'noOp', } as const @@ -113,8 +109,6 @@ export const OpTypeInverse = { loadBlock, unloadBlock, loadCommon, - createType, - setSchemaIds, emptyMod, noOp */ From c4828b7006f633786385d598e6a8a2ac68e3946d Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 11 Feb 2026 19:35:20 +0100 Subject: [PATCH 227/449] adds or index --- native/query/filter/filter.zig | 37 ++++++--- native/query/single.zig | 69 +--------------- native/types.zig | 4 +- src/db-query/ast/filter/condition.ts | 4 - src/db-query/ast/filter/filter.ts | 117 +++++++++++++++++++++++++-- src/zigTsExports.ts | 5 +- test/modify/insert.ts | 2 +- test/query-ast/include.ts | 113 ++++++++++++++++++++------ test/query/ast.ts | 15 +++- 9 files changed, 249 insertions(+), 117 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index a84477892c..1cb49c6896 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -38,6 +38,11 @@ pub fn prepare( c.offset = utils.alignLeftLen(c.len, q[nextI .. totalSize + i]); const end = totalSize + i; + if (c.op.compare == t.FilterOpCompare.nextOrIndex) { + // if NEXT END = -1 then its q.len + std.debug.print("HELLO ITS OR {any} \n", .{utils.readPtr(u64, q, nextI + @alignOf(u64) - c.offset).*}); + } + switch (c.op.compare) { .selectLargeRefEdge => { // const select = utils.readPtr(t.FilterSelect, q, i + q[i] + utils.sizeOf(t.FilterCondition) + @alignOf(t.FilterSelect) - condition.offset); @@ -106,25 +111,33 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { var pass: bool = true; var v: []const u8 = undefined; var prop: u8 = 255; - var nextOrIndex: usize = q.len; - while (i < nextOrIndex) { + // var end: usize = q.len; + var end: usize = q.len; + + while (i < end) { const c = utils.readPtr(t.FilterCondition, q, i + q[i]); const index = i + q[i] + utils.sizeOf(t.FilterCondition); var nextIndex = COND_ALIGN_BYTES + 1 + utils.sizeOf(t.FilterCondition) + c.size + i; + std.debug.print("-> F {any} I {any} S {any} P {any} \n", .{ c.op, i, c.start, c.prop }); + if (prop != c.prop) { prop = c.prop; - if (c.fieldSchema.type == Selva.c.SELVA_FIELD_TYPE_ALIAS) { - v = try Fields.getAliasByNode(try Node.getType(ctx.db, node), node, c.fieldSchema.field); - } else { - v = Fields.getRaw(node, c.fieldSchema); - } + // if (c.fieldSchema.type == Selva.c.SELVA_FIELD_TYPE_ALIAS) { + // v = try Fields.getAliasByNode(try Node.getType(ctx.db, node), node, c.fieldSchema.field); + // } else { + v = Fields.getRaw(node, c.fieldSchema); + // } } + std.debug.print("-> F OK!!! {any} I {any} S {any} P {any} \n", .{ c.op, i, c.start, c.prop }); + pass = switch (c.op.compare) { .nextOrIndex => blk: { - nextOrIndex = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; - std.debug.print("hello OR {any} \n", .{nextOrIndex}); + end = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; + // nextEnd = nextOrIndex; + // put second thing PREV OR INDEX here + std.debug.print("-> OR {any} \n", .{end}); break :blk true; }, .selectRef => blk: { @@ -150,11 +163,11 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { }; }, }; + if (!pass) { - i = nextOrIndex; - nextOrIndex = q.len; + i = end; + end = q.len; } else { - // i = nextIndex; } } diff --git a/native/query/single.zig b/native/query/single.zig index 4656bf26c7..e7a304f2e4 100644 --- a/native/query/single.zig +++ b/native/query/single.zig @@ -98,60 +98,6 @@ pub fn reference( i.* += header.includeSize; } -// pub fn referenceEdge( -// ctx: *Query.QueryCtx, -// q: []u8, -// from: Node.Node, -// fromType: Selva.Type, -// i: *usize, -// ) !void { -// const header = utils.readNext(t.QueryHeaderSingleReference, q, i); -// const fs = try Schema.getFieldSchema(fromType, header.prop); -// if (References.getReference(from, fs)) |ref| { -// const typeEntry = try Node.getType(ctx.db, header.typeId); -// const n = Node.getNode(typeEntry, ref.dst); - -// if (n) |node| { -// try ctx.thread.query.append(t.ReadOp.reference); -// try ctx.thread.query.append(header.prop); -// const resultByteSizeIndex = try ctx.thread.query.reserve(4); -// const startIndex = ctx.thread.query.index; -// try ctx.thread.query.append(ref.dst); -// const nestedQuery = q[i.* .. i.* + header.includeSize]; -// try Include.include(node, ctx, nestedQuery, typeEntry); - -// // handle this case -// // if ref.edge == 0 - -// const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); -// const e = Node.getNode(edgeTypeEntry, ref.edge); - -// if (e) |edge| { -// const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; -// try ctx.thread.query.append(t.ReadOp.edge); -// const edgesByteSizeIndex = try ctx.thread.query.reserve(4); -// const edgeStartIndex = ctx.thread.query.index; -// try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); -// ctx.thread.query.writeAs( -// u32, -// @truncate(ctx.thread.query.index - edgeStartIndex), -// edgesByteSizeIndex, -// ); -// } - -// i.* += header.edgeSize + header.includeSize; - -// ctx.thread.query.writeAs( -// u32, -// @truncate(ctx.thread.query.index - startIndex), -// resultByteSizeIndex, -// ); -// } -// } - -// i.* += header.includeSize; -// } - pub fn referenceEdge( ctx: *Query.QueryCtx, q: []u8, @@ -175,32 +121,25 @@ pub fn referenceEdge( const nestedQuery = q[i.* .. i.* + header.includeSize]; try Include.include(node, ctx, nestedQuery, typeEntry); - // handle this case - // if ref.edge == 0 - const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const e = Node.getNode(edgeTypeEntry, ref.edge); - // std.debug.print("edge {any} {any} \n", .{ e, ref.edge }); - - // dont think this is nessecary - if (e) |edge| { const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; try ctx.thread.query.append(t.ReadOp.edge); - const edgesByteSizeIndex = try ctx.thread.query.reserve(4); const edgeStartIndex = ctx.thread.query.index; - try Include.include(edge, ctx, edgeQuery, edgeTypeEntry); - ctx.thread.query.writeAs( u32, @truncate(ctx.thread.query.index - edgeStartIndex), edgesByteSizeIndex, ); } else { - std.log.err("singe ref edge -> WRONG EDGE HAS TO BE THERE!\n", .{}); + std.log.err( + "singe ref edge -> WRONG EDGE NODE HAS TO BE THERE! (even if it does not hold values) \n", + .{}, + ); } ctx.thread.query.writeAs( diff --git a/native/types.zig b/native/types.zig index a2a9a9b6ac..e342aa6f29 100644 --- a/native/types.zig +++ b/native/types.zig @@ -317,9 +317,10 @@ pub const RefOp = enum(u8) { end = @intFromEnum(ModOp.end), set = 3, setEdge = 4, + // setIndex = 4, // setTmp = 5, - // // setEdge = 6, + // setEdge = 6, // setIndexTmp = 7, // setEdgeIndex = 8, @@ -888,6 +889,7 @@ pub const FilterOpCompare = enum(u8) { selectLargeRefsEdge = 207, nextOrIndex = 253, + andOp = 254, }; pub const FilterOp = packed struct { diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts index 69caadef7e..d2f5266d2d 100644 --- a/src/db-query/ast/filter/condition.ts +++ b/src/db-query/ast/filter/condition.ts @@ -37,8 +37,6 @@ export const conditionBuffer = ( FilterConditionAlignOf + 1, ) + prop.size - console.log('----', prop.size) - return { condition, offset } } @@ -145,8 +143,6 @@ export const createCondition = ( } else { write(condition, value[0], offset) } - - console.log('derp', condition) return condition } else if (value.length > vectorLen) { // only relevant for eq and neq diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index fd2910adb9..a6a31f83ba 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -5,8 +5,14 @@ import { TypeDef, } from '../../../schema/defs/index.js' import { debugBuffer } from '../../../sdk.js' -import { writeUint64 } from '../../../utils/uint8.js' -import { FilterOpCompare, ID_PROP, PropType } from '../../../zigTsExports.js' +import { concatUint8Arr, writeUint64 } from '../../../utils/uint8.js' +import { + FilterConditionAlignOf, + FilterOpCompare, + ID_PROP, + PropType, + writeFilterConditionProps, +} from '../../../zigTsExports.js' import { Ctx, FilterAst, FilterOp } from '../ast.js' import { conditionBuffer, @@ -63,12 +69,35 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { return walkCtx } +const MAX_U64 = 213123211231221 - 1e9 + +const indexOf = ( + haystack: Uint8Array, + needle: Uint8Array, + offset: number, + end: number, +) => { + if (needle.length === 0) return 0 + for (let i = offset; i <= end - needle.length; i++) { + let found = true + for (let j = 0; j < needle.length; j++) { + if (haystack[i + j] !== needle[j]) { + found = false + break + } + } + if (found) return i + } + return -1 +} + export const filter = ( ast: FilterAst, ctx: Ctx, typeDef: TypeDef, filterIndex: number = 0, - lastProp: number = ID_PROP, + lastProp: number = PropType.id, + prevOr?: Uint8Array, ): number => { const startIndex = ctx.query.length @@ -86,6 +115,8 @@ export const filter = ( ctx.query.reserve(conditionByteSize(8, 8)) } + let andOrReplace: Uint8Array | void = undefined + const { main } = walk(ast, ctx, typeDef, walkCtx) for (const { prop, ops } of main) { @@ -97,6 +128,42 @@ export const filter = ( } } + if (ast.and) { + console.log('========AND========', startIndex) + console.dir(ast.and, { depth: 10 }) + // reserve OR + // if (ast.or) { + // ctx.query.reserve(conditionByteSize(8, 8)) + // } + // maybe just add .AND command + + if (ast.or) { + const { offset, condition } = conditionBuffer( + { + id: 67, + // PropType.id, + size: 8, + start: 0, + }, + 8, + { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, + ) + console.log(condition) + writeUint64(condition, MAX_U64 + Math.floor(Math.random() * 1e9), offset) + andOrReplace = condition + filter( + ast.and, + ctx, + typeDef, + ctx.query.length - startIndex, + walkCtx.prop, + andOrReplace, + ) + } else { + filter(ast.and, ctx, typeDef, ctx.query.length - startIndex, walkCtx.prop) + } + } + if (ast.or) { const resultSize = ctx.query.length - startIndex const nextOrIndex = resultSize + filterIndex @@ -108,11 +175,49 @@ export const filter = ( ) console.info('NEXT OR INDEX', nextOrIndex) + console.log('========OR========') console.dir(ast.or, { depth: 10 }) writeUint64(condition, nextOrIndex, offset) ctx.query.set(condition, startIndex) // then add the actual OR cond + // if FROM OR + // + // if (ast.or) { + // ctx.query.reserve(conditionByteSize(8, 8)) + // } + + if (prevOr) { + console.log('========== PREV OR ==========') + ctx.query.set(prevOr, ctx.query.length) + } + + if (andOrReplace) { + console.log('========== PREV OR REPLACE ==========') + console.log(andOrReplace) + let index = indexOf( + ctx.query.data, + andOrReplace, + startIndex, + ctx.query.length, + ) + if (index === -1) { + console.log('derp', index) + } else { + writeUint64(ctx.query.data, nextOrIndex, offset + index) + + // console.log('derp', index) + + // console.log('----------->', index + offset - 8, offset) + + writeFilterConditionProps.prop( + ctx.query.data, + walkCtx.prop, + index + FilterConditionAlignOf + 1, + ) + } + } + filter( ast.or, ctx, @@ -122,9 +227,9 @@ export const filter = ( ) } - console.log('-------------------------DERP FILTER...') - debugBuffer(ctx.query.data, startIndex, ctx.query.length) - console.log('-------------------------DERP FILTER... DONE') + // console.log('-------------------------DERP FILTER...') + // debugBuffer(ctx.query.data, startIndex, ctx.query.length) + // console.log('-------------------------DERP FILTER... DONE') return ctx.query.length - startIndex } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 5ce28ddd85..efde1dafbf 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -4091,6 +4091,7 @@ export const FilterOpCompare = { selectLargeRefEdge: 206, selectLargeRefsEdge: 207, nextOrIndex: 253, + andOp: 254, } as const export const FilterOpCompareInverse = { @@ -4112,6 +4113,7 @@ export const FilterOpCompareInverse = { 206: 'selectLargeRefEdge', 207: 'selectLargeRefsEdge', 253: 'nextOrIndex', + 254: 'andOp', } as const /** @@ -4132,7 +4134,8 @@ export const FilterOpCompareInverse = { selectSmallRefs, selectLargeRefEdge, selectLargeRefsEdge, - nextOrIndex + nextOrIndex, + andOp */ export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] diff --git a/test/modify/insert.ts b/test/modify/insert.ts index 300891b422..b7f9d58f04 100644 --- a/test/modify/insert.ts +++ b/test/modify/insert.ts @@ -20,7 +20,7 @@ await test('insert', async (t) => { { email: 'youri@saulx.com', isNice: true }, ) - const res1 = await db.query('user', id1).get() + const res1 = await db.query('user', id1).get().toObject() deepEqual(res1, { id: id1, uuid: '9dg786', diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 8c3066f2de..e0154da947 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -6,6 +6,9 @@ import { } from '../../src/protocol/index.js' import { BasedDb, debugBuffer } from '../../src/sdk.js' import { AutoSizedUint8Array } from '../../src/utils/AutoSizedUint8Array.js' +import { writeUint16, writeUint32 } from '../../src/utils/uint8.js' +import wait from '../../src/utils/wait.js' +import { perf } from '../shared/perf.js' import test from '../shared/test.js' await test('include', async (t) => { @@ -15,13 +18,13 @@ await test('include', async (t) => { const client = await db.setSchema({ types: { friend: { - y: 'uint16', + y: 'uint32', }, user: { name: 'string', x: 'boolean', flap: 'uint32', - y: 'uint16', + y: 'uint32', cook: { type: 'object', props: { @@ -46,7 +49,7 @@ await test('include', async (t) => { const a = client.create('user', { name: 'AAAAAAAAAA', - y: 67, + y: 3, x: true, flap: 9999, cook: { @@ -54,14 +57,36 @@ await test('include', async (t) => { }, }) - await client.create('user', { - name: 'CCCCCCCCC', - cook: { - cookie: 1234, - }, - y: 0, - mrFriend: { id: a, $level: 99 }, - }) + // for (let i = 0; i < 5e6; i++) { + // client.create('user', { + // y: i, + // x: true, + // flap: 9999, + // cook: { + // cookie: 1234, + // }, + // }) + // } + + await db.drain() + + // await client.create('user', { + // name: 'CCCCCCCCC', + // cook: { + // cookie: 1234, + // }, + // y: 0, + // mrFriend: { id: a, $level: 99 }, + // }) + + // await client.create('user', { + // name: 'DDDDDDDDD', + // cook: { + // cookie: 1234, + // }, + // y: 0, + // mrFriend: { id: a, $level: 22 }, + // }) await db.drain() @@ -69,27 +94,32 @@ await test('include', async (t) => { type: 'user', filter: { props: { - y: { ops: [{ op: '=', val: 0 }] }, + flap: { ops: [{ op: '=', val: 9999 }] }, }, and: { props: { - y: { ops: [{ op: '=', val: 10 }] }, + y: { ops: [{ op: '=', val: 100 }] }, }, or: { props: { y: { ops: [{ op: '=', val: 3 }] }, }, - or: { - props: { - y: { ops: [{ op: '=', val: 4 }] }, - }, - }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 4 }] }, + // }, + // }, }, }, or: { props: { - y: { ops: [{ op: '=', val: 67 }] }, + y: { ops: [{ op: '=', val: 670 }] }, }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 67 }] }, + // }, + // }, }, }, @@ -97,24 +127,53 @@ await test('include', async (t) => { props: { y: { include: {} }, - // edge is broken in read mrFriend: { props: { y: { include: {} }, }, - edges: { - props: { - $level: { include: {} }, - }, - }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, }, }, } + // (1: y == 0 && ( 2: y == 10 || 4: y == 3)) || 3: y == 67 + + // so the thing is we need to keep track of the NEXT or vs query.len + + // ->:3 :1 ->:4 :2 ->:3 :4 + const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) + debugBuffer(ctx.query) + const result = await db.server.getQueryBuf(ctx.query) - debugBuffer(result) + // const queries: any = [] + // for (let i = 0; i < 10; i++) { + // const x = ctx.query.slice(0) + // writeUint32(x, i + 1, 0) + // queries.push(x) + // } + + // await perf( + // async () => { + // const q: any = [] + // for (let i = 0; i < 10; i++) { + // q.push(db.server.getQueryBuf(queries[i])) + // } + // await Promise.all(q) + // }, + // 'filter speed', + // { + // repeat: 10, + // }, + // ) + // quite large + + // deflate it? const readSchemaBuf = serializeReaderSchema(ctx.readSchema) @@ -122,6 +181,8 @@ await test('include', async (t) => { console.dir(obj, { depth: 10 }) + // RETURN NULL FOR UNDEFINED + console.log( JSON.stringify(obj).length, readSchemaBuf.byteLength + result.byteLength, diff --git a/test/query/ast.ts b/test/query/ast.ts index 132af391e6..d2c85f3acb 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -35,6 +35,12 @@ await test('query types', async (t) => { $role: 'string', }, }, + annoyingThings: { + items: { + ref: 'soAnnoy', + prop: 'users', + }, + }, }, }, }) @@ -60,7 +66,14 @@ await test('query types', async (t) => { const query = db .query2('user') - .include('isNice', 'name', 'otherUsers', 'textField', 'friend') + .include( + 'isNice', + 'name', + 'otherUsers', + 'textField', + 'friend', + 'friend.$rank', + ) const result = await query.get() From 5a91779a8721c1c5cdc69495f4d869a1eaa8b06c Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 11 Feb 2026 19:37:31 +0100 Subject: [PATCH 228/449] filter --- src/db-query/ast/filter/filter.ts | 49 +++++-------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index a6a31f83ba..fbd821f62d 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -101,10 +101,6 @@ export const filter = ( ): number => { const startIndex = ctx.query.length - // need to pass the prop - - // or cond needs to be here - const walkCtx = { main: [], tree: typeDef.tree, @@ -129,14 +125,6 @@ export const filter = ( } if (ast.and) { - console.log('========AND========', startIndex) - console.dir(ast.and, { depth: 10 }) - // reserve OR - // if (ast.or) { - // ctx.query.reserve(conditionByteSize(8, 8)) - // } - // maybe just add .AND command - if (ast.or) { const { offset, condition } = conditionBuffer( { @@ -148,7 +136,6 @@ export const filter = ( 8, { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, ) - console.log(condition) writeUint64(condition, MAX_U64 + Math.floor(Math.random() * 1e9), offset) andOrReplace = condition filter( @@ -174,27 +161,15 @@ export const filter = ( { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, ) - console.info('NEXT OR INDEX', nextOrIndex) - console.log('========OR========') console.dir(ast.or, { depth: 10 }) writeUint64(condition, nextOrIndex, offset) ctx.query.set(condition, startIndex) - // then add the actual OR cond - - // if FROM OR - // - // if (ast.or) { - // ctx.query.reserve(conditionByteSize(8, 8)) - // } if (prevOr) { - console.log('========== PREV OR ==========') ctx.query.set(prevOr, ctx.query.length) } if (andOrReplace) { - console.log('========== PREV OR REPLACE ==========') - console.log(andOrReplace) let index = indexOf( ctx.query.data, andOrReplace, @@ -202,20 +177,14 @@ export const filter = ( ctx.query.length, ) if (index === -1) { - console.log('derp', index) - } else { - writeUint64(ctx.query.data, nextOrIndex, offset + index) - - // console.log('derp', index) - - // console.log('----------->', index + offset - 8, offset) - - writeFilterConditionProps.prop( - ctx.query.data, - walkCtx.prop, - index + FilterConditionAlignOf + 1, - ) + throw new Error('Cannot find AND OR REPLACE INDEX') } + writeUint64(ctx.query.data, nextOrIndex, offset + index) + writeFilterConditionProps.prop( + ctx.query.data, + walkCtx.prop, + index + FilterConditionAlignOf + 1, + ) } filter( @@ -227,9 +196,5 @@ export const filter = ( ) } - // console.log('-------------------------DERP FILTER...') - // debugBuffer(ctx.query.data, startIndex, ctx.query.length) - // console.log('-------------------------DERP FILTER... DONE') - return ctx.query.length - startIndex } From 45f346f8b626197b90492414cd78b0ac7d390fcc Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 11 Feb 2026 19:45:03 +0100 Subject: [PATCH 229/449] only add top level or at nested level --- src/db-query/ast/filter/filter.ts | 19 +++++++++++++------ test/query-ast/include.ts | 30 ++++++------------------------ 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index fbd821f62d..277c16df3c 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -69,7 +69,7 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { return walkCtx } -const MAX_U64 = 213123211231221 - 1e9 +const MAX_INDEX = 11e9 - 1e9 const indexOf = ( haystack: Uint8Array, @@ -128,15 +128,18 @@ export const filter = ( if (ast.or) { const { offset, condition } = conditionBuffer( { - id: 67, - // PropType.id, + id: PropType.id, size: 8, start: 0, }, 8, { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, ) - writeUint64(condition, MAX_U64 + Math.floor(Math.random() * 1e9), offset) + writeUint64( + condition, + MAX_INDEX + Math.floor(Math.random() * 1e9), + offset, + ) andOrReplace = condition filter( ast.and, @@ -166,7 +169,11 @@ export const filter = ( ctx.query.set(condition, startIndex) if (prevOr) { - ctx.query.set(prevOr, ctx.query.length) + if (ast.or.or) { + } else { + ctx.query.set(prevOr, ctx.query.length) + prevOr = undefined + } } if (andOrReplace) { @@ -186,13 +193,13 @@ export const filter = ( index + FilterConditionAlignOf + 1, ) } - filter( ast.or, ctx, typeDef, ctx.query.length - startIndex + filterIndex, walkCtx.prop, + prevOr, ) } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index e0154da947..10d352a666 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -49,7 +49,7 @@ await test('include', async (t) => { const a = client.create('user', { name: 'AAAAAAAAAA', - y: 3, + y: 4, x: true, flap: 9999, cook: { @@ -70,24 +70,6 @@ await test('include', async (t) => { await db.drain() - // await client.create('user', { - // name: 'CCCCCCCCC', - // cook: { - // cookie: 1234, - // }, - // y: 0, - // mrFriend: { id: a, $level: 99 }, - // }) - - // await client.create('user', { - // name: 'DDDDDDDDD', - // cook: { - // cookie: 1234, - // }, - // y: 0, - // mrFriend: { id: a, $level: 22 }, - // }) - await db.drain() const ast: QueryAst = { @@ -104,11 +86,11 @@ await test('include', async (t) => { props: { y: { ops: [{ op: '=', val: 3 }] }, }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 4 }] }, - // }, - // }, + or: { + props: { + y: { ops: [{ op: '=', val: 4 }] }, + }, + }, }, }, or: { From 5c1346953f941cd1fafa69b686ec7a6f9c7fac52 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 11 Feb 2026 19:48:31 +0100 Subject: [PATCH 230/449] make --- test/query-ast/include.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 10d352a666..3d537e2086 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,3 +1,4 @@ +import { deflate } from 'fflate' import { QueryAst } from '../../src/db-query/ast/ast.js' import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { @@ -10,6 +11,7 @@ import { writeUint16, writeUint32 } from '../../src/utils/uint8.js' import wait from '../../src/utils/wait.js' import { perf } from '../shared/perf.js' import test from '../shared/test.js' +import { deflateSync } from 'zlib' await test('include', async (t) => { const db = new BasedDb({ path: t.tmp }) @@ -48,7 +50,6 @@ await test('include', async (t) => { }) const a = client.create('user', { - name: 'AAAAAAAAAA', y: 4, x: true, flap: 9999, @@ -57,6 +58,15 @@ await test('include', async (t) => { }, }) + const b = client.create('user', { + y: 15, + x: true, + flap: 9999, + cook: { + cookie: 1234, + }, + }) + // for (let i = 0; i < 5e6; i++) { // client.create('user', { // y: i, @@ -97,11 +107,11 @@ await test('include', async (t) => { props: { y: { ops: [{ op: '=', val: 670 }] }, }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 67 }] }, - // }, - // }, + or: { + props: { + y: { ops: [{ op: '=', val: 15 }] }, + }, + }, }, }, @@ -132,6 +142,10 @@ await test('include', async (t) => { debugBuffer(ctx.query) + console.log(deflateSync(ctx.query).byteLength) + + debugBuffer(deflateSync(ctx.query)) + const result = await db.server.getQueryBuf(ctx.query) // const queries: any = [] // for (let i = 0; i < 10; i++) { From b28d2ecff5280bd9ab87ac30478021ce67e6aa6c Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 12 Feb 2026 10:03:22 +0100 Subject: [PATCH 231/449] update test --- test/query/ast.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/query/ast.ts b/test/query/ast.ts index 5a8bcc0f07..076286f589 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -58,9 +58,9 @@ await test('query types', async (t) => { users: [userA], }) - // const query = db.query2('user').include('isNice', 'name', '**.id') + const query = db.query2('user').include('isNice', 'name', 'friend.**') - // const result = await query.get() + const result = await query.get() // for (const { name, isNice, otherUsers, friend } of result) { // // const friendName = friend?.name From 6b555474188c833aada2b908641bb87559ebb216 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 12 Feb 2026 10:04:48 +0100 Subject: [PATCH 232/449] Store selva type entries directly on SelvaDb Ref FDN-1880 --- clibs/include/selva/db.h | 4 +- clibs/lib/selva/db.c | 141 +++++++++++++++++------------------ clibs/lib/selva/fields.c | 3 +- clibs/lib/selva/include/db.h | 15 +--- clibs/lib/selva/io/dump.c | 10 +-- 5 files changed, 80 insertions(+), 93 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 4820737ebf..1e49b53bd7 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -90,13 +90,13 @@ node_type_t selva_get_max_type(const struct SelvaDb *db) __attribute__((nonnull) * Find a type by type id. */ SELVA_EXPORT -struct SelvaTypeEntry *selva_get_type_by_index(const struct SelvaDb *db, node_type_t type) __attribute__((nonnull)); +struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) __attribute__((nonnull)); /** * Get the type for node. */ SELVA_EXPORT -struct SelvaTypeEntry *selva_get_type_by_node(const struct SelvaDb *db, struct SelvaNode *node) __attribute__((nonnull, pure)); +struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) __attribute__((nonnull, pure)); SELVA_EXPORT inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index d6da229103..caf80fe63e 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -62,13 +62,6 @@ int SelvaAlias_cmp_dest(const struct SelvaAlias *a, const struct SelvaAlias *b) return node_id_cmp(a->dest, b->dest); } -__attribute__((nonnull)) -static int SelvaTypeEntry_cmp(const struct SelvaTypeEntry *a, const struct SelvaTypeEntry *b) -{ - return (int)((struct SelvaTypeEntryFind *)a)->type - (int)((struct SelvaTypeEntryFind *)b)->type; -} - -RB_GENERATE(SelvaTypeEntryIndex, SelvaTypeEntry, _entry, SelvaTypeEntry_cmp) RB_GENERATE(SelvaNodeIndex, SelvaNode, _index_entry, SelvaNode_cmp) RB_GENERATE(SelvaAliasesByName, SelvaAlias, _entryByName, SelvaAlias_cmp_name) RB_GENERATE(SelvaAliasesByDest, SelvaAlias, _entryByDest, SelvaAlias_cmp_dest) @@ -148,23 +141,6 @@ void selva_db_expire_tick(struct SelvaDb *db, int64_t now) selva_expire_tick(&db->expiring, nullptr, now); } - -static uint32_t te_slab_size(void) -{ - const size_t te_size = sizeof(struct SelvaTypeEntry); - uint32_t slab_size = (1'048'576 / te_size) * te_size; - - slab_size--; - slab_size |= slab_size >> 1; - slab_size |= slab_size >> 2; - slab_size |= slab_size >> 4; - slab_size |= slab_size >> 8; - slab_size |= slab_size >> 16; - slab_size++; - - return slab_size; -} - static bool eq_type_exists(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) { struct SelvaTypeEntry *te; @@ -203,7 +179,7 @@ static int selva_db_create_type(struct SelvaDb *db, node_type_t type, const uint return SELVA_ENOBUFS; } - struct SelvaTypeEntry *te = mempool_get(&db->types.pool); + struct SelvaTypeEntry *te = &db->types[type - 1]; #if 0 fprintf(stderr, "schema_buf: [ "); @@ -217,7 +193,6 @@ static int selva_db_create_type(struct SelvaDb *db, node_type_t type, const uint te->type = type; err = schemabuf_parse_ns(&te->ns, schema_buf, schema_len, db->sdb_version ?: SELVA_SDB_VERSION); if (err) { - mempool_return(&db->types.pool, te); return err; } @@ -229,36 +204,78 @@ static int selva_db_create_type(struct SelvaDb *db, node_type_t type, const uint const size_t node_size = sizeof_wflex(struct SelvaNode, fields.fields_map, nfo.nr_fields); mempool_init2(&te->nodepool, NODEPOOL_SLAB_SIZE, node_size, alignof(size_t), MEMPOOL_ADV_RANDOM | MEMPOOL_ADV_HP_SOFT); - if (RB_INSERT(SelvaTypeEntryIndex, &db->types.index, te)) { - db_panic("Schema update not supported"); - } - db->types.count++; return 0; } +struct SelvaDbSchemaDesc { + node_type_t type; + uint32_t len; +} __packed; + +static size_t schema_count_types(size_t len, uint8_t schema[len]) +{ + size_t n = 0; + + for (size_t i = 0; i < len;) { + struct SelvaDbSchemaDesc desc; + + if (unlikely(len - i < sizeof(desc))) { + fprintf(stderr, "%s schema too short\n", __func__); + return 0; + } + + memcpy(&desc, schema + i, sizeof(desc)); + i += sizeof(desc) + desc.len; + n++; + } + + return n; +} + +static uint32_t te_size(void) +{ + const size_t te_size = sizeof(struct SelvaTypeEntry); + uint32_t slab_size = (1'048'576 / te_size) * te_size; + + slab_size--; + slab_size |= slab_size >> 1; + slab_size |= slab_size >> 2; + slab_size |= slab_size >> 4; + slab_size |= slab_size >> 8; + slab_size |= slab_size >> 16; + slab_size++; + + return slab_size; +} + struct SelvaDb *selva_db_create(size_t len, uint8_t schema[len]) { - struct SelvaDb *db = selva_calloc(1, sizeof(*db)); + const size_t nr_types = schema_count_types(len, schema); + struct SelvaDb *db; + + if (nr_types == 0) { + return nullptr; + } - mempool_init(&db->types.pool, te_slab_size(), sizeof(struct SelvaTypeEntry), alignof(struct SelvaTypeEntry)); + db = selva_calloc(1, offsetof(typeof(*db), types[0]) + nr_types * te_size()); + db->nr_types = nr_types; db->expiring.expire_cb = expire_cb; db->expiring.cancel_cb = cancel_cb; db->dirfd = AT_FDCWD; selva_expire_init(&db->expiring); for (size_t i = 0; i < len;) { - struct { - node_type_t type; - uint32_t len; - } __packed desc; + struct SelvaDbSchemaDesc desc; int err; if (unlikely(len - i < sizeof(desc))) { fprintf(stderr, "%s schema too short\n", __func__); goto fail; } + memcpy(&desc, schema + i, sizeof(desc)); + assert(desc.type <= db->nr_types); err = selva_db_create_type(db, desc.type, schema + i + sizeof(desc), desc.len); i += sizeof(desc) + desc.len; if (err) { @@ -333,7 +350,7 @@ static void del_all_nodes(struct SelvaDb *db, struct SelvaTypeEntry *te) } } -static void destroy_type(struct SelvaDb *db, struct SelvaTypeEntry *te) +static void destroy_type(struct SelvaTypeEntry *te) { /* * We assume that as the nodes are deleted the aliases are also freed. @@ -343,33 +360,24 @@ static void destroy_type(struct SelvaDb *db, struct SelvaTypeEntry *te) colvec_deinit_te(te); - /* - * Remove this type from the type list. - */ - RB_REMOVE(SelvaTypeEntryIndex, &db->types.index, te); - mempool_destroy(&te->nodepool); selva_free(te->blocks); schemabuf_deinit_fields_schema(&te->ns.fields_schema); + selva_free(te->schema_buf); #if 0 memset(te, 0, sizeof(*te)); #endif - selva_free(te->schema_buf); - mempool_return(&db->types.pool, te); - db->types.count--; + te->type = 0; } static void del_all_types(struct SelvaDb *db) { - struct SelvaTypeEntry *te; - struct SelvaTypeEntry *tmp; - - RB_FOREACH_SAFE(te, SelvaTypeEntryIndex, &db->types.index, tmp) { - del_all_nodes(db, te); + for (size_t ti = 0; ti < db->nr_types; ti++) { + del_all_nodes(db, &db->types[ti]); } - RB_FOREACH_SAFE(te, SelvaTypeEntryIndex, &db->types.index, tmp) { - destroy_type(db, te); + for (size_t ti = 0; ti < db->nr_types; ti++) { + destroy_type(&db->types[ti]); } } @@ -421,9 +429,8 @@ struct SelvaTypeBlock *selva_get_block(struct SelvaTypeBlocks *blocks, node_id_t void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx) { - struct SelvaTypeEntry *te; - - RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { + for (size_t ti = 0; ti < db->nr_types; ti++) { + struct SelvaTypeEntry *te = &db->types[ti]; struct SelvaTypeBlocks *blocks = te->blocks; for (block_id_t block_i = 0; block_i < blocks->len; block_i++) { @@ -442,31 +449,23 @@ void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, node_type_t selva_get_max_type(const struct SelvaDb *db) { - struct SelvaTypeEntry *te; - - te = RB_MAX(SelvaTypeEntryIndex, (typeof_unqual(db->types.index) *)&db->types.index); - return te ? te->type : 0; + assert(db->types[db->nr_types - 1].type == db->nr_types); + return db->nr_types; } -struct SelvaTypeEntry *selva_get_type_by_index(const struct SelvaDb *db, node_type_t type) +struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) { - struct SelvaTypeEntryFind find = { type }; - if (type == 0) { return nullptr; } - - return RB_FIND(SelvaTypeEntryIndex, (typeof_unqual(db->types.index) *)&db->types.index, (struct SelvaTypeEntry *)&find); + assert(type - 1 < db->nr_types); + return &db->types[type - 1]; } -struct SelvaTypeEntry *selva_get_type_by_node(const struct SelvaDb *db, struct SelvaNode *node) +struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) { - struct SelvaTypeEntryFind find = { node->type }; - struct SelvaTypeEntry *te; - - te = RB_FIND(SelvaTypeEntryIndex, (typeof_unqual(db->types.index) *)&db->types.index, (struct SelvaTypeEntry *)&find); - assert(te); - return te; + assert(node->type - 1 < db->nr_types); + return &db->types[node->type - 1]; } extern inline node_type_t selva_get_type(const struct SelvaTypeEntry *te); diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 9d92bc969c..5ffddc4c73 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -297,7 +297,8 @@ static const struct SelvaFieldSchema *get_edge_dst_fs( return nullptr; } - type_dst = selva_get_type_by_index(db, efc->dst_node_type); + /* TODO This could be also handled with a Generic */ + type_dst = selva_get_type_by_index((typeof_unqual(*db) *)db, efc->dst_node_type); assert(type_dst->type == efc->dst_node_type); return selva_get_fs_by_te_field(type_dst, efc->inverse_field); diff --git a/clibs/lib/selva/include/db.h b/clibs/lib/selva/include/db.h index d75ff82276..dba3284501 100644 --- a/clibs/lib/selva/include/db.h +++ b/clibs/lib/selva/include/db.h @@ -15,7 +15,6 @@ struct selva_string; -RB_HEAD(SelvaTypeEntryIndex, SelvaTypeEntry); RB_HEAD(SelvaNodeIndex, SelvaNode); RB_HEAD(SelvaAliasesByName, SelvaAlias); RB_HEAD(SelvaAliasesByDest, SelvaAlias); @@ -76,8 +75,6 @@ struct SelvaTypeBlock { struct SelvaTypeEntry { node_type_t type; - RB_ENTRY(SelvaTypeEntry) _entry; - /** * Node blocks in this type. */ @@ -141,15 +138,6 @@ struct SelvaDbExpireToken { * Database instance. */ struct SelvaDb { - /** - * SelvaTypeEntries. - */ - struct { - struct SelvaTypeEntryIndex index; - struct mempool pool; /*!< types area allocated from here. */ - size_t count; /*!< Total count of types. */ - } types; - /** * Expiring nodes. */ @@ -161,9 +149,10 @@ struct SelvaDb { int dirfd; uint32_t sdb_version; /*!< Current SDB version. Set on common load and save. 0 if not saved/loaded. */ + size_t nr_types; + struct SelvaTypeEntry types[] __counted_by(nr_types); }; -RB_PROTOTYPE(SelvaTypeEntryIndex, SelvaTypeEntry, _entry, SelvaTypeEntry_cmp) RB_PROTOTYPE(SelvaNodeIndex, SelvaNode, _index_entry, SelvaNode_cmp) RB_PROTOTYPE(SelvaAliasesByName, SelvaAlias, _entryByName, SelvaAlias_cmp_name) RB_PROTOTYPE(SelvaAliasesByDest, SelvaAlias, _entryByDest, SelvaAlias_cmp_dest) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 6be551bff4..fef4257136 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -291,11 +291,10 @@ static void save_expire(struct selva_io *io, struct SelvaDb *db) static void save_aliases(struct selva_io *io, struct SelvaDb *db) { - struct SelvaTypeEntry *te; - write_dump_magic(io, DUMP_MAGIC_ALIASES); - RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { + for (size_t ti = 0; ti < db->nr_types; ti++) { + struct SelvaTypeEntry *te = &db->types[ti]; const size_t nr_fields = te->ns.nr_alias_fields; for (size_t i = 0; i < nr_fields; i++) { @@ -847,14 +846,13 @@ static int load_expire(struct selva_io *io, struct SelvaDb *db) __attribute__((warn_unused_result)) static int load_aliases(struct selva_io *io, struct SelvaDb *db) { - struct SelvaTypeEntry *te; - if (!read_dump_magic(io, DUMP_MAGIC_ALIASES)) { selva_io_errlog(io, "Invalid aliases magic"); return SELVA_EINVAL; } - RB_FOREACH(te, SelvaTypeEntryIndex, &db->types.index) { + for (size_t ti = 0; ti < db->nr_types; ti++) { + struct SelvaTypeEntry *te = &db->types[ti]; const size_t nr_fields = te->ns.nr_alias_fields; for (size_t i = 0; i < nr_fields; i++) { From 76662e6d9468c65719dd5e8d0c1c99de8561143c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 12 Feb 2026 10:35:39 +0100 Subject: [PATCH 233/449] Inline SelvaTypeEntry funcs --- clibs/include/selva/db.h | 37 ++++++++++++++++++++++++++++++++++--- clibs/lib/selva/db.c | 21 +++------------------ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 1e49b53bd7..2e75eb5643 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -84,21 +84,52 @@ SELVA_EXPORT int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block_i, char *errlog_buf, size_t errlog_size) __attribute__((nonnull)); SELVA_EXPORT -node_type_t selva_get_max_type(const struct SelvaDb *db) __attribute__((nonnull)); +[[reproducible]] +inline node_type_t selva_get_max_type(const struct SelvaDb *db) +#ifndef __zig +{ + assert(db->types[db->nr_types - 1].type == db->nr_types); + return db->nr_types; +} +#else +; +#endif /** * Find a type by type id. */ SELVA_EXPORT -struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) __attribute__((nonnull)); +[[reproducible]] +inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) +#ifndef __zig +{ + if (type == 0) { + return nullptr; + } + assert(type - 1 < db->nr_types); + return &db->types[type - 1]; +} +#else +; +#endif /** * Get the type for node. */ SELVA_EXPORT -struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) __attribute__((nonnull, pure)); +[[reproducible]] +inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) +#ifndef __zig +{ + assert(node->type - 1 < db->nr_types); + return &db->types[node->type - 1]; +} +#else +; +#endif SELVA_EXPORT +[[reproducible]] inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) #ifndef __zig { diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index caf80fe63e..5673a94054 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -447,26 +447,11 @@ void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, } } -node_type_t selva_get_max_type(const struct SelvaDb *db) -{ - assert(db->types[db->nr_types - 1].type == db->nr_types); - return db->nr_types; -} +extern inline node_type_t selva_get_max_type(const struct SelvaDb *db); -struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) -{ - if (type == 0) { - return nullptr; - } - assert(type - 1 < db->nr_types); - return &db->types[type - 1]; -} +extern inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type); -struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) -{ - assert(node->type - 1 < db->nr_types); - return &db->types[node->type - 1]; -} +extern inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node); extern inline node_type_t selva_get_type(const struct SelvaTypeEntry *te); From ca9389e84ea49f994b75b015c1496d6cc4226db5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 12 Feb 2026 11:15:38 +0100 Subject: [PATCH 234/449] Add types perf test --- test/types.perf.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/types.perf.ts diff --git a/test/types.perf.ts b/test/types.perf.ts new file mode 100644 index 0000000000..ac8069d65f --- /dev/null +++ b/test/types.perf.ts @@ -0,0 +1,32 @@ +import { BasedDb } from '../src/index.js' +import test from './shared/test.js' +import { perf } from './shared/assert.js' +import { fastPrng } from '../src/utils/fastPrng.js' + +const NR_TYPES = 16384 + +await test('create and access many types', async (t) => { + const prng = fastPrng() + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => t.backup(db)) + + const rndType = () => `type${prng(1, NR_TYPES)}` + const client = await db.setSchema({ + types: Object.fromEntries(Array.from({ length: 16384 }, (_, i) => [ `type${i + 1}`, { bool: 'boolean' } ])), + }) + + await perf( + () => { + client.create(rndType(), { + bool: true, + }) + }, + 'create booleans', + { repeat: 1_000_000 }, + ) + + await db.drain() +}) From 347538209da0262298f3b3d49589217e30f224b7 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 12 Feb 2026 11:16:12 +0100 Subject: [PATCH 235/449] wip --- test/youzi.ts | 233 ++++++-------------------------------------------- 1 file changed, 25 insertions(+), 208 deletions(-) diff --git a/test/youzi.ts b/test/youzi.ts index e96d3301c9..c34b830c48 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -1,212 +1,29 @@ -import { BasedDb } from '../src/index.js' -import { AutoSizedUint8Array } from '../src/utils/AutoSizedUint8Array.js' -import { LangCode, pushModifyHeader } from '../src/zigTsExports.js' -import { - flush, - getTypeDefs, - serializeCreate, -} from '../src/db-client/modify/index.js' -import { parseSchema } from '../src/schema.js' -import test from './shared/test.js' +// @ts-nocheck -await test('schema-defs', async (t) => { - const schema = parseSchema({ - types: { - user: { - age: 'number', - rating: 'uint8', - nested: { - props: { - friends: { - items: { - ref: 'user', - prop: 'nested.friends', - $rating: 'number', - }, - }, - }, - }, - }, - }, - }) - const defs = getTypeDefs(schema) - console.dir(defs, { depth: 3 }) -}) +// query('user').include('name', 'id', 'body') -await test.skip('modify raw', async (t) => { - const db = new BasedDb({ path: t.tmp }) - await db.start({ clean: true }) +// import test from "./shared/test.js"; +// // import client from './client' +// const b: any = {} +// const useQuery: any = {} - t.after(() => t.backup(db)) - - await db.setSchema({ - types: { - user: { - age: 'number', - rating: 'uint8', - friends: { - items: { - ref: 'user', - prop: 'friends', - $rank: 'uint8', - }, - }, - name: 'string', - }, - }, - }) - - const buf = new AutoSizedUint8Array() - pushModifyHeader(buf, { - opId: 0, // is filled on server - opType: 0, // is filled on server - schema: 0, - count: 1, - }) - - serializeCreate( - db.client.schema!, - 'user', - { - age: 32, - rating: 5, - name: 'youzi', - }, - buf, - LangCode.nl, - ) - - serializeCreate( - db.client.schema!, - 'user', - { - age: 24, - rating: 54, - name: 'jamez', - friends: [{ id: 1, $rank: 5 }], - }, - buf, - LangCode.nl, - ) - - await db.server.modify(buf.view) - - const res = await db.query('user').include('*', 'friends.*').get().toObject() - console.dir(res, { depth: null }) -}) - -await test.skip('modify client', async (t) => { - const db = new BasedDb({ path: t.tmp }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - // When using setSchema, the return value is a typed client - const client = await db.setSchema({ - types: { - user: { - friends: { - items: { - ref: 'user', - prop: 'friends', - $rank: 'uint8', - }, - }, - friend: { - ref: 'user', - prop: 'friend', - $rating: 'uint8', - }, - name: 'string', - }, - }, - }) - - const youzi = client.create('user', { - name: 'youzi', - }) - - // olli uses BasedModify for youzi - const olli = client.create('user', { - name: 'olli', - friends: { add: [youzi] }, - friend: youzi, - }) - - // youzi is now in-flight - flush(db.client.modifyCtx) - - const jamez = client.create('user', { - name: 'jamez', - friend: { id: youzi, $rating: 10 }, - }) - - const marco = client.create('user', { - name: 'mr marco', - friends: [youzi], - }) - - jamez.then((jamezId) => { - const fulco = client - .create('user', { - name: 'mr fulco', - friends: [jamezId], - friend: jamezId, - }) - .then(() => { - const tom = client - .create('user', { - name: 'mr tom', - friends: [jamezId], - friend: jamezId, - }) - .then() - }) - }) - - // this will await all queued modifies - const res = await db.query('user').include('*', 'friend').get().toObject() - console.dir(res, { depth: null }) -}) - -// await test('reffies', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// }) - -// await db.start({ clean: true }) - -// t.after(() => t.backup(db)) - -// await db.setSchema({ -// types: { -// user: { -// name: 'string', -// others: { -// items: { -// ref: 'user', -// prop: 'others', -// $rating: 'number', -// }, -// }, -// }, -// }, -// }) - -// const userId = await db.create('user', { name: 'a' }) - -// await db.create('user', { -// others: [ -// { -// id: userId, -// $rating: 20, -// }, -// ], -// // others: [userId], -// name: 'bxxxxxxxx', -// }) - -// const res = await db.query('user').include('*', '**').get().toObject() - -// console.dir(res, { depth: null }) +// await test('browser things', async t => { +// const { data } = useQuery('cms', () => b.query('user').include('name'), []) // }) + +// // a.tsx +// import { getTypedClient, Provider } from 'myCoolLib' +// const myFullyTypedClient = getTypedClient(myCustomSchema) + +// const App = () => { +// return +// +// +// } + +// // b.tsx +// import { useClient } from 'myCoolLib' +// const Page = () => { +// const myFullyTypedClient = useClient() +// return ... +// } From a42ad173c2ee31350e24fa44409d04401ce04e80 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 12 Feb 2026 11:56:33 +0100 Subject: [PATCH 236/449] Improve backup size logging --- test/shared/test.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/shared/test.ts b/test/shared/test.ts index 33b993201d..40b96b1ade 100644 --- a/test/shared/test.ts +++ b/test/shared/test.ts @@ -95,13 +95,8 @@ const test: { console.log(styleText('gray', `saved db ${performance.now() - d} ms`)) const size = await dirSize(t.tmp) - - const kbs = ~~(size / 1024) - if (kbs < 5000) { - console.log(styleText('gray', `backup size ${kbs}kb`)) - } else { - console.log(styleText('gray', `backup size ${~~(kbs / 1024)}mb`)) - } + const strSize = size < 1_048_576 ? `${Math.ceil(size / 1024)} kiB` : `${Math.ceil(size / 1_048_576)} MiB` + console.log(styleText('gray', `backup size ${strSize}`)) await db.stop() From 4529cc76c7a4b3d4f444fd7eb8815740dafc193b Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 12 Feb 2026 14:35:41 +0100 Subject: [PATCH 237/449] Pass fixed length hint to selva for string & bin --- clibs/lib/selva/fields.c | 4 ++-- clibs/lib/selva/schema.c | 4 ++-- native/types.zig | 2 +- src/schema/defs/props/binary.ts | 2 +- src/schema/defs/props/cardinality.ts | 2 +- src/schema/defs/props/strings.ts | 4 ++-- src/zigTsExports.ts | 12 ++++++------ 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 5ffddc4c73..5ad22e951a 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -213,7 +213,7 @@ static int set_field_string(struct SelvaFields *fields, const struct SelvaFieldS assert(len >= 2 + sizeof(uint32_t)); assume(len >= 2 + sizeof(uint32_t)); - if (fs->string.fixed_len && len > fs->string.fixed_len) { + if (fs->string.fixed_len > 0 && len > fs->string.fixed_len) { return SELVA_ENOBUFS; } @@ -927,7 +927,7 @@ static inline int _selva_fields_get_mutable_string(struct SelvaNode *node, const return SELVA_EINTYPE; } - if (fs->string.fixed_len && len > fs->string.fixed_len) { + if (fs->string.fixed_len > 0 && len > fs->string.fixed_len) { return SELVA_ENOBUFS; } diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index 946c5e8b1b..aea9b8ac0a 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -94,7 +94,7 @@ static int type2fs_string(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc { struct { enum SelvaFieldType type; - uint8_t fixed_len; + uint8_t fixed_len_hint; uint32_t default_len; } __packed head; size_t off = 0; @@ -115,7 +115,7 @@ static int type2fs_string(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc * We only allow very short strings to be stored as fixed embedded * strings. This is best to be aligned to 64-bit boundaries */ - .fixed_len = head.fixed_len <= 48 ? head.fixed_len : 0, + .fixed_len = head.fixed_len_hint <= 48 ? head.fixed_len_hint : 0, .default_len = head.default_len, }, }; diff --git a/native/types.zig b/native/types.zig index 8a0c3ebc59..77d84d138c 100644 --- a/native/types.zig +++ b/native/types.zig @@ -932,7 +932,7 @@ pub const SelvaSchemaMicroBuffer = packed struct { pub const SelvaSchemaString = packed struct { type: SelvaFieldType, - fixedLen: u8, + fixedLenHint: u8, defaultLen: u32, }; diff --git a/src/schema/defs/props/binary.ts b/src/schema/defs/props/binary.ts index 78d6d2aa69..ef2b5d485b 100644 --- a/src/schema/defs/props/binary.ts +++ b/src/schema/defs/props/binary.ts @@ -54,7 +54,7 @@ export const binary = class Binary extends BasePropDef { override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, - fixedLen: 0, + fixedLenHint: this.schema.maxBytes ?? 0, defaultLen: 0, }) } diff --git a/src/schema/defs/props/cardinality.ts b/src/schema/defs/props/cardinality.ts index 26e0f9b549..36cc1cb489 100644 --- a/src/schema/defs/props/cardinality.ts +++ b/src/schema/defs/props/cardinality.ts @@ -45,7 +45,7 @@ export const cardinality = class Cardinality extends BasePropDef { override pushSelvaSchema(buf: AutoSizedUint8Array) { pushSelvaSchemaString(buf, { type: PropTypeSelva.string, - fixedLen: 0, + fixedLenHint: 0, defaultLen: 0, }) } diff --git a/src/schema/defs/props/strings.ts b/src/schema/defs/props/strings.ts index 986e615ef7..662ffcece7 100644 --- a/src/schema/defs/props/strings.ts +++ b/src/schema/defs/props/strings.ts @@ -116,8 +116,8 @@ export const string = class String extends BasePropDef { override pushSelvaSchema(buf: AutoSizedUint8Array) { const index = pushSelvaSchemaString(buf, { type: PropTypeSelva.string, - fixedLen: 0, - defaultLen: 0, + fixedLenHint: this.schema.maxBytes ?? 0, // Note that selva doesn't do actual validation + defaultLen: 0, // TODO also check that defaultLen <= maxBytes }) if (this.schema.default) { const start = buf.length diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index db845bb5c7..85cda99eb1 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -4565,7 +4565,7 @@ export const pushSelvaSchemaMicroBuffer = ( export type SelvaSchemaString = { type: SelvaFieldType - fixedLen: number + fixedLenHint: number defaultLen: number } @@ -4580,7 +4580,7 @@ export const writeSelvaSchemaString = ( ): number => { buf[offset] = Number(header.type) offset += 1 - buf[offset] = Number(header.fixedLen) + buf[offset] = Number(header.fixedLenHint) offset += 1 writeUint32(buf, Number(header.defaultLen), offset) offset += 4 @@ -4591,7 +4591,7 @@ export const writeSelvaSchemaStringProps = { type: (buf: Uint8Array, value: SelvaFieldType, offset: number) => { buf[offset] = Number(value) }, - fixedLen: (buf: Uint8Array, value: number, offset: number) => { + fixedLenHint: (buf: Uint8Array, value: number, offset: number) => { buf[offset + 1] = Number(value) }, defaultLen: (buf: Uint8Array, value: number, offset: number) => { @@ -4605,7 +4605,7 @@ export const readSelvaSchemaString = ( ): SelvaSchemaString => { const value: SelvaSchemaString = { type: buf[offset], - fixedLen: buf[offset + 1], + fixedLenHint: buf[offset + 1], defaultLen: readUint32(buf, offset + 2), } return value @@ -4613,7 +4613,7 @@ export const readSelvaSchemaString = ( export const readSelvaSchemaStringProps = { type: (buf: Uint8Array, offset: number) => buf[offset], - fixedLen: (buf: Uint8Array, offset: number) => buf[offset + 1], + fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } @@ -4629,7 +4629,7 @@ export const pushSelvaSchemaString = ( ): number => { const index = buf.length buf.pushUint8(Number(header.type)) - buf.pushUint8(Number(header.fixedLen)) + buf.pushUint8(Number(header.fixedLenHint)) buf.pushUint32(Number(header.defaultLen)) return index } From 7daaf41a7e15d2a4c945f90f9e10ca5d7015eb55 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 12 Feb 2026 15:09:31 +0100 Subject: [PATCH 238/449] Add todo comments --- src/schema/defs/props/strings.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/schema/defs/props/strings.ts b/src/schema/defs/props/strings.ts index 662ffcece7..d1b7356134 100644 --- a/src/schema/defs/props/strings.ts +++ b/src/schema/defs/props/strings.ts @@ -45,8 +45,10 @@ export const string = class String extends BasePropDef { constructor(prop: SchemaString, path: string[], typeDef: TypeDef) { super(prop, path, typeDef) if (prop.maxBytes && prop.maxBytes < 61) { + // TODO explain why 61 bytes (1 byte is for size but why 60 byte and not 47 or 63? */ this.size = prop.maxBytes + 1 } else if (prop.max && prop.max < 31) { + // TODO Explain why this is here this.size = prop.max * 2 + 1 } if (this.size) { From 092b1a3c24d056259eb9b196d62c19ca9ebecb34 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Fri, 13 Feb 2026 11:03:09 +0100 Subject: [PATCH 239/449] fix --- native/query/filter/filter.zig | 13 ++--- test/query-ast/include.ts | 96 +++++++++++++++++----------------- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 1cb49c6896..d34e8755d9 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -38,10 +38,10 @@ pub fn prepare( c.offset = utils.alignLeftLen(c.len, q[nextI .. totalSize + i]); const end = totalSize + i; - if (c.op.compare == t.FilterOpCompare.nextOrIndex) { - // if NEXT END = -1 then its q.len - std.debug.print("HELLO ITS OR {any} \n", .{utils.readPtr(u64, q, nextI + @alignOf(u64) - c.offset).*}); - } + // if (c.op.compare == t.FilterOpCompare.nextOrIndex) { + // if NEXT END = -1 then its q.len + // std.debug.print("HELLO ITS OR {any} \n", .{utils.readPtr(u64, q, nextI + @alignOf(u64) - c.offset).*}); + // } switch (c.op.compare) { .selectLargeRefEdge => { @@ -119,8 +119,6 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { const index = i + q[i] + utils.sizeOf(t.FilterCondition); var nextIndex = COND_ALIGN_BYTES + 1 + utils.sizeOf(t.FilterCondition) + c.size + i; - std.debug.print("-> F {any} I {any} S {any} P {any} \n", .{ c.op, i, c.start, c.prop }); - if (prop != c.prop) { prop = c.prop; // if (c.fieldSchema.type == Selva.c.SELVA_FIELD_TYPE_ALIAS) { @@ -130,14 +128,11 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { // } } - std.debug.print("-> F OK!!! {any} I {any} S {any} P {any} \n", .{ c.op, i, c.start, c.prop }); - pass = switch (c.op.compare) { .nextOrIndex => blk: { end = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; // nextEnd = nextOrIndex; // put second thing PREV OR INDEX here - std.debug.print("-> OR {any} \n", .{end}); break :blk true; }, .selectRef => blk: { diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 3d537e2086..1159222455 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -67,20 +67,22 @@ await test('include', async (t) => { }, }) - // for (let i = 0; i < 5e6; i++) { - // client.create('user', { - // y: i, - // x: true, - // flap: 9999, - // cook: { - // cookie: 1234, - // }, - // }) - // } + let d = Date.now() + + for (let i = 0; i < 5e6; i++) { + client.create('user', { + y: i, + x: true, + flap: 9999, + cook: { + cookie: 1234, + }, + }) + } await db.drain() - await db.drain() + console.log(Date.now() - d, 'ms') const ast: QueryAst = { type: 'user', @@ -103,16 +105,16 @@ await test('include', async (t) => { }, }, }, - or: { - props: { - y: { ops: [{ op: '=', val: 670 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 15 }] }, - }, - }, - }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 670 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 15 }] }, + // }, + // }, + // }, }, // (y == 0 && (y == 10 || y == 3 || y == 4)) || y == 67 @@ -144,29 +146,29 @@ await test('include', async (t) => { console.log(deflateSync(ctx.query).byteLength) - debugBuffer(deflateSync(ctx.query)) + debugBuffer(deflateSync(ctx.query).toString('hex')) const result = await db.server.getQueryBuf(ctx.query) - // const queries: any = [] - // for (let i = 0; i < 10; i++) { - // const x = ctx.query.slice(0) - // writeUint32(x, i + 1, 0) - // queries.push(x) - // } - - // await perf( - // async () => { - // const q: any = [] - // for (let i = 0; i < 10; i++) { - // q.push(db.server.getQueryBuf(queries[i])) - // } - // await Promise.all(q) - // }, - // 'filter speed', - // { - // repeat: 10, - // }, - // ) + const queries: any = [] + for (let i = 0; i < 10; i++) { + const x = ctx.query.slice(0) + writeUint32(x, i + 1, 0) + queries.push(x) + } + + await perf( + async () => { + const q: any = [] + for (let i = 0; i < 10; i++) { + q.push(db.server.getQueryBuf(queries[i])) + } + await Promise.all(q) + }, + 'filter speed', + { + repeat: 100, + }, + ) // quite large // deflate it? @@ -175,12 +177,12 @@ await test('include', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - console.dir(obj, { depth: 10 }) + // console.dir(obj, { depth: 10 }) // RETURN NULL FOR UNDEFINED - console.log( - JSON.stringify(obj).length, - readSchemaBuf.byteLength + result.byteLength, - ) + // console.log( + // JSON.stringify(obj).length, + // readSchemaBuf.byteLength + result.byteLength, + // ) }) From 69874e022b707669a50e147457fb7d6d88268f92 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 11:03:29 +0100 Subject: [PATCH 240/449] wip --- _string.ts | 64 ----------------------------------- src/db-client/query2/index.ts | 13 +++++-- test/query/ast.ts | 13 +++---- test/types.perf.ts | 7 +++- 4 files changed, 24 insertions(+), 73 deletions(-) delete mode 100644 _string.ts diff --git a/_string.ts b/_string.ts deleted file mode 100644 index e2b1d6c97e..0000000000 --- a/_string.ts +++ /dev/null @@ -1,64 +0,0 @@ -import native from '../native.js' -import { Ctx } from './_modify/Ctx.js' -import { resize } from './_modify/resize.js' -import { ENCODER, makeTmpBuffer, writeUint32 } from '../utils/uint8.js' -import { COMPRESSED, NOT_COMPRESSED } from '../protocol/index.js' -import { LangCode, LangCodeEnum } from '../zigTsExports.js' - -const { getUint8Array: getTmpBuffer } = makeTmpBuffer(4096) // the usual page size? - -export const write = ( - ctx: Ctx, - value: string, - offset: number, - lang: LangCodeEnum, - noCompression: boolean, -): number => { - const buf = ctx.buf - value = value.normalize('NFKD') - buf[offset] = lang - const { written: l } = ENCODER.encodeInto(value, buf.subarray(offset + 2)) - const crc = native.crc32(buf.subarray(offset + 2, offset + 2 + l)) - if (value.length > 200 && !noCompression) { - const insertPos = offset + 6 + l - const startPos = offset + 2 - const endPos = offset + 2 + l - const willEnd = insertPos + l - resize(ctx, willEnd) - buf.copyWithin(insertPos, startPos, endPos) - const size = native.compress(buf, offset + 6, l) - if (size === 0) { - resize(ctx, l + 6) - buf[offset + 1] = NOT_COMPRESSED - ENCODER.encodeInto(value, buf.subarray(offset + 2)) - writeUint32(buf, crc, offset + l + 2) - return l + 6 - } else { - resize(ctx, size + 10) - let len = l - buf[offset + 1] = COMPRESSED - writeUint32(buf, len, offset + 2) - writeUint32(buf, crc, offset + size + 6) - return size + 10 - } - } else { - buf[offset + 1] = NOT_COMPRESSED - writeUint32(buf, crc, offset + 2 + l) - return l + 6 - } -} - -export const stringCompress = (str: string): Uint8Array => { - const s = str.normalize('NFKD') - const tmpCompressBlock = getTmpBuffer(2 * native.stringByteLength(s) + 10) - const l = write( - { buf: tmpCompressBlock } as Ctx, - str, - 0, - LangCode.none, - false, - ) - const nBuffer = new Uint8Array(l) - nBuffer.set(tmpCompressBlock.subarray(0, l)) - return nBuffer -} diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 2abdcd11c6..29f550b1cf 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -79,14 +79,23 @@ class QueryBranch< op: Operator, val: InferPathType, opts?: FilterOpts, - ): this { + ): this & FilterMethods { const target = traverse((this.ast.filter ??= {}), prop as string) target.ops ??= [] target.ops.push({ op, val }) - return this + return { + ...this, + and: this.filter, + or: this.filter, + } } } +type FilterMethods = { + and: T['filter'] + or: T['filter'] +} + export class BasedQuery2< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, diff --git a/test/query/ast.ts b/test/query/ast.ts index a0aa547dc7..8ea6156d17 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -51,12 +51,10 @@ await test('query types', async (t) => { nl: 'mijn text', en: 'my text', }, - annoyingThings: [ - { - id: 2, - // $snurk: true, - }, - ], + }) + + const userB = db.create('user', { + isNice: false, }) db.create('soAnnoy', { @@ -74,9 +72,12 @@ await test('query types', async (t) => { 'friend', 'friend.$rank', ) + .filter('isNice', '=', false) + .and('name', '=', 'youzi') const result = await query.get() + console.log('--->', result) // for (const { name, isNice, otherUsers, friend } of result) { // // const friendName = friend?.name // const friendFriend = friend?.isNice diff --git a/test/types.perf.ts b/test/types.perf.ts index ac8069d65f..2d2c830abb 100644 --- a/test/types.perf.ts +++ b/test/types.perf.ts @@ -15,7 +15,12 @@ await test('create and access many types', async (t) => { const rndType = () => `type${prng(1, NR_TYPES)}` const client = await db.setSchema({ - types: Object.fromEntries(Array.from({ length: 16384 }, (_, i) => [ `type${i + 1}`, { bool: 'boolean' } ])), + types: Object.fromEntries( + Array.from({ length: 16384 }, (_, i) => [ + `type${i + 1}`, + { bool: 'boolean' }, + ]), + ), }) await perf( From 049659c40df7a43622bcb09144cb54eb495aa768 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Fri, 13 Feb 2026 11:16:06 +0100 Subject: [PATCH 241/449] fix --- test/include/thread.perf.ts | 46 ++++++++++++++++++++++++++++++++++++- test/query-ast/include.ts | 29 +++++++++++++++-------- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/test/include/thread.perf.ts b/test/include/thread.perf.ts index e3760c07d3..99d42b468b 100644 --- a/test/include/thread.perf.ts +++ b/test/include/thread.perf.ts @@ -207,6 +207,50 @@ await test('include', async (t) => { .include('nr') // 'start', 'end', 'target' .filter('target.nr', '>', 1001) // .or('nr', '=', 100) + // .or('nr', '=', 200) + // .or((p) => { + // p.filter('nr', 10).and('nr', 20) + // }) + // .and((p) => { + // p.filter('y', 20) + // p.or('x', 10) + // }) + + /* + { + filter: { + props: {nr" '>' 10001} + and: { + props: { y: [10]} + or: { props: { x: 10}} + } + } + + } + + { + filter: { + props: {nr" '>' 10001} + or: { + props: { nr: [10,20]} + } + } + + } + + { + filter: { + props: {nr" '>' 10001} + or: { + or: { + } + } + } + + } + + */ + .range(0, 10e6) .get() .inspect() @@ -254,7 +298,7 @@ await test.skip('default', async (t) => { special: { type: 'vector', size: 4, - baseType: 'number', + baseType: 'float64', // TODO //default: new Uint8Array([0, 0, 0, 0]), }, diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 1159222455..7e19cc3d75 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -105,18 +105,27 @@ await test('include', async (t) => { }, }, }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 670 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 15 }] }, - // }, - // }, - // }, + or: { + props: { + y: { ops: [{ op: '=', val: 670 }] }, + }, + or: { + props: { + y: { ops: [{ op: '=', val: 15 }] }, + }, + }, + }, }, + // filter('flap', '=', 9999) + // .and(q => { + // q.filter('y', '=', 100) + // .or('y', '=', 3) + // .or('y', '=', 4) + // }) + // .or('y','=', 670) + // .or('y', '=', 15) + // (y == 0 && (y == 10 || y == 3 || y == 4)) || y == 67 props: { From 776a3cfbce17d85d609b878cb5eec157d3f432d3 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 11:44:30 +0100 Subject: [PATCH 242/449] Minor opt for mempool_new_slab() --- clibs/lib/selva/mem/mempool.c | 55 ++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/clibs/lib/selva/mem/mempool.c b/clibs/lib/selva/mem/mempool.c index 95a5b46ad4..dd2175fab0 100644 --- a/clibs/lib/selva/mem/mempool.c +++ b/clibs/lib/selva/mem/mempool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 SAULX + * Copyright (c) 2020-2026 SAULX * SPDX-License-Identifier: MIT */ #if defined(__STDC_LIB_EXT1__) @@ -46,11 +46,13 @@ #define MEMPOOL_GROWING_FREE_LIST 1 #endif -char *mempool_get_obj(const struct mempool *mempool, struct mempool_chunk *chunk) { +char *mempool_get_obj(const struct mempool *mempool, struct mempool_chunk *chunk) +{ return ((char *)chunk) + sizeof(struct mempool_chunk) + PAD(sizeof(struct mempool_chunk), mempool->obj_align); } -static struct mempool_chunk *get_chunk(const struct mempool *mempool, void *obj) { +static struct mempool_chunk *get_chunk(const struct mempool *mempool, void *obj) +{ char *p = ((char *)obj) - PAD(sizeof(struct mempool_chunk), mempool->obj_align) - sizeof(struct mempool_chunk); return (struct mempool_chunk *)p; @@ -61,11 +63,13 @@ static struct mempool_slab *get_slab(const struct mempool_chunk *chunk) return (struct mempool_slab *)(chunk->slab & ~(uintptr_t)1); } -struct mempool_slab *mempool_get_slab(const struct mempool *mempool, void *obj) { +struct mempool_slab *mempool_get_slab(const struct mempool *mempool, void *obj) +{ return get_slab(get_chunk(mempool, obj)); } -struct mempool_slab_info mempool_slab_info(const struct mempool *mempool) { +struct mempool_slab_info mempool_slab_info(const struct mempool *mempool) +{ const size_t slab_size = (size_t)mempool->slab_size_kb * 1024; const size_t chunk_size = ALIGNED_SIZE( sizeof(struct mempool_chunk) + @@ -84,7 +88,8 @@ struct mempool_slab_info mempool_slab_info(const struct mempool *mempool) { }; } -void mempool_init(struct mempool *mempool, size_t slab_size, size_t obj_size, size_t obj_align) { +void mempool_init(struct mempool *mempool, size_t slab_size, size_t obj_size, size_t obj_align) +{ assert(slab_size - sizeof(struct mempool_slab) > obj_size && slab_size / 1024 > 0 && slab_size / 1024 < UINT16_MAX && @@ -100,7 +105,8 @@ void mempool_init(struct mempool *mempool, size_t slab_size, size_t obj_size, si mempool->advice = MEMPOOL_ADV_NORMAL | MEMPOOL_ADV_HP_NO; } -void mempool_init2(struct mempool *mempool, size_t slab_size, size_t obj_size, size_t obj_align, enum mempool_advice advice) { +void mempool_init2(struct mempool *mempool, size_t slab_size, size_t obj_size, size_t obj_align, enum mempool_advice advice) +{ mempool_init(mempool, slab_size, obj_size, obj_align); mempool->advice = advice; } @@ -108,11 +114,13 @@ void mempool_init2(struct mempool *mempool, size_t slab_size, size_t obj_size, s /** * Free slab that was allocated in mempool */ -static void mempool_free_slab(const struct mempool *mempool, struct mempool_slab *slab) { +static void mempool_free_slab(const struct mempool *mempool, struct mempool_slab *slab) +{ (void)munmap(slab, mempool->slab_size_kb * 1024); } -void mempool_destroy(struct mempool *mempool) { +void mempool_destroy(struct mempool *mempool) +{ /* * We don't keep track of the slab pointers because we assume the user to * know the slabs and return every single one of them before destroying the @@ -129,7 +137,8 @@ void mempool_destroy(struct mempool *mempool) { memset(mempool, 0, sizeof(*mempool)); } -void mempool_gc(struct mempool *mempool) { +void mempool_gc(struct mempool *mempool) +{ struct mempool_slab_info info = mempool_slab_info(mempool); /* @@ -185,7 +194,8 @@ static int defrag_cmp(void *ctx_, const void *a, const void *b) return chunk_a < chunk_b; } -void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, const void*)) { +void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, const void*)) +{ struct mempool_slab_info slab_nfo = mempool_slab_info(mempool); struct mempool_defrag_ctx ctx = { .mempool = mempool, @@ -243,8 +253,12 @@ void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, con /** * Allocate a new slab using mmap(). */ -static int mempool_new_slab(struct mempool *mempool) { +static void mempool_new_slab(struct mempool *mempool) +{ const size_t bsize = mempool->slab_size_kb * 1024; +#if !defined(__linux__) + constexpr +#endif int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; struct mempool_slab *slab; @@ -268,7 +282,7 @@ static int mempool_new_slab(struct mempool *mempool) { } #endif perror("Failed to allocate a slab"); - return 1; + abort(); } #if defined(__linux__) || defined(__MACH__) @@ -317,20 +331,14 @@ static int mempool_new_slab(struct mempool *mempool) { } MEMPOOL_FOREACH_CHUNK_END(); SLIST_INSERT_HEAD(&mempool->slabs, slab, next_slab); - - return 0; } -void *mempool_get(struct mempool *mempool) { +void *mempool_get(struct mempool *mempool) +{ struct mempool_chunk *next; if (LIST_EMPTY(&mempool->free_chunks)) { - int err; - - err = mempool_new_slab(mempool); - if (err) { - abort(); - } + mempool_new_slab(mempool); } next = LIST_FIRST(&mempool->free_chunks); @@ -341,7 +349,8 @@ void *mempool_get(struct mempool *mempool) { return mempool_get_obj(mempool, next); } -void mempool_return(struct mempool *mempool, void *p) { +void mempool_return(struct mempool *mempool, void *p) +{ struct mempool_chunk *chunk = get_chunk(mempool, p); LIST_INSERT_HEAD(&mempool->free_chunks, chunk, next_free); From 0cd6188ac58de5c4efcc9d2d9b385d2efacd31d2 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 12:15:57 +0100 Subject: [PATCH 243/449] Inline selva_foreach_block() when possible This reduces indirect function calls and should make the loops slightly faster. This make `saveCommon()` 6.78 % faster. --- clibs/include/selva/db.h | 28 +++++++++++++++++++++++++--- clibs/lib/selva/db.c | 22 ++-------------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 2e75eb5643..fb4cca276a 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -139,9 +139,6 @@ inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) ; #endif -SELVA_EXPORT -void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx); - SELVA_EXPORT inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te) #ifndef __zig @@ -213,6 +210,31 @@ inline node_id_t selva_block_i2end(const struct SelvaTypeEntry *te, block_id_t b ; #endif +SELVA_EXPORT +inline void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx) +#ifndef __zig +{ + for (size_t ti = 0; ti < db->nr_types; ti++) { + struct SelvaTypeEntry *te = &db->types[ti]; + struct SelvaTypeBlocks *blocks = te->blocks; + + for (block_id_t block_i = 0; block_i < blocks->len; block_i++) { + struct SelvaTypeBlock *block = &blocks->blocks[block_i]; + + /* + * Note that we call it or_mask because the cb() is called if any + * bit of the mask is set in the status. + */ + if (atomic_load_explicit(&block->status.atomic, memory_order_consume) & or_mask) { + cb(ctx, db, te, block_i, selva_block_i2start(te, block_i)); + } + } + } +} +#else +; +#endif + /** * \addtogroup block_status * @{ diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 5673a94054..177854992b 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -427,26 +427,6 @@ struct SelvaTypeBlock *selva_get_block(struct SelvaTypeBlocks *blocks, node_id_t return &blocks->blocks[block_i]; } -void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx) -{ - for (size_t ti = 0; ti < db->nr_types; ti++) { - struct SelvaTypeEntry *te = &db->types[ti]; - struct SelvaTypeBlocks *blocks = te->blocks; - - for (block_id_t block_i = 0; block_i < blocks->len; block_i++) { - struct SelvaTypeBlock *block = &blocks->blocks[block_i]; - - /* - * Note that we call it or_mask because the cb() is called if any - * bit of the mask is set in the status. - */ - if (atomic_load_explicit(&block->status.atomic, memory_order_consume) & or_mask) { - cb(ctx, db, te, block_i, selva_block_i2start(te, block_i)); - } - } - } -} - extern inline node_type_t selva_get_max_type(const struct SelvaDb *db); extern inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type); @@ -467,6 +447,8 @@ extern inline node_id_t selva_block_i2start(const struct SelvaTypeEntry *te, blo extern inline node_id_t selva_block_i2end(const struct SelvaTypeEntry *te, block_id_t block_i); +extern inline void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx); + extern inline enum SelvaTypeBlockStatus selva_block_status_get(const struct SelvaTypeEntry *te, block_id_t block_i); extern inline void selva_block_status_replace(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus status); From 010ff0d2b8732316b161f45685670e69c658f226 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 13:06:30 +0100 Subject: [PATCH 244/449] add filter ast creation --- src/db-client/query2/index.ts | 123 +++++++++++++++++++++++-- test/query/ast.ts | 163 ++++++++++++++++++++-------------- 2 files changed, 208 insertions(+), 78 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 29f550b1cf..32e2550852 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,4 +1,4 @@ -import type { FilterLeaf, QueryAst } from '../../db-query/ast/ast.js' +import type { FilterAst, FilterLeaf, QueryAst } from '../../db-query/ast/ast.js' import type { PickOutput, ResolveInclude, @@ -70,6 +70,11 @@ class QueryBranch< return this as any } + filter( + fn: ( + filter: FilterFn, + ) => FilterBranch>, + ): FilterBranch filter< P extends | keyof (ResolvedProps & EdgeProps) @@ -79,18 +84,83 @@ class QueryBranch< op: Operator, val: InferPathType, opts?: FilterOpts, - ): this & FilterMethods { - const target = traverse((this.ast.filter ??= {}), prop as string) + ): FilterBranch + filter(prop: any, op?: any, val?: any, opts?: any): FilterBranch { + this.#filterGroup ??= this.ast.filter ??= {} + return this.#addFilter(prop, op, val, opts, false) + } + + and( + fn: ( + filter: FilterFn, + ) => FilterBranch>, + ): FilterBranch + and< + P extends + | keyof (ResolvedProps & EdgeProps) + | Path, + >( + prop: P, + op: Operator, + val: InferPathType, + opts?: FilterOpts, + ): FilterBranch + and(prop: any, op?: any, val?: any, opts?: any): FilterBranch { + return this.filter(prop, op, val, opts) + } + + or( + fn: ( + filter: FilterFn, + ) => FilterBranch>, + ): FilterBranch + or< + P extends + | keyof (ResolvedProps & EdgeProps) + | Path, + >( + prop: P, + op: Operator, + val: InferPathType, + opts?: FilterOpts, + ): FilterBranch + or(prop: any, op?: any, val?: any, opts?: any): FilterBranch { + this.#filterGroup ??= this.ast.filter ??= {} + this.#filterGroup = this.#filterGroup.or ??= {} + return this.#addFilter(prop, op, val, opts, true) + } + + #filterGroup?: FilterAst + #addFilter( + prop: any, + op: any, + val: any, + opts: any, + isOr: boolean, + ): FilterBranch { + if (typeof prop === 'function') { + prop((...args) => { + const target = isOr + ? this.#filterGroup! + : (this.#filterGroup!.and ??= {}) + const branch = new QueryBranch(target) + branch.#filterGroup = target + ;(branch.filter as any)(...args) + return branch + }) + return this as any + } + + const target = traverse(this.#filterGroup, prop as string) target.ops ??= [] target.ops.push({ op, val }) - return { - ...this, - and: this.filter, - or: this.filter, - } + return this as any } } +type FilterBranch = Omit & + FilterMethods + type FilterMethods = { and: T['filter'] or: T['filter'] @@ -114,7 +184,8 @@ export class BasedQuery2< ) { super({}) this.ast.type = type as string - this.ast.target = target + if (target) this.ast.target = target + this.db = db } db: DbClient @@ -141,6 +212,40 @@ export class BasedQuery2< } } +type FilterFn< + S extends { types: any }, + T extends keyof S['types'], + EdgeProps extends Record, +> = FilterSignature< + S, + T, + EdgeProps, + FilterBranch> +> + +type FilterSignature< + S extends { types: any }, + T extends keyof S['types'], + EdgeProps extends Record, + Result, +> = { + ( + fn: ( + filter: FilterFn, + ) => FilterBranch>, + ): Result + < + P extends + | keyof (ResolvedProps & EdgeProps) + | Path, + >( + prop: P, + op: Operator, + val: InferPathType, + opts?: FilterOpts, + ): Result +} + type SelectFn = < P extends keyof ResolvedProps, >( diff --git a/test/query/ast.ts b/test/query/ast.ts index 8ea6156d17..5b86b822ba 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -1,91 +1,116 @@ import { $buffer } from '../../src/db-client/query2/result.js' -import { testDb } from '../shared/index.js' +import { deepEqual, testDb } from '../shared/index.js' import test from '../shared/test.js' -await test('query types', async (t) => { +await test('query ast creation', async (t) => { const db = await testDb(t, { locales: { en: true, nl: true, }, types: { - soAnnoy: { - title: 'string', - - users: { - items: { - ref: 'user', - prop: 'annoyingThings', - }, - }, - }, user: { name: 'string', isNice: 'boolean', - textField: 'text', - friend: { - ref: 'user', - prop: 'friend', - $rank: 'number', - }, - otherUsers: { - items: { - ref: 'user', - prop: 'otherUsers', - $role: 'string', - }, - }, - annoyingThings: { - items: { - ref: 'soAnnoy', - prop: 'users', - }, - }, }, }, }) - const userA = db.create('user', { - isNice: true, - textField: { - nl: 'mijn text', - en: 'my text', - }, - }) + { + const query = db + .query2('user') + .filter('isNice', '=', false) + .and('name', '=', 'youzi') - const userB = db.create('user', { - isNice: false, - }) + deepEqual(query.ast, { + type: 'user', + filter: { + props: { + isNice: { ops: [{ op: '=', val: false }] }, + name: { ops: [{ op: '=', val: 'youzi' }] }, + }, + }, + }) + } - db.create('soAnnoy', { - title: 'super annoying', - users: [userA], - }) + { + const query = db + .query2('user') + .filter('isNice', '=', false) + .and('name', '=', 'youzi') + .or('name', '=', 'james') + + deepEqual(query.ast, { + type: 'user', + filter: { + props: { + isNice: { ops: [{ op: '=', val: false }] }, + name: { ops: [{ op: '=', val: 'youzi' }] }, + }, + or: { + props: { + name: { ops: [{ op: '=', val: 'james' }] }, + }, + }, + }, + }) + } - const query = db - .query2('user') - .include( - 'isNice', - 'name', - 'otherUsers', - 'textField', - 'friend', - 'friend.$rank', - ) - .filter('isNice', '=', false) - .and('name', '=', 'youzi') + { + const query = db + .query2('user') + .filter('isNice', '=', false) + .and('name', '=', 'youzi') + .or('name', '=', 'james') + .and('isNice', '=', false) + + deepEqual(query.ast, { + type: 'user', + filter: { + props: { + isNice: { ops: [{ op: '=', val: false }] }, + name: { ops: [{ op: '=', val: 'youzi' }] }, + }, + or: { + props: { + name: { ops: [{ op: '=', val: 'james' }] }, + isNice: { ops: [{ op: '=', val: false }] }, + }, + }, + }, + }) + } - const result = await query.get() + { + const query = db + .query2('user') + .filter((filter) => filter('name', '=', 'youzi').or('isNice', '=', true)) + .or((filter) => filter('name', '=', 'james').or('isNice', '=', false)) - console.log('--->', result) - // for (const { name, isNice, otherUsers, friend } of result) { - // // const friendName = friend?.name - // const friendFriend = friend?.isNice - // for (const item of otherUsers) { - // const name: string = item.name - // const isNice: boolean = item.isNice - // const id: number = item.id - // const textField: { nl: string; en: string } = item.textField - // } - // } + deepEqual(query.ast, { + type: 'user', + filter: { + and: { + props: { + name: { ops: [{ op: '=', val: 'youzi' }] }, + }, + or: { + props: { + isNice: { ops: [{ op: '=', val: true }] }, + }, + }, + }, + or: { + props: { + name: { ops: [{ op: '=', val: 'james' }] }, + }, + or: { + props: { + isNice: { ops: [{ op: '=', val: false }] }, + }, + }, + }, + }, + }) + } }) From 1e5b845c0afa3f595be678ef0d26341fcb811e5d Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 13:34:07 +0100 Subject: [PATCH 245/449] add isolated query() --- src/db-client/query2/index.ts | 26 +++++++++++- test/query/ast.ts | 75 ++++++++++++++++++++++++++++------- 2 files changed, 85 insertions(+), 16 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 32e2550852..c549c0d6b5 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -166,6 +166,31 @@ type FilterMethods = { or: T['filter'] } +export function query< + S extends { types: any }, + T extends keyof S['types'] & string = keyof S['types'] & string, +>(type: T): QueryBranch + +export function query< + S extends { types: any }, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + type: T, + id: number | Partial>, +): QueryBranch + +export function query< + S extends { types: any }, + T extends keyof S['types'] & string = keyof S['types'] & string, +>( + type: T, + id?: number | Partial>, +): QueryBranch { + const ast: any = { type } + if (id) ast.target = id + return new QueryBranch(ast) +} + export class BasedQuery2< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, @@ -185,7 +210,6 @@ export class BasedQuery2< super({}) this.ast.type = type as string if (target) this.ast.target = target - this.db = db } db: DbClient diff --git a/test/query/ast.ts b/test/query/ast.ts index 5b86b822ba..381da90b2b 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -1,9 +1,9 @@ -import { $buffer } from '../../src/db-client/query2/result.js' +import { query } from '../../src/db-client/query2/index.js' import { deepEqual, testDb } from '../shared/index.js' import test from '../shared/test.js' await test('query ast creation', async (t) => { - const db = await testDb(t, { + const schema = { locales: { en: true, nl: true, @@ -14,15 +14,16 @@ await test('query ast creation', async (t) => { isNice: 'boolean', }, }, - }) + } as const + + type Schema = typeof schema { - const query = db - .query2('user') + const q = query('user') .filter('isNice', '=', false) .and('name', '=', 'youzi') - deepEqual(query.ast, { + deepEqual(q.ast, { type: 'user', filter: { props: { @@ -34,13 +35,12 @@ await test('query ast creation', async (t) => { } { - const query = db - .query2('user') + const q = query('user') .filter('isNice', '=', false) .and('name', '=', 'youzi') .or('name', '=', 'james') - deepEqual(query.ast, { + deepEqual(q.ast, { type: 'user', filter: { props: { @@ -57,14 +57,13 @@ await test('query ast creation', async (t) => { } { - const query = db - .query2('user') + const q = query('user') .filter('isNice', '=', false) .and('name', '=', 'youzi') .or('name', '=', 'james') .and('isNice', '=', false) - deepEqual(query.ast, { + deepEqual(q.ast, { type: 'user', filter: { props: { @@ -82,12 +81,11 @@ await test('query ast creation', async (t) => { } { - const query = db - .query2('user') + const q = query('user') .filter((filter) => filter('name', '=', 'youzi').or('isNice', '=', true)) .or((filter) => filter('name', '=', 'james').or('isNice', '=', false)) - deepEqual(query.ast, { + deepEqual(q.ast, { type: 'user', filter: { and: { @@ -113,4 +111,51 @@ await test('query ast creation', async (t) => { }, }) } + + { + const q = query('user') + .filter((filter) => filter('name', '=', 'youzi').or('isNice', '=', true)) + + .or((filter) => + filter((filter) => + filter('name', '=', 'james').or('isNice', '!=', true), + ) + .or('isNice', '=', false) + .and('name', '!=', 'olli'), + ) + + deepEqual(q.ast, { + type: 'user', + filter: { + and: { + props: { + name: { ops: [{ op: '=', val: 'youzi' }] }, + }, + or: { + props: { + isNice: { ops: [{ op: '=', val: true }] }, + }, + }, + }, + or: { + and: { + props: { + name: { ops: [{ op: '=', val: 'james' }] }, + }, + or: { + props: { + isNice: { ops: [{ op: '!=', val: true }] }, + }, + }, + }, + or: { + props: { + isNice: { ops: [{ op: '=', val: false }] }, + name: { ops: [{ op: '!=', val: 'olli' }] }, + }, + }, + }, + }, + }) + } }) From 4d8b8791dd5b1154c2d7a59cdb1e505f1334d9d6 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 13:53:02 +0100 Subject: [PATCH 246/449] update query() --- GEMINI.md | 31 +++++++++++-------------------- src/db-client/query2/index.ts | 6 ++++-- src/schema/schema/schema.ts | 18 ++++++++++-------- test/query/ast.ts | 22 ++++++++++------------ 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/GEMINI.md b/GEMINI.md index 3fc416fe8e..7a6117167b 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -81,18 +81,14 @@ const a = (ctx) => { - Prefer self-documenting code (clear variable names, etc.). - If you must add a comment, keep it minimal and explain the _why_, not the _what_. -## Running db Tests +## Running Tests -The following instructions apply only to the `@based/db` package. Other packages have their own test runners. +The project uses a custom test runner. **Always use `npm test`** to run tests. Do **NOT** use `npx tsx` or `node` directly on test files. -The `db` package uses a custom test runner. To run the tests, you can use `npm run test` from the `packages/db` directory, or `npm run test -w @based/db` from the root of the project. You can pass arguments to filter the tests. +You can run tests from the root simply with `npm test`. You can pass arguments to filter the tests. ```bash -# From packages/db -npm run test -- [filters] - -# From root -npm run test -w @based/db -- [filters] +npm test [filter] ``` There is also a `test-fast` script that skips the build step. @@ -111,28 +107,23 @@ You can filter which tests to run by providing one or more filter arguments. - **Run all tests:** ```bash - # from packages/db - npm run test + npm test ``` -- **Run all tests and stop on the first failure:** +- **Run tests for a specific file or folder (e.g. `query/ast`):** ```bash - # from packages/db - npm run test -- stopOnFail + npm test query/ast ``` -- **Run tests in files with "view" in the path:** +- **Run all tests and stop on the first failure:** ```bash - # from packages/db - npm run test -- view + npm test stopOnFail ``` - **Run tests with "observe" in the name, in files with "view" in the path:** ```bash - # from packages/db - npm run test -- view:observe + npm test view:observe ``` - **Run all tests 10 times:** ```bash - # from packages/db - npm run test -- 10 + npm test 10 ``` ## Zig version diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index c549c0d6b5..f4829041a1 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -166,13 +166,15 @@ type FilterMethods = { or: T['filter'] } +// This overload is for when the user provides NO schema argument, rely on generic default or explicit generic export function query< - S extends { types: any }, + S extends { types: any } = { types: any }, T extends keyof S['types'] & string = keyof S['types'] & string, >(type: T): QueryBranch +// This overload is for when the user provides NO schema argument + ID, rely on generic default or explicit generic export function query< - S extends { types: any }, + S extends { types: any } = { types: any }, T extends keyof S['types'] & string = keyof S['types'] & string, >( type: T, diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index d298478d0a..f8dcfd72b6 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -70,6 +70,7 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( ? I : never +// Helper to find Props in other types that reference TName with a specific 'prop' field // Helper to find Props in other types that reference TName with a specific 'prop' field type GetBackRefs = UnionToIntersection< { @@ -111,14 +112,15 @@ export type ResolvedProps< TName extends keyof Types, Props = NormalizeType extends { props: infer P } ? P : {}, BackRefs = GetBackRefs, -> = { - [K in keyof (Props & - ([BackRefs] extends [never] ? {} : Omit)) as Extract< - K, - string - >]: (Props & - ([BackRefs] extends [never] ? {} : Omit))[K] -} +> = string extends keyof Types + ? any + : { + [K in keyof (Props & + ([BackRefs] extends [never] + ? {} + : Omit)) as Extract]: (Props & + ([BackRefs] extends [never] ? {} : Omit))[K] + } type NormalizeType = T extends { props: infer P } ? Omit & { props: { [K in keyof P]: NormalizeProp } } diff --git a/test/query/ast.ts b/test/query/ast.ts index 381da90b2b..e88d8f8b8b 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -3,23 +3,21 @@ import { deepEqual, testDb } from '../shared/index.js' import test from '../shared/test.js' await test('query ast creation', async (t) => { - const schema = { + type Schema = { locales: { - en: true, - nl: true, - }, + en: true + nl: true + } types: { user: { - name: 'string', - isNice: 'boolean', - }, - }, - } as const - - type Schema = typeof schema + name: 'string' + isNice: 'boolean' + } + } + } { - const q = query('user') + const q = query('user') .filter('isNice', '=', false) .and('name', '=', 'youzi') From 06fc85f845bb736332f0b1119cc5fde7970efd5d Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 14:13:33 +0100 Subject: [PATCH 247/449] ast building for aggregate --- src/db-client/query2/index.ts | 85 +++++++++++++++++++++++++++++++++++ src/db-client/query2/types.ts | 7 +++ src/db-query/ast/ast.ts | 1 + test/query/ast.ts | 43 ++++++++++++++++++ test/query/types.ts | 28 ++++++++++++ 5 files changed, 164 insertions(+) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index f4829041a1..8f0704bcd0 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -9,12 +9,18 @@ import type { InferPathType, FilterEdges, InferSchemaOutput, + NumberPaths, } from './types.js' import type { ResolvedProps, SchemaOut } from '../../schema/index.js' import { astToQueryCtx } from '../../db-query/ast/toCtx.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import type { DbClient } from '../../sdk.js' import { proxyResult } from './result.js' +import type { + StepInput, + IntervalString, + aggFnOptions, +} from '../query/aggregates/types.js' class QueryBranch< S extends { types: any } = { types: any }, @@ -130,6 +136,85 @@ class QueryBranch< return this.#addFilter(prop, op, val, opts, true) } + sum

>(...props: P[]): this { + this.ast.sum ??= { props: [] } + this.ast.sum.props.push(...(props as string[])) + return this + } + + count(): this { + this.ast.count = {} + return this + } + + cardinality(...props: string[]): this { + this.ast.cardinality ??= { props: [] } + this.ast.cardinality.props.push(...props) + return this + } + + avg

>(...props: P[]): this { + this.ast.avg ??= { props: [] } + this.ast.avg.props.push(...(props as string[])) + return this + } + + hmean

>(...props: P[]): this { + this.ast.harmonicMean ??= { props: [] } + this.ast.harmonicMean.props.push(...(props as string[])) + return this + } + + max

>(...props: P[]): this { + this.ast.max ??= { props: [] } + this.ast.max.props.push(...(props as string[])) + return this + } + + min

>(...props: P[]): this { + this.ast.min ??= { props: [] } + this.ast.min.props.push(...(props as string[])) + return this + } + + stddev

>( + prop: P | P[], + opts?: aggFnOptions, + ): this { + this.ast.stddev ??= { props: [] } + const props = Array.isArray(prop) ? prop : [prop] + this.ast.stddev.props.push(...(props as string[])) + if (opts?.mode) { + this.ast.stddev.samplingMode = opts.mode + } + return this + } + + var

>(prop: P | P[], opts?: aggFnOptions): this { + this.ast.variance ??= { props: [] } + const props = Array.isArray(prop) ? prop : [prop] + this.ast.variance.props.push(...(props as string[])) + if (opts?.mode) { + this.ast.variance.samplingMode = opts.mode + } + return this + } + + groupBy(prop: string, step?: StepInput): this { + this.ast.groupBy = { prop } + if (step) { + if (typeof step === 'object') { + const s = step as any + if (s.step) this.ast.groupBy.step = s.step + if (s.timeZone) this.ast.groupBy.timeZone = s.timeZone + if (s.display) this.ast.groupBy.display = s.display + } else { + this.ast.groupBy.step = step + } + } + return this + } + #filterGroup?: FilterAst #addFilter( prop: any, diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 04c9806d98..1de440ed4a 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -285,3 +285,10 @@ export type InferPathType< : never : never : never + +export type NumberPaths< + S extends { types: any; locales?: any }, + T extends keyof S['types'], +> = { + [K in Path]: InferPathType extends number ? K : never +}[Path] diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 8ccdfc75da..5341687984 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -67,6 +67,7 @@ export type QueryAst = { sort?: { prop: string; order: 'asc' | 'desc' } props?: Record edges?: QueryAst + // aggregate options count?: { props?: string } sum?: { props: string[] } cardinality?: { props: string[] } diff --git a/test/query/ast.ts b/test/query/ast.ts index e88d8f8b8b..af2dea0eb6 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -12,6 +12,7 @@ await test('query ast creation', async (t) => { user: { name: 'string' isNice: 'boolean' + age: 'number' } } } @@ -156,4 +157,46 @@ await test('query ast creation', async (t) => { }, }) } + + { + const q = query('user').sum('age') + deepEqual(q.ast, { + type: 'user', + sum: { props: ['age'] }, + }) + } + + { + const q = query('user') + .count() + .cardinality('name') + .avg('age') + .hmean('age') + .max('age') + .min('age') + .stddev('age', { mode: 'population' }) + .var('age', { mode: 'sample' }) + .groupBy('name') + + deepEqual(q.ast, { + type: 'user', + count: {}, + cardinality: { props: ['name'] }, + avg: { props: ['age'] }, + harmonicMean: { props: ['age'] }, + max: { props: ['age'] }, + min: { props: ['age'] }, + stddev: { props: ['age'], samplingMode: 'population' }, + variance: { props: ['age'], samplingMode: 'sample' }, + groupBy: { prop: 'name' }, + }) + } + + { + const q = query('user').groupBy('age', 10) + deepEqual(q.ast, { + type: 'user', + groupBy: { prop: 'age', step: 10 }, + }) + } }) diff --git a/test/query/types.ts b/test/query/types.ts index 8622e90b76..de511dd9ad 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -254,6 +254,34 @@ await test('query types', async (t) => { // console.log({ id, myEnum, nonExistent }) // } } + + { + // Sum aggregates + const query = db.query2('everything').sum('n', 'i8').sum('card') + const queryInvalid = db.query2('everything') + // @ts-expect-error + queryInvalid.sum('s') + // @ts-expect-error + queryInvalid.sum('b') + + query.avg('n').hmean('n').max('n').min('n').stddev('n').var('n') + + query.cardinality('s') + query.groupBy('s') + + // @ts-expect-error + queryInvalid.avg('s') + // @ts-expect-error + queryInvalid.hmean('s') + // @ts-expect-error + queryInvalid.max('s') + // @ts-expect-error + queryInvalid.min('s') + // @ts-expect-error + queryInvalid.stddev('s') + // @ts-expect-error + queryInvalid.var('s') + } }) await test('query types', async (t) => { From e9e7ff8085da396b10723bc8826948c1ea7171e6 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 15:02:40 +0100 Subject: [PATCH 248/449] Prealloc mempool slabs before loading nodes --- clibs/lib/selva/include/mempool.h | 4 +- clibs/lib/selva/io/dump.c | 12 ++++ clibs/lib/selva/mem/mempool.c | 106 +++++++++++++++++++++++++----- 3 files changed, 103 insertions(+), 19 deletions(-) diff --git a/clibs/lib/selva/include/mempool.h b/clibs/lib/selva/include/mempool.h index 822feef256..1f1c5340d3 100644 --- a/clibs/lib/selva/include/mempool.h +++ b/clibs/lib/selva/include/mempool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 SAULX + * Copyright (c) 2020-2026 SAULX * SPDX-License-Identifier: MIT */ #pragma once @@ -105,6 +105,8 @@ void mempool_gc(struct mempool *mempool) */ void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, const void*)); +void mempool_prealloc(struct mempool *mempool, size_t nr_objects); + /** * Get a new object from the pool. */ diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index fef4257136..554834bc63 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -748,6 +748,18 @@ static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeE sdb_nr_nodes_t nr_nodes; io->sdb_read(&nr_nodes, sizeof(nr_nodes), 1, io); + if (nr_nodes == 0) { + return 0; + } + + /* + * Prealloc slabs before loading. + * TODO Partials. + * This is not always optimal with partials because we may already have + * enough free objects. Perhaps only do this on startup. + */ + mempool_prealloc(&te->nodepool, nr_nodes); + for (sdb_nr_nodes_t i = 0; i < nr_nodes; i++) { node_id_t node_id; diff --git a/clibs/lib/selva/mem/mempool.c b/clibs/lib/selva/mem/mempool.c index dd2175fab0..153bb76b8b 100644 --- a/clibs/lib/selva/mem/mempool.c +++ b/clibs/lib/selva/mem/mempool.c @@ -250,6 +250,36 @@ void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, con } MEMPOOL_FOREACH_SLAB_END(); } +static void add_new_slab2freelist(struct mempool *mempool, struct mempool_slab *slab) +{ + const struct mempool_slab_info info = mempool_slab_info(mempool); + typeof(mempool->free_chunks) *free_chunks = &mempool->free_chunks; +#ifdef MEMPOOL_GROWING_FREE_LIST + struct mempool_chunk *prev = nullptr; +#endif + + slab->nr_free = info.nr_objects; + + /* + * Add all new objects to the list of free objects in the pool. + */ + MEMPOOL_FOREACH_CHUNK_BEGIN(info, slab) { + chunk->slab = (uintptr_t)slab; /* also marked as free. */ +#ifdef MEMPOOL_GROWING_FREE_LIST + if (prev) { + LIST_INSERT_AFTER(prev, chunk, next_free); + } else { +#endif + LIST_INSERT_HEAD(free_chunks, chunk, next_free); +#ifdef MEMPOOL_GROWING_FREE_LIST + } + prev = chunk; +#endif + } MEMPOOL_FOREACH_CHUNK_END(); + + SLIST_INSERT_HEAD(&mempool->slabs, slab, next_slab); +} + /** * Allocate a new slab using mmap(). */ @@ -306,31 +336,71 @@ static void mempool_new_slab(struct mempool *mempool) } #endif - const struct mempool_slab_info info = mempool_slab_info(mempool); + add_new_slab2freelist(mempool, slab); +} - slab->nr_free = info.nr_objects; +void mempool_prealloc(struct mempool *mempool, size_t nr_objects) +{ + struct mempool_slab_info nfo = mempool_slab_info(mempool); + const size_t nr_slabs = (nr_objects + nfo.nr_objects - 1) / nfo.nr_objects; + const size_t slab_size = nr_slabs * mempool->slab_size_kb * 1024; + const size_t bsize = nr_slabs * slab_size; +#if !defined(__linux__) + constexpr +#endif + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; + struct mempool_slab *slabs; - /* - * Add all new objects to the list of free objects in the pool. - */ -#ifdef MEMPOOL_GROWING_FREE_LIST - struct mempool_chunk *prev = nullptr; + assert(nr_objects > 0); + + /* TODO Verify that huge pages can be partially unmapped later if necessary. */ +#if 0 && defined(__linux__) + if (bsize >= 2048 * 1024 && + (mempool->advice & (MEMPOOL_ADV_HP_SOFT | MEMPOOL_ADV_HP_HARD))) { + mmap_flags |= MAP_HUGETLB /* | MAP_HUGE_2MB */; + } #endif - MEMPOOL_FOREACH_CHUNK_BEGIN(info, slab) { - chunk->slab = (uintptr_t)slab; /* also marked as free. */ -#ifdef MEMPOOL_GROWING_FREE_LIST - if (prev) { - LIST_INSERT_AFTER(prev, chunk, next_free); - } else { + +#if defined(__linux__) +retry: #endif - LIST_INSERT_HEAD(&mempool->free_chunks, chunk, next_free); -#ifdef MEMPOOL_GROWING_FREE_LIST + slabs = mmap(0, bsize, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); + if (slabs == MAP_FAILED) { +#if defined(__linux__) + if ((mmap_flags & MAP_HUGETLB) && + (mempool->advice & MEMPOOL_ADV_HP_SOFT)) { + mmap_flags &= ~MAP_HUGETLB; + goto retry; } - prev = chunk; #endif - } MEMPOOL_FOREACH_CHUNK_END(); + perror("Failed to allocate a slabs"); + abort(); + } - SLIST_INSERT_HEAD(&mempool->slabs, slab, next_slab); +#if defined(__linux__) || defined(__MACH__) + if (mempool->advice & (MEMPOOL_ADV_RANDOM | MEMPOOL_ADV_SEQUENTIAL)) { + switch (mempool->advice & (MEMPOOL_ADV_RANDOM | MEMPOOL_ADV_SEQUENTIAL)) { + case MEMPOOL_ADV_RANDOM: + madvise(slabs, bsize, MADV_RANDOM); + break; + case MEMPOOL_ADV_SEQUENTIAL: + madvise(slabs, bsize, MADV_SEQUENTIAL); + break; + default: /* NOP */ + break; + } + } +#endif + +#if defined(__linux__) + if (bsize >= 2048 * 1024 && (mempool->advice & MEMPOOL_ADV_HP_THP)) { + (void)madvise(slabs, bsize, MADV_HUGEPAGE); + } +#endif + + for (size_t i = 0; i < nr_slabs; i++) { + add_new_slab2freelist(mempool, (typeof(slabs))((uint8_t *)slabs + i * slab_size)); + } } void *mempool_get(struct mempool *mempool) From c0ffa36cfcadd7431a2778a4b047f545abc69f56 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 15:32:47 +0100 Subject: [PATCH 249/449] Disable pool prealloc for now --- clibs/lib/selva/io/dump.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 554834bc63..6b80605a57 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -752,13 +752,16 @@ static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeE return 0; } +#if 0 /* * Prealloc slabs before loading. * TODO Partials. * This is not always optimal with partials because we may already have * enough free objects. Perhaps only do this on startup. + * TODO This is also problematic for very large allocs. */ mempool_prealloc(&te->nodepool, nr_nodes); +#endif for (sdb_nr_nodes_t i = 0; i < nr_nodes; i++) { node_id_t node_id; From 9ea8a0c7d2c5fcc7e64e85dbcdfec36a1697406c Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Fri, 13 Feb 2026 15:40:29 +0100 Subject: [PATCH 250/449] add includes (step 1) --- native/query/filter/compare.zig | 88 +++ native/query/filter/filter.zig | 1 + native/query/filter/instruction.zig | 1 + native/types.zig | 6 + src/db-client/query/filter/condition.ts | 3 +- src/db-client/query/filter/types.ts | 8 +- src/db-query/ast/filter/condition.ts | 43 +- src/zigTsExports.ts | 736 ++++++++++++++---------- test/query-ast/include.ts | 65 ++- 9 files changed, 605 insertions(+), 346 deletions(-) diff --git a/native/query/filter/compare.zig b/native/query/filter/compare.zig index e2f9508622..0a665bb05e 100644 --- a/native/query/filter/compare.zig +++ b/native/query/filter/compare.zig @@ -15,6 +15,7 @@ pub const Function = enum(u8) { range, eqBatch, eqBatchSmall, + inc, }; pub fn eqBatch(T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) bool { @@ -85,3 +86,90 @@ pub fn range(T: type, q: []u8, v: []const u8, i: usize, c: *t.FilterCondition) b return (utils.readPtr(T, v, c.start).* -% utils.readPtr(T, q, i + @alignOf(T) - c.offset).*) <= utils.readPtr(T, q, i + (size * 2) - c.offset).*; } + +// put this in variableSize +// this with batching => [a,b,c] quite nice +const vectorLenU8 = std.simd.suggestVectorLength(u8).?; +const indexes = std.simd.iota(u8, vectorLenU8); +const nulls: @Vector(vectorLenU8, u8) = @splat(@as(u8, 255)); + +pub fn include(q: []u8, v: []const u8, qI: usize, c: *t.FilterCondition) bool { + const query: []u8 = q[qI .. c.size + qI]; + var value: []const u8 = undefined; + + // Make the has seperate we also need to use LIKE + // FIX COMPRESS + if (v[0] == 1) { + // compressed + value = v[0..3]; + } else { + value = v[2 .. v.len - 4]; + } + + var i: usize = 0; + const l = value.len; + const ql = query.len; + if (l < vectorLenU8) { + while (i < l) : (i += 1) { + if (value[i] == query[0]) { + if (i + ql - 1 > l) { + return false; + } + var j: usize = 1; + while (j < ql) : (j += 1) { + if (value[i + j] != query[j]) { + break; + } + } + if (j == ql) { + return true; + } + } + } + return false; + } + + const queryVector: @Vector(vectorLenU8, u8) = @splat(query[0]); + + while (i <= (l - vectorLenU8)) : (i += vectorLenU8) { + const h: @Vector(vectorLenU8, u8) = value[i..][0..vectorLenU8].*; + const matches = h == queryVector; + if (@reduce(.Or, matches)) { + if (l > 1) { + const result = @select(u8, matches, indexes, nulls); + const index = @reduce(.Min, result) + i; + if (index + ql - 1 > l) { + return false; + } + var j: usize = 1; + while (j < ql) : (j += 1) { + if (value[index + j] != query[j]) { + break; + } + } + if (j == ql) { + return true; + } + } + } + } + while (i < l and ql <= l - i) : (i += 1) { + const id2 = value[i]; + if (id2 == query[0]) { + if (i + ql - 1 > l) { + return false; + } + var j: usize = 1; + while (j < ql) : (j += 1) { + if (value[i + j] != query[j]) { + break; + } + } + if (j == ql) { + return true; + } + return true; + } + } + return false; +} diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index d34e8755d9..e5da606844 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -101,6 +101,7 @@ inline fn compare( .range => Compare.range(T, q, v, index, c), .eqBatch => Compare.eqBatch(T, q, v, index, c), .eqBatchSmall => Compare.eqBatchSmall(T, q, v, index, c), + .inc => Compare.include(q, v, index, c), }; return if (meta.invert) !res else res; } diff --git a/native/query/filter/instruction.zig b/native/query/filter/instruction.zig index fc3766e3f0..8bdfe8925c 100644 --- a/native/query/filter/instruction.zig +++ b/native/query/filter/instruction.zig @@ -18,6 +18,7 @@ fn getFunc(comptime tag: t.FilterOpCompare) Compare.Function { .lt => Compare.Function.le, .ge => Compare.Function.ge, .gt => Compare.Function.gt, + .inc, .ninc => Compare.Function.inc, else => Compare.Function.eq, }; } diff --git a/native/types.zig b/native/types.zig index 77d84d138c..4ddf6f8896 100644 --- a/native/types.zig +++ b/native/types.zig @@ -878,6 +878,12 @@ pub const FilterOpCompare = enum(u8) { lt = 15, ge = 20, le = 21, + // ----------- + inc = 22, + ninc = 23, + incBatch = 24, + nincBatch = 25, + // ---------- selectLargeRefs = 203, selectRef = 204, diff --git a/src/db-client/query/filter/condition.ts b/src/db-client/query/filter/condition.ts index 13ac1cb831..02d75340a9 100644 --- a/src/db-client/query/filter/condition.ts +++ b/src/db-client/query/filter/condition.ts @@ -45,7 +45,8 @@ const getFilterOp = ( op: FilterOp write: ReturnType } => { - if ( + if (operator === 'includes' || operator === '!includes') { + } else if ( operator === '=' || operator === '<' || operator === '>' || diff --git a/src/db-client/query/filter/types.ts b/src/db-client/query/filter/types.ts index e579f203f2..0d946669d4 100644 --- a/src/db-client/query/filter/types.ts +++ b/src/db-client/query/filter/types.ts @@ -17,9 +17,9 @@ export type Operator = | '<=' | '..' | '!..' - | 'exists' - | '!exists' - | 'like' - | '!like' + // | 'exists' + // | '!exists' + // | 'like' + // | '!like' | 'includes' | '!includes' diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts index d2f5266d2d..cd6d68f4d2 100644 --- a/src/db-query/ast/filter/condition.ts +++ b/src/db-query/ast/filter/condition.ts @@ -1,3 +1,4 @@ +import native from '../../../native.js' import { PropDef } from '../../../schema/defs/index.js' import { FilterConditionByteSize, @@ -7,6 +8,7 @@ import { writeFilterCondition, ModifyEnum, LangCodeEnum, + PropType, } from '../../../zigTsExports.js' import { FilterOpts, Operator } from '../ast.js' @@ -47,12 +49,16 @@ const opMap: Partial> = { '<': 'lt', '>=': 'ge', '<=': 'le', + includes: 'inc', + '!includes': 'ninc', } +// redo this whole thing + const getFilterOp = ( prop: PropDef, operator: Operator, - size: number, + value: any[], ): { size: number op: FilterOp @@ -64,19 +70,38 @@ const getFilterOp = ( throw new Error(`un supported op ${operator}`) } - const write = (buf: Uint8Array, val: any, offset: number) => { + let write = (buf: Uint8Array, val: any, offset: number) => { + console.log('write', val) prop.write(buf, val, offset) } - if ((opName === 'eq' || opName === 'neq') && size > 1) { - const vectorLen = 16 / prop.size - if (size > vectorLen) { + let size = prop.size + if (size === 0) { + if (prop.type === PropType.string) { + if (value.length === 1) { + size = native.stringByteLength(value[0]) + + console.log('SIZE', size) + + write = (buf: Uint8Array, val: any, offset: number) => { + const x = new TextEncoder() + x.encodeInto(val, buf.subarray(offset)) + } + // opName has to be eqCrc32 if EQ + } + } + } + + if ((opName === 'eq' || opName === 'neq') && value.length > 1) { + // incBatch, incBatchSmall + const vectorLen = 16 / size + if (value.length > vectorLen) { return { op: { compare: FilterOpCompare[`${opName}Batch`], prop: prop.type, }, - size: prop.size, + size, write, } } else { @@ -95,7 +120,7 @@ const getFilterOp = ( compare: FilterOpCompare.range, prop: prop.type, }, - size: prop.size * 2, + size: size * 2, write: (condition: Uint8Array, v: any, offset: number) => { // x >= 3 && x <= 11 // (x -% 3) <= (11 - 3) @@ -110,7 +135,7 @@ const getFilterOp = ( compare: FilterOpCompare[opName], prop: prop.type, }, - size: prop.size, + size, write, } } @@ -126,7 +151,7 @@ export const createCondition = ( value = [value] } - const { op, size, write } = getFilterOp(prop, operator, value.length) + const { op, size, write } = getFilterOp(prop, operator, value) // this is fixed make fixed and variable in a file diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 85cda99eb1..39f2bb705d 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1,12 +1,20 @@ -import { - writeUint16, writeInt16, - writeUint32, writeInt32, - writeUint64, writeInt64, - writeFloatLE, writeDoubleLE, - readUint16, readInt16, - readUint32, readInt32, - readUint64, readInt64, - readFloatLE, readDoubleLE +import { + writeUint16, + writeInt16, + writeUint32, + writeInt32, + writeUint64, + writeInt64, + writeFloatLE, + writeDoubleLE, + readUint16, + readInt16, + readUint32, + readInt32, + readUint64, + readInt64, + readFloatLE, + readDoubleLE, } from './utils/index.js' import { AutoSizedUint8Array } from './utils/AutoSizedUint8Array.js' @@ -36,7 +44,8 @@ export const BridgeResponseInverse = { flushQuery, flushModify */ -export type BridgeResponseEnum = (typeof BridgeResponse)[keyof typeof BridgeResponse] +export type BridgeResponseEnum = + (typeof BridgeResponse)[keyof typeof BridgeResponse] export const OpType = { id: 0, @@ -266,7 +275,7 @@ export const readModifyHeader = ( ): ModifyHeader => { const value: ModifyHeader = { opId: readUint32(buf, offset), - opType: (buf[offset + 4]) as OpTypeEnum, + opType: buf[offset + 4] as OpTypeEnum, schema: readUint64(buf, offset + 5), count: readUint32(buf, offset + 13), } @@ -274,10 +283,10 @@ export const readModifyHeader = ( } export const readModifyHeaderProps = { - opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - opType: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as OpTypeEnum, - schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), - count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), + opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + opType: (buf: Uint8Array, offset: number) => buf[offset + 4] as OpTypeEnum, + schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), + count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), } export const createModifyHeader = (header: ModifyHeader): Uint8Array => { @@ -353,9 +362,9 @@ export const readModifyUpdateHeader = ( offset: number, ): ModifyUpdateHeader => { const value: ModifyUpdateHeader = { - op: (buf[offset]) as ModifyEnum, - type: (readUint16(buf, offset + 1)) as TypeId, - isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, + op: buf[offset] as ModifyEnum, + type: readUint16(buf, offset + 1) as TypeId, + isTmp: ((buf[offset + 3] >>> 0) & 1) === 1, id: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -363,14 +372,18 @@ export const readModifyUpdateHeader = ( } export const readModifyUpdateHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => + ((buf[offset + 3] >>> 0) & 1) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { +export const createModifyUpdateHeader = ( + header: ModifyUpdateHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyUpdateHeaderByteSize) writeModifyUpdateHeader(buffer, header, 0) return buffer @@ -440,22 +453,26 @@ export const readModifyDeleteHeader = ( offset: number, ): ModifyDeleteHeader => { const value: ModifyDeleteHeader = { - op: (buf[offset]) as ModifyEnum, - type: (readUint16(buf, offset + 1)) as TypeId, - isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, + op: buf[offset] as ModifyEnum, + type: readUint16(buf, offset + 1) as TypeId, + isTmp: ((buf[offset + 3] >>> 0) & 1) === 1, id: readUint32(buf, offset + 4), } return value } export const readModifyDeleteHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => + ((buf[offset + 3] >>> 0) & 1) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), } -export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { +export const createModifyDeleteHeader = ( + header: ModifyDeleteHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyDeleteHeaderByteSize) writeModifyDeleteHeader(buffer, header, 0) return buffer @@ -516,20 +533,23 @@ export const readModifyCreateHeader = ( offset: number, ): ModifyCreateHeader => { const value: ModifyCreateHeader = { - op: (buf[offset]) as ModifyEnum, - type: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as ModifyEnum, + type: readUint16(buf, offset + 1) as TypeId, size: readUint32(buf, offset + 3), } return value } export const readModifyCreateHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), } -export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { +export const createModifyCreateHeader = ( + header: ModifyCreateHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyCreateHeaderByteSize) writeModifyCreateHeader(buffer, header, 0) return buffer @@ -593,8 +613,8 @@ export const readModifyCreateRingHeader = ( offset: number, ): ModifyCreateRingHeader => { const value: ModifyCreateRingHeader = { - op: (buf[offset]) as ModifyEnum, - type: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as ModifyEnum, + type: readUint16(buf, offset + 1) as TypeId, maxNodeId: readUint32(buf, offset + 3), size: readUint32(buf, offset + 7), } @@ -602,13 +622,16 @@ export const readModifyCreateRingHeader = ( } export const readModifyCreateRingHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, - type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, + type: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } -export const createModifyCreateRingHeader = (header: ModifyCreateRingHeader): Uint8Array => { +export const createModifyCreateRingHeader = ( + header: ModifyCreateRingHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyCreateRingHeaderByteSize) writeModifyCreateRingHeader(buffer, header, 0) return buffer @@ -643,7 +666,8 @@ export const ModifyIncrementInverse = { increment, decrement */ -export type ModifyIncrementEnum = (typeof ModifyIncrement)[keyof typeof ModifyIncrement] +export type ModifyIncrementEnum = + (typeof ModifyIncrement)[keyof typeof ModifyIncrement] export type ModifyMainHeader = { id: number @@ -699,8 +723,8 @@ export const readModifyMainHeader = ( ): ModifyMainHeader => { const value: ModifyMainHeader = { id: buf[offset], - type: (buf[offset + 1]) as PropTypeEnum, - increment: (buf[offset + 2]) as ModifyIncrementEnum, + type: buf[offset + 1] as PropTypeEnum, + increment: buf[offset + 2] as ModifyIncrementEnum, size: buf[offset + 3], start: readUint16(buf, offset + 4), } @@ -708,14 +732,17 @@ export const readModifyMainHeader = ( } export const readModifyMainHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - increment: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as ModifyIncrementEnum, - size: (buf: Uint8Array, offset: number) => buf[offset + 3], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, + increment: (buf: Uint8Array, offset: number) => + buf[offset + 2] as ModifyIncrementEnum, + size: (buf: Uint8Array, offset: number) => buf[offset + 3], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), } -export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { +export const createModifyMainHeader = ( + header: ModifyMainHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyMainHeaderByteSize) writeModifyMainHeader(buffer, header, 0) return buffer @@ -776,19 +803,21 @@ export const readModifyPropHeader = ( ): ModifyPropHeader => { const value: ModifyPropHeader = { id: buf[offset], - type: (buf[offset + 1]) as PropTypeEnum, + type: buf[offset + 1] as PropTypeEnum, size: readUint32(buf, offset + 2), } return value } export const readModifyPropHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyPropHeader = (header: ModifyPropHeader): Uint8Array => { +export const createModifyPropHeader = ( + header: ModifyPropHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyPropHeaderByteSize) writeModifyPropHeader(buffer, header, 0) return buffer @@ -831,7 +860,8 @@ export const ModifyReferencesInverse = { delIds, delTmpIds */ -export type ModifyReferencesEnum = (typeof ModifyReferences)[keyof typeof ModifyReferences] +export type ModifyReferencesEnum = + (typeof ModifyReferences)[keyof typeof ModifyReferences] export type ModifyReferencesHeader = { op: ModifyReferencesEnum @@ -868,18 +898,20 @@ export const readModifyReferencesHeader = ( offset: number, ): ModifyReferencesHeader => { const value: ModifyReferencesHeader = { - op: (buf[offset]) as ModifyReferencesEnum, + op: buf[offset] as ModifyReferencesEnum, size: readUint32(buf, offset + 1), } return value } export const readModifyReferencesHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyReferencesEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyReferencesEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } -export const createModifyReferencesHeader = (header: ModifyReferencesHeader): Uint8Array => { +export const createModifyReferencesHeader = ( + header: ModifyReferencesHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesHeaderByteSize) writeModifyReferencesHeader(buffer, header, 0) return buffer @@ -950,8 +982,8 @@ export const readModifyReferencesMetaHeader = ( ): ModifyReferencesMetaHeader => { const value: ModifyReferencesMetaHeader = { id: readUint32(buf, offset), - isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, - withIndex: (((buf[offset + 4] >>> 1) & 1)) === 1, + isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, + withIndex: ((buf[offset + 4] >>> 1) & 1) === 1, index: readUint32(buf, offset + 5), size: readUint32(buf, offset + 9), } @@ -959,14 +991,18 @@ export const readModifyReferencesMetaHeader = ( } export const readModifyReferencesMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, - index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 0) & 1) === 1, + withIndex: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 1) & 1) === 1, + index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), } -export const createModifyReferencesMetaHeader = (header: ModifyReferencesMetaHeader): Uint8Array => { +export const createModifyReferencesMetaHeader = ( + header: ModifyReferencesMetaHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesMetaHeaderByteSize) writeModifyReferencesMetaHeader(buffer, header, 0) return buffer @@ -1031,19 +1067,22 @@ export const readModifyReferenceMetaHeader = ( ): ModifyReferenceMetaHeader => { const value: ModifyReferenceMetaHeader = { id: readUint32(buf, offset), - isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, size: readUint32(buf, offset + 5), } return value } export const readModifyReferenceMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 0) & 1) === 1, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), } -export const createModifyReferenceMetaHeader = (header: ModifyReferenceMetaHeader): Uint8Array => { +export const createModifyReferenceMetaHeader = ( + header: ModifyReferenceMetaHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyReferenceMetaHeaderByteSize) writeModifyReferenceMetaHeader(buffer, header, 0) return buffer @@ -1071,14 +1110,18 @@ export const ModifyCardinalityHeaderByteSize = 2 export const ModifyCardinalityHeaderAlignOf = 2 -export const packModifyCardinalityHeader = (obj: ModifyCardinalityHeader): number => { +export const packModifyCardinalityHeader = ( + obj: ModifyCardinalityHeader, +): number => { let val = 0 val |= ((obj.sparse ? 1 : 0) & 1) << 0 val |= (Number(obj.precision) & 255) << 8 return val } -export const unpackModifyCardinalityHeader = (val: number): ModifyCardinalityHeader => { +export const unpackModifyCardinalityHeader = ( + val: number, +): ModifyCardinalityHeader => { return { sparse: ((val >>> 0) & 1) === 1, precision: Number((val >>> 8) & 255), @@ -1113,18 +1156,20 @@ export const readModifyCardinalityHeader = ( offset: number, ): ModifyCardinalityHeader => { const value: ModifyCardinalityHeader = { - sparse: (((buf[offset] >>> 0) & 1)) === 1, + sparse: ((buf[offset] >>> 0) & 1) === 1, precision: buf[offset + 1], } return value } export const readModifyCardinalityHeaderProps = { - sparse: (buf: Uint8Array, offset: number) => (((buf[offset] >>> 0) & 1)) === 1, - precision: (buf: Uint8Array, offset: number) => buf[offset + 1], + sparse: (buf: Uint8Array, offset: number) => ((buf[offset] >>> 0) & 1) === 1, + precision: (buf: Uint8Array, offset: number) => buf[offset + 1], } -export const createModifyCardinalityHeader = (header: ModifyCardinalityHeader): Uint8Array => { +export const createModifyCardinalityHeader = ( + header: ModifyCardinalityHeader, +): Uint8Array => { const buffer = new Uint8Array(ModifyCardinalityHeaderByteSize) writeModifyCardinalityHeader(buffer, header, 0) return buffer @@ -1178,17 +1223,19 @@ export const readModifyResultItem = ( ): ModifyResultItem => { const value: ModifyResultItem = { id: readUint32(buf, offset), - err: (buf[offset + 4]) as ModifyErrorEnum, + err: buf[offset + 4] as ModifyErrorEnum, } return value } export const readModifyResultItemProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - err: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyErrorEnum, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + err: (buf: Uint8Array, offset: number) => buf[offset + 4] as ModifyErrorEnum, } -export const createModifyResultItem = (header: ModifyResultItem): Uint8Array => { +export const createModifyResultItem = ( + header: ModifyResultItem, +): Uint8Array => { const buffer = new Uint8Array(ModifyResultItemByteSize) writeModifyResultItem(buffer, header, 0) return buffer @@ -1352,7 +1399,8 @@ export const PropTypeSelvaInverse = { aliases, colVec */ -export type PropTypeSelvaEnum = (typeof PropTypeSelva)[keyof typeof PropTypeSelva] +export type PropTypeSelvaEnum = + (typeof PropTypeSelva)[keyof typeof PropTypeSelva] export const RefOp = { clear: 0, @@ -1427,7 +1475,8 @@ export const ReferencesSelectInverse = { any, all */ -export type ReferencesSelectEnum = (typeof ReferencesSelect)[keyof typeof ReferencesSelect] +export type ReferencesSelectEnum = + (typeof ReferencesSelect)[keyof typeof ReferencesSelect] export const RefEdgeOp = { noEdgeNoIndexRealId: 0, @@ -2160,30 +2209,28 @@ export const writeSortHeaderProps = { }, } -export const readSortHeader = ( - buf: Uint8Array, - offset: number, -): SortHeader => { +export const readSortHeader = (buf: Uint8Array, offset: number): SortHeader => { const value: SortHeader = { - order: (buf[offset]) as OrderEnum, + order: buf[offset] as OrderEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, start: readUint16(buf, offset + 3), len: readUint16(buf, offset + 5), - lang: (buf[offset + 7]) as LangCodeEnum, + lang: buf[offset + 7] as LangCodeEnum, edgeType: readUint16(buf, offset + 8), } return value } export const readSortHeaderProps = { - order: (buf: Uint8Array, offset: number) => (buf[offset]) as OrderEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - lang: (buf: Uint8Array, offset: number) => (buf[offset + 7]) as LangCodeEnum, - edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + order: (buf: Uint8Array, offset: number) => buf[offset] as OrderEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + lang: (buf: Uint8Array, offset: number) => buf[offset + 7] as LangCodeEnum, + edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } export const createSortHeader = (header: SortHeader): Uint8Array => { @@ -2317,7 +2364,8 @@ export const QueryIteratorTypeInverse = { groupBy, groupByFilter */ -export type QueryIteratorTypeEnum = (typeof QueryIteratorType)[keyof typeof QueryIteratorType] +export type QueryIteratorTypeEnum = + (typeof QueryIteratorType)[keyof typeof QueryIteratorType] export const QueryType = { id: 0, @@ -2462,17 +2510,18 @@ export const readIncludeHeader = ( offset: number, ): IncludeHeader => { const value: IncludeHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, } return value } export const readIncludeHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, } export const createIncludeHeader = (header: IncludeHeader): Uint8Array => { @@ -2549,20 +2598,23 @@ export const readIncludeMetaHeader = ( offset: number, ): IncludeMetaHeader => { const value: IncludeMetaHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, } return value } export const readIncludeMetaHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, } -export const createIncludeMetaHeader = (header: IncludeMetaHeader): Uint8Array => { +export const createIncludeMetaHeader = ( + header: IncludeMetaHeader, +): Uint8Array => { const buffer = new Uint8Array(IncludeMetaHeaderByteSize) writeIncludeMetaHeader(buffer, header, 0) return buffer @@ -2626,22 +2678,25 @@ export const readIncludePartialHeader = ( offset: number, ): IncludePartialHeader => { const value: IncludePartialHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, prop: buf[offset + 1], - propType: (buf[offset + 2]) as PropTypeEnum, + propType: buf[offset + 2] as PropTypeEnum, amount: readUint16(buf, offset + 3), } return value } export const readIncludePartialHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, - amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 2] as PropTypeEnum, + amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createIncludePartialHeader = (header: IncludePartialHeader): Uint8Array => { +export const createIncludePartialHeader = ( + header: IncludePartialHeader, +): Uint8Array => { const buffer = new Uint8Array(IncludePartialHeaderByteSize) writeIncludePartialHeader(buffer, header, 0) return buffer @@ -2715,11 +2770,13 @@ export const readIncludePartialProp = ( } export const readIncludePartialPropProps = { - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), } -export const createIncludePartialProp = (header: IncludePartialProp): Uint8Array => { +export const createIncludePartialProp = ( + header: IncludePartialProp, +): Uint8Array => { const buffer = new Uint8Array(IncludePartialPropByteSize) writeIncludePartialProp(buffer, header, 0) return buffer @@ -2790,20 +2847,22 @@ export const readIncludeOpts = ( ): IncludeOpts => { const value: IncludeOpts = { end: readUint32(buf, offset), - isChars: (((buf[offset + 4] >>> 0) & 1)) === 1, - hasOpts: (((buf[offset + 4] >>> 1) & 1)) === 1, + isChars: ((buf[offset + 4] >>> 0) & 1) === 1, + hasOpts: ((buf[offset + 4] >>> 1) & 1) === 1, langFallbackSize: buf[offset + 5], - lang: (buf[offset + 6]) as LangCodeEnum, + lang: buf[offset + 6] as LangCodeEnum, } return value } export const readIncludeOptsProps = { - end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isChars: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, - hasOpts: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, - langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], - lang: (buf: Uint8Array, offset: number) => (buf[offset + 6]) as LangCodeEnum, + end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isChars: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 0) & 1) === 1, + hasOpts: (buf: Uint8Array, offset: number) => + ((buf[offset + 4] >>> 1) & 1) === 1, + langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], + lang: (buf: Uint8Array, offset: number) => buf[offset + 6] as LangCodeEnum, } export const createIncludeOpts = (header: IncludeOpts): Uint8Array => { @@ -2869,8 +2928,8 @@ export const readIncludeResponse = ( } export const readIncludeResponseProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + prop: (buf: Uint8Array, offset: number) => buf[offset], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } export const createIncludeResponse = (header: IncludeResponse): Uint8Array => { @@ -2950,10 +3009,10 @@ export const readIncludeResponseMeta = ( offset: number, ): IncludeResponseMeta => { const value: IncludeResponseMeta = { - op: (buf[offset]) as ReadOpEnum, + op: buf[offset] as ReadOpEnum, prop: buf[offset + 1], - lang: (buf[offset + 2]) as LangCodeEnum, - compressed: (((buf[offset + 3] >>> 0) & 1)) === 1, + lang: buf[offset + 2] as LangCodeEnum, + compressed: ((buf[offset + 3] >>> 0) & 1) === 1, crc32: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -2961,15 +3020,18 @@ export const readIncludeResponseMeta = ( } export const readIncludeResponseMetaProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as ReadOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - lang: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as LangCodeEnum, - compressed: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, - crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => buf[offset] as ReadOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + lang: (buf: Uint8Array, offset: number) => buf[offset + 2] as LangCodeEnum, + compressed: (buf: Uint8Array, offset: number) => + ((buf[offset + 3] >>> 0) & 1) === 1, + crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createIncludeResponseMeta = (header: IncludeResponseMeta): Uint8Array => { +export const createIncludeResponseMeta = ( + header: IncludeResponseMeta, +): Uint8Array => { const buffer = new Uint8Array(IncludeResponseMetaByteSize) writeIncludeResponseMeta(buffer, header, 0) return buffer @@ -3038,8 +3100,8 @@ export const readSubscriptionHeader = ( offset: number, ): SubscriptionHeader => { const value: SubscriptionHeader = { - op: (buf[offset]) as OpTypeEnum, - typeId: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as OpTypeEnum, + typeId: readUint16(buf, offset + 1) as TypeId, fieldsLen: buf[offset + 3], partialLen: buf[offset + 4], } @@ -3047,13 +3109,16 @@ export const readSubscriptionHeader = ( } export const readSubscriptionHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as OpTypeEnum, - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], - partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], + op: (buf: Uint8Array, offset: number) => buf[offset] as OpTypeEnum, + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], + partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], } -export const createSubscriptionHeader = (header: SubscriptionHeader): Uint8Array => { +export const createSubscriptionHeader = ( + header: SubscriptionHeader, +): Uint8Array => { const buffer = new Uint8Array(SubscriptionHeaderByteSize) writeSubscriptionHeader(buffer, header, 0) return buffer @@ -3164,7 +3229,11 @@ export const writeQueryHeaderProps = { includeSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 22) }, - iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { + iteratorType: ( + buf: Uint8Array, + value: QueryIteratorTypeEnum, + offset: number, + ) => { buf[offset + 24] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3180,10 +3249,10 @@ export const readQueryHeader = ( offset: number, ): QueryHeader => { const value: QueryHeader = { - op: (buf[offset]) as QueryTypeEnum, + op: buf[offset] as QueryTypeEnum, prop: buf[offset + 1], - typeId: (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, + typeId: readUint16(buf, offset + 2) as TypeId, + edgeTypeId: readUint16(buf, offset + 4) as TypeId, offset: readUint32(buf, offset + 6), limit: readUint32(buf, offset + 10), filterSize: readUint16(buf, offset + 14), @@ -3191,28 +3260,34 @@ export const readQueryHeader = ( edgeSize: readUint16(buf, offset + 18), edgeFilterSize: readUint16(buf, offset + 20), includeSize: readUint16(buf, offset + 22), - iteratorType: (buf[offset + 24]) as QueryIteratorTypeEnum, + iteratorType: buf[offset + 24] as QueryIteratorTypeEnum, size: readUint16(buf, offset + 25), - sort: (((buf[offset + 27] >>> 0) & 1)) === 1, + sort: ((buf[offset + 27] >>> 0) & 1) === 1, } return value } export const readQueryHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), - edgeFilterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 20), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 22), - iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 24]) as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), - sort: (buf: Uint8Array, offset: number) => (((buf[offset + 27] >>> 0) & 1)) === 1, + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 4) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), + edgeFilterSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 20), + includeSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 22), + iteratorType: (buf: Uint8Array, offset: number) => + buf[offset + 24] as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), + sort: (buf: Uint8Array, offset: number) => + ((buf[offset + 27] >>> 0) & 1) === 1, } export const createQueryHeader = (header: QueryHeader): Uint8Array => { @@ -3310,8 +3385,8 @@ export const readQueryHeaderSingle = ( offset: number, ): QueryHeaderSingle => { const value: QueryHeaderSingle = { - op: (buf[offset]) as QueryTypeEnum, - typeId: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as QueryTypeEnum, + typeId: readUint16(buf, offset + 1) as TypeId, prop: buf[offset + 3], id: readUint32(buf, offset + 4), filterSize: readUint16(buf, offset + 8), @@ -3322,16 +3397,20 @@ export const readQueryHeaderSingle = ( } export const readQueryHeaderSingleProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - prop: (buf: Uint8Array, offset: number) => buf[offset + 3], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), - aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), -} - -export const createQueryHeaderSingle = (header: QueryHeaderSingle): Uint8Array => { + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + prop: (buf: Uint8Array, offset: number) => buf[offset + 3], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + includeSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 10), + aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), +} + +export const createQueryHeaderSingle = ( + header: QueryHeaderSingle, +): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleByteSize) writeQueryHeaderSingle(buffer, header, 0) return buffer @@ -3411,10 +3490,10 @@ export const readQueryHeaderSingleReference = ( offset: number, ): QueryHeaderSingleReference => { const value: QueryHeaderSingleReference = { - op: (buf[offset]) as QueryTypeEnum, + op: buf[offset] as QueryTypeEnum, prop: buf[offset + 1], - typeId: (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, + typeId: readUint16(buf, offset + 2) as TypeId, + edgeTypeId: readUint16(buf, offset + 4) as TypeId, edgeSize: readUint16(buf, offset + 6), includeSize: readUint16(buf, offset + 8), } @@ -3422,15 +3501,19 @@ export const readQueryHeaderSingleReference = ( } export const readQueryHeaderSingleReferenceProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 4) as TypeId, + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } -export const createQueryHeaderSingleReference = (header: QueryHeaderSingleReference): Uint8Array => { +export const createQueryHeaderSingleReference = ( + header: QueryHeaderSingleReference, +): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleReferenceByteSize) writeQueryHeaderSingleReference(buffer, header, 0) return buffer @@ -3482,7 +3565,8 @@ export const VectorBaseTypeInverse = { float32, float64 */ -export type VectorBaseTypeEnum = (typeof VectorBaseType)[keyof typeof VectorBaseType] +export type VectorBaseTypeEnum = + (typeof VectorBaseType)[keyof typeof VectorBaseType] export type AggHeader = { op: QueryTypeEnum @@ -3546,7 +3630,11 @@ export const writeAggHeaderProps = { filterSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 11) }, - iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { + iteratorType: ( + buf: Uint8Array, + value: QueryIteratorTypeEnum, + offset: number, + ) => { buf[offset + 13] = Number(value) }, resultsSize: (buf: Uint8Array, value: number, offset: number) => { @@ -3563,36 +3651,39 @@ export const writeAggHeaderProps = { }, } -export const readAggHeader = ( - buf: Uint8Array, - offset: number, -): AggHeader => { +export const readAggHeader = (buf: Uint8Array, offset: number): AggHeader => { const value: AggHeader = { - op: (buf[offset]) as QueryTypeEnum, - typeId: (readUint16(buf, offset + 1)) as TypeId, + op: buf[offset] as QueryTypeEnum, + typeId: readUint16(buf, offset + 1) as TypeId, offset: readUint32(buf, offset + 3), limit: readUint32(buf, offset + 7), filterSize: readUint16(buf, offset + 11), - iteratorType: (buf[offset + 13]) as QueryIteratorTypeEnum, + iteratorType: buf[offset + 13] as QueryIteratorTypeEnum, resultsSize: readUint16(buf, offset + 14), accumulatorSize: readUint16(buf, offset + 16), - hasGroupBy: (((buf[offset + 18] >>> 0) & 1)) === 1, - isSamplingSet: (((buf[offset + 18] >>> 1) & 1)) === 1, + hasGroupBy: ((buf[offset + 18] >>> 0) & 1) === 1, + isSamplingSet: ((buf[offset + 18] >>> 1) & 1) === 1, } return value } export const readAggHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), - iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 13]) as QueryIteratorTypeEnum, - resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 18] >>> 0) & 1)) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 18] >>> 1) & 1)) === 1, + op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 1) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), + iteratorType: (buf: Uint8Array, offset: number) => + buf[offset + 13] as QueryIteratorTypeEnum, + resultsSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 14), + accumulatorSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 16), + hasGroupBy: (buf: Uint8Array, offset: number) => + ((buf[offset + 18] >>> 0) & 1) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => + ((buf[offset + 18] >>> 1) & 1) === 1, } export const createAggHeader = (header: AggHeader): Uint8Array => { @@ -3693,27 +3784,30 @@ export const readAggRefsHeader = ( offset: number, ): AggRefsHeader => { const value: AggRefsHeader = { - op: (buf[offset]) as IncludeOpEnum, + op: buf[offset] as IncludeOpEnum, targetProp: buf[offset + 1], offset: readUint32(buf, offset + 2), filterSize: readUint16(buf, offset + 6), resultsSize: readUint16(buf, offset + 8), accumulatorSize: readUint16(buf, offset + 10), - hasGroupBy: (((buf[offset + 12] >>> 0) & 1)) === 1, - isSamplingSet: (((buf[offset + 12] >>> 1) & 1)) === 1, + hasGroupBy: ((buf[offset + 12] >>> 0) & 1) === 1, + isSamplingSet: ((buf[offset + 12] >>> 1) & 1) === 1, } return value } export const readAggRefsHeaderProps = { - op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, - targetProp: (buf: Uint8Array, offset: number) => buf[offset + 1], - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), - hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 12] >>> 0) & 1)) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 12] >>> 1) & 1)) === 1, + op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, + targetProp: (buf: Uint8Array, offset: number) => buf[offset + 1], + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + accumulatorSize: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 10), + hasGroupBy: (buf: Uint8Array, offset: number) => + ((buf[offset + 12] >>> 0) & 1) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => + ((buf[offset + 12] >>> 1) & 1) === 1, } export const createAggRefsHeader = (header: AggRefsHeader): Uint8Array => { @@ -3748,13 +3842,17 @@ export const addMultiSubscriptionHeaderByteSize = 2 export const addMultiSubscriptionHeaderAlignOf = 2 -export const packaddMultiSubscriptionHeader = (obj: addMultiSubscriptionHeader): number => { +export const packaddMultiSubscriptionHeader = ( + obj: addMultiSubscriptionHeader, +): number => { let val = 0 val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackaddMultiSubscriptionHeader = (val: number): addMultiSubscriptionHeader => { +export const unpackaddMultiSubscriptionHeader = ( + val: number, +): addMultiSubscriptionHeader => { return { typeId: Number((val >>> 0) & 65535), } @@ -3787,10 +3885,12 @@ export const readaddMultiSubscriptionHeader = ( } export const readaddMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createaddMultiSubscriptionHeader = (header: addMultiSubscriptionHeader): Uint8Array => { +export const createaddMultiSubscriptionHeader = ( + header: addMultiSubscriptionHeader, +): Uint8Array => { const buffer = new Uint8Array(addMultiSubscriptionHeaderByteSize) writeaddMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -3813,13 +3913,17 @@ export const removeMultiSubscriptionHeaderByteSize = 2 export const removeMultiSubscriptionHeaderAlignOf = 2 -export const packremoveMultiSubscriptionHeader = (obj: removeMultiSubscriptionHeader): number => { +export const packremoveMultiSubscriptionHeader = ( + obj: removeMultiSubscriptionHeader, +): number => { let val = 0 val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackremoveMultiSubscriptionHeader = (val: number): removeMultiSubscriptionHeader => { +export const unpackremoveMultiSubscriptionHeader = ( + val: number, +): removeMultiSubscriptionHeader => { return { typeId: Number((val >>> 0) & 65535), } @@ -3852,10 +3956,12 @@ export const readremoveMultiSubscriptionHeader = ( } export const readremoveMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createremoveMultiSubscriptionHeader = (header: removeMultiSubscriptionHeader): Uint8Array => { +export const createremoveMultiSubscriptionHeader = ( + header: removeMultiSubscriptionHeader, +): Uint8Array => { const buffer = new Uint8Array(removeMultiSubscriptionHeaderByteSize) writeremoveMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -3924,15 +4030,12 @@ export const writeAggPropProps = { }, } -export const readAggProp = ( - buf: Uint8Array, - offset: number, -): AggProp => { +export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { const value: AggProp = { propId: buf[offset], - propType: (buf[offset + 1]) as PropTypeEnum, + propType: buf[offset + 1] as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), - aggFunction: (buf[offset + 4]) as AggFunctionEnum, + aggFunction: buf[offset + 4] as AggFunctionEnum, resultPos: readUint16(buf, offset + 5), accumulatorPos: readUint16(buf, offset + 7), } @@ -3940,12 +4043,16 @@ export const readAggProp = ( } export const readAggPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), - aggFunction: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as AggFunctionEnum, - resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - accumulatorPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 1] as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2), + aggFunction: (buf: Uint8Array, offset: number) => + buf[offset + 4] as AggFunctionEnum, + resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + accumulatorPos: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 7), } export const createAggProp = (header: AggProp): Uint8Array => { @@ -4028,7 +4135,7 @@ export const readGroupByKeyProp = ( ): GroupByKeyProp => { const value: GroupByKeyProp = { propId: buf[offset], - propType: (buf[offset + 1]) as PropTypeEnum, + propType: buf[offset + 1] as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), stepType: buf[offset + 4], stepRange: readUint32(buf, offset + 5), @@ -4038,12 +4145,14 @@ export const readGroupByKeyProp = ( } export const readGroupByKeyPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), - stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], - stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => + buf[offset + 1] as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2), + stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], + stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), } export const createGroupByKeyProp = (header: GroupByKeyProp): Uint8Array => { @@ -4079,6 +4188,8 @@ export const FilterOpCompare = { lt: 15, ge: 20, le: 21, + inc: 22, + ninc: 23, selectLargeRefs: 203, selectRef: 204, selectSmallRefs: 205, @@ -4131,7 +4242,8 @@ export const FilterOpCompareInverse = { nextOrIndex, andOp */ -export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] +export type FilterOpCompareEnum = + (typeof FilterOpCompare)[keyof typeof FilterOpCompare] export type FilterOp = { prop: PropTypeEnum @@ -4177,20 +4289,18 @@ export const writeFilterOpProps = { }, } -export const readFilterOp = ( - buf: Uint8Array, - offset: number, -): FilterOp => { +export const readFilterOp = (buf: Uint8Array, offset: number): FilterOp => { const value: FilterOp = { - prop: (buf[offset]) as PropTypeEnum, - compare: (buf[offset + 1]) as FilterOpCompareEnum, + prop: buf[offset] as PropTypeEnum, + compare: buf[offset + 1] as FilterOpCompareEnum, } return value } export const readFilterOpProps = { - prop: (buf: Uint8Array, offset: number) => (buf[offset]) as PropTypeEnum, - compare: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as FilterOpCompareEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset] as PropTypeEnum, + compare: (buf: Uint8Array, offset: number) => + buf[offset + 1] as FilterOpCompareEnum, } export const createFilterOp = (header: FilterOp): Uint8Array => { @@ -4286,13 +4396,15 @@ export const readFilterCondition = ( } export const readFilterConditionProps = { - op: (buf: Uint8Array, offset: number) => unpackFilterOp(readUint16(buf, offset)), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - prop: (buf: Uint8Array, offset: number) => buf[offset + 6], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), - len: (buf: Uint8Array, offset: number) => buf[offset + 9], - fieldSchema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 10), - offset: (buf: Uint8Array, offset: number) => buf[offset + 18], + op: (buf: Uint8Array, offset: number) => + unpackFilterOp(readUint16(buf, offset)), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + prop: (buf: Uint8Array, offset: number) => buf[offset + 6], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + len: (buf: Uint8Array, offset: number) => buf[offset + 9], + fieldSchema: (buf: Uint8Array, offset: number) => + readUint64(buf, offset + 10), + offset: (buf: Uint8Array, offset: number) => buf[offset + 18], } export const createFilterCondition = (header: FilterCondition): Uint8Array => { @@ -4359,15 +4471,16 @@ export const readFilterSelect = ( const value: FilterSelect = { size: readUint32(buf, offset), typeEntry: readUint64(buf, offset + 4), - typeId: (readUint16(buf, offset + 12)) as TypeId, + typeId: readUint16(buf, offset + 12) as TypeId, } return value } export const readFilterSelectProps = { - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), - typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 12)) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), + typeId: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 12) as TypeId, } export const createFilterSelect = (header: FilterSelect): Uint8Array => { @@ -4450,14 +4563,16 @@ export const readSelvaSchemaHeader = ( } export const readSelvaSchemaHeaderProps = { - blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], - nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], - nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], - sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], + blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], + nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], + nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], + sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], } -export const createSelvaSchemaHeader = (header: SelvaSchemaHeader): Uint8Array => { +export const createSelvaSchemaHeader = ( + header: SelvaSchemaHeader, +): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaHeaderByteSize) writeSelvaSchemaHeader(buffer, header, 0) return buffer @@ -4486,7 +4601,9 @@ export const SelvaSchemaMicroBufferByteSize = 4 export const SelvaSchemaMicroBufferAlignOf = 4 -export const packSelvaSchemaMicroBuffer = (obj: SelvaSchemaMicroBuffer): number => { +export const packSelvaSchemaMicroBuffer = ( + obj: SelvaSchemaMicroBuffer, +): number => { let val = 0 val |= (Number(obj.type) & 255) << 0 val |= (Number(obj.len) & 65535) << 8 @@ -4494,7 +4611,9 @@ export const packSelvaSchemaMicroBuffer = (obj: SelvaSchemaMicroBuffer): number return val } -export const unpackSelvaSchemaMicroBuffer = (val: number): SelvaSchemaMicroBuffer => { +export const unpackSelvaSchemaMicroBuffer = ( + val: number, +): SelvaSchemaMicroBuffer => { return { type: Number((val >>> 0) & 255), len: Number((val >>> 8) & 65535), @@ -4541,12 +4660,14 @@ export const readSelvaSchemaMicroBuffer = ( } export const readSelvaSchemaMicroBufferProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], + type: (buf: Uint8Array, offset: number) => buf[offset], + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], } -export const createSelvaSchemaMicroBuffer = (header: SelvaSchemaMicroBuffer): Uint8Array => { +export const createSelvaSchemaMicroBuffer = ( + header: SelvaSchemaMicroBuffer, +): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaMicroBufferByteSize) writeSelvaSchemaMicroBuffer(buffer, header, 0) return buffer @@ -4612,12 +4733,14 @@ export const readSelvaSchemaString = ( } export const readSelvaSchemaStringProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], - defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + type: (buf: Uint8Array, offset: number) => buf[offset], + fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], + defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createSelvaSchemaString = (header: SelvaSchemaString): Uint8Array => { +export const createSelvaSchemaString = ( + header: SelvaSchemaString, +): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaStringByteSize) writeSelvaSchemaString(buffer, header, 0) return buffer @@ -4690,8 +4813,8 @@ export const readSelvaSchemaText = ( } export const readSelvaSchemaTextProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], + type: (buf: Uint8Array, offset: number) => buf[offset], + nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], } export const createSelvaSchemaText = (header: SelvaSchemaText): Uint8Array => { @@ -4771,21 +4894,23 @@ export const readSelvaSchemaRef = ( const value: SelvaSchemaRef = { type: buf[offset], flags: buf[offset + 1], - dstNodeType: (readUint16(buf, offset + 2)) as TypeId, + dstNodeType: readUint16(buf, offset + 2) as TypeId, inverseField: buf[offset + 4], - edgeNodeType: (readUint16(buf, offset + 5)) as TypeId, + edgeNodeType: readUint16(buf, offset + 5) as TypeId, capped: readUint32(buf, offset + 7), } return value } export const readSelvaSchemaRefProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - flags: (buf: Uint8Array, offset: number) => buf[offset + 1], - dstNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, - inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], - edgeNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 5)) as TypeId, - capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + type: (buf: Uint8Array, offset: number) => buf[offset], + flags: (buf: Uint8Array, offset: number) => buf[offset + 1], + dstNodeType: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 2) as TypeId, + inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], + edgeNodeType: (buf: Uint8Array, offset: number) => + readUint16(buf, offset + 5) as TypeId, + capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } export const createSelvaSchemaRef = (header: SelvaSchemaRef): Uint8Array => { @@ -4864,13 +4989,15 @@ export const readSelvaSchemaColvec = ( } export const readSelvaSchemaColvecProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], + type: (buf: Uint8Array, offset: number) => buf[offset], + vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], } -export const createSelvaSchemaColvec = (header: SelvaSchemaColvec): Uint8Array => { +export const createSelvaSchemaColvec = ( + header: SelvaSchemaColvec, +): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaColvecByteSize) writeSelvaSchemaColvec(buffer, header, 0) return buffer @@ -4887,4 +5014,3 @@ export const pushSelvaSchemaColvec = ( buf.pushUint8(Number(header.hasDefault)) return index } - diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 7e19cc3d75..a32677bc63 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -50,6 +50,7 @@ await test('include', async (t) => { }) const a = client.create('user', { + name: 'mr snurf a', y: 4, x: true, flap: 9999, @@ -59,6 +60,7 @@ await test('include', async (t) => { }) const b = client.create('user', { + name: 'mr snurf b', y: 15, x: true, flap: 9999, @@ -71,6 +73,7 @@ await test('include', async (t) => { for (let i = 0; i < 5e6; i++) { client.create('user', { + name: `mr snurf ${i}`, y: i, x: true, flap: 9999, @@ -88,33 +91,40 @@ await test('include', async (t) => { type: 'user', filter: { props: { - flap: { ops: [{ op: '=', val: 9999 }] }, - }, - and: { - props: { - y: { ops: [{ op: '=', val: 100 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 3 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 4 }] }, - }, - }, - }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 670 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 15 }] }, - }, + name: { + // ADD LOWER CASE + // ops: [{ op: 'includes', val: 'flap', opts: { lowerCase: true } }], + ops: [{ op: 'includes', val: 'x' }], }, }, + // props: { + // flap: { ops: [{ op: '=', val: 9999 }] }, + // }, + // and: { + // props: { + // y: { ops: [{ op: '=', val: 100 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 3 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 4 }] }, + // }, + // }, + // }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 670 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 15 }] }, + // }, + // }, + // }, }, // filter('flap', '=', 9999) @@ -130,6 +140,7 @@ await test('include', async (t) => { props: { y: { include: {} }, + name: { include: {} }, mrFriend: { props: { y: { include: {} }, @@ -175,7 +186,7 @@ await test('include', async (t) => { }, 'filter speed', { - repeat: 100, + repeat: 10, }, ) // quite large @@ -186,7 +197,7 @@ await test('include', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - // console.dir(obj, { depth: 10 }) + console.dir(obj, { depth: 10 }) // RETURN NULL FOR UNDEFINED From e54de6e7e1a4aa8ce580a7ff5e429437c828d825 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 15:47:10 +0100 Subject: [PATCH 251/449] add aggregate support to new query and rename harmonicMean to hmean --- src/db-client/query/BasedDbQuery.ts | 4 +- src/db-client/query2/index.ts | 272 +++++++++++++++++++++++----- src/db-client/query2/result.ts | 3 + src/db-query/ast/aggregates.ts | 4 +- src/db-query/ast/ast.ts | 2 +- src/protocol/db-read/aggregate.ts | 5 +- src/utils/AutoSizedUint8Array.ts | 1 + test/aggregate/basic.ts | 12 +- test/aggregate/deep.ts | 9 +- test/aggregate/dev.ts | 2 +- test/aggregate/validation.ts | 9 +- test/query/ast.ts | 2 +- test/query/db.ts | 62 +++++++ test/query/types.ts | 25 +++ test/scenarios/nycTaxi.ts | 2 +- 15 files changed, 339 insertions(+), 75 deletions(-) create mode 100644 test/query/db.ts diff --git a/src/db-client/query/BasedDbQuery.ts b/src/db-client/query/BasedDbQuery.ts index 0a24e93b14..53193d5e83 100644 --- a/src/db-client/query/BasedDbQuery.ts +++ b/src/db-client/query/BasedDbQuery.ts @@ -399,14 +399,14 @@ export class QueryBranch { return this } - harmonicMean(...fields: string[]): T { + hmean(...fields: string[]): T { if (fields.length === 0) { throw new Error('Empty harmonic mean function called') } if (this.queryCommands) { this.queryCommands.push({ - method: 'harmonicMean', + method: 'hmean', args: fields, }) } else { diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 8f0704bcd0..da2df05737 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -35,6 +35,8 @@ class QueryBranch< SourceField extends string | number | symbol | undefined = undefined, IsRoot extends boolean = false, EdgeProps extends Record = {}, + Aggregate = {}, + GroupedKey extends string | undefined = undefined, > { constructor(ast: QueryAst) { this.ast = ast @@ -50,22 +52,17 @@ class QueryBranch< )[], >( ...props: F - ): IsRoot extends true - ? BasedQuery2< - S, - T, - (K extends '*' ? never : K) | ResolveIncludeArgs, - IsSingle - > - : QueryBranch< - S, - T, - (K extends '*' ? never : K) | ResolveIncludeArgs, - IsSingle, - SourceField, - IsRoot, - EdgeProps - > { + ): NextBranch< + S, + T, + (K extends '*' ? never : K) | ResolveIncludeArgs, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + GroupedKey + > { for (const prop of props as (string | Function)[]) { if (typeof prop === 'function') { prop((prop: string) => new QueryBranch(traverse(this.ast, prop))) @@ -136,71 +133,193 @@ class QueryBranch< return this.#addFilter(prop, op, val, opts, true) } - sum

>(...props: P[]): this { + sum

>( + ...props: P[] + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { [Key in P]: { sum: number } }, + GroupedKey + > { this.ast.sum ??= { props: [] } this.ast.sum.props.push(...(props as string[])) - return this + return this as any } - count(): this { + count(): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { count: number }, + GroupedKey + > { this.ast.count = {} - return this + return this as any } - cardinality(...props: string[]): this { + cardinality

( + ...props: P[] + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { [Key in P]: { cardinality: number } }, + GroupedKey + > { this.ast.cardinality ??= { props: [] } this.ast.cardinality.props.push(...props) - return this + return this as any } - avg

>(...props: P[]): this { + avg

>( + ...props: P[] + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { [Key in P]: { avg: number } }, + GroupedKey + > { this.ast.avg ??= { props: [] } this.ast.avg.props.push(...(props as string[])) - return this + return this as any } - hmean

>(...props: P[]): this { - this.ast.harmonicMean ??= { props: [] } - this.ast.harmonicMean.props.push(...(props as string[])) - return this + hmean

>( + ...props: P[] + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { [Key in P]: { hmean: number } }, + GroupedKey + > { + this.ast.hmean ??= { props: [] } + this.ast.hmean.props.push(...(props as string[])) + return this as any } - max

>(...props: P[]): this { + max

>( + ...props: P[] + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { [Key in P]: { max: InferPathType } }, + GroupedKey + > { this.ast.max ??= { props: [] } this.ast.max.props.push(...(props as string[])) - return this + return this as any } - min

>(...props: P[]): this { + min

>( + ...props: P[] + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { [Key in P]: { min: InferPathType } }, + GroupedKey + > { this.ast.min ??= { props: [] } this.ast.min.props.push(...(props as string[])) - return this + return this as any } stddev

>( prop: P | P[], opts?: aggFnOptions, - ): this { + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { + [Key in P extends any[] ? P[number] : P]: { stddev: number } + }, + GroupedKey + > { this.ast.stddev ??= { props: [] } const props = Array.isArray(prop) ? prop : [prop] this.ast.stddev.props.push(...(props as string[])) if (opts?.mode) { this.ast.stddev.samplingMode = opts.mode } - return this + return this as any } - var

>(prop: P | P[], opts?: aggFnOptions): this { + var

>( + prop: P | P[], + opts?: aggFnOptions, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & { + [Key in P extends any[] ? P[number] : P]: { variance: number } + }, + GroupedKey + > { this.ast.variance ??= { props: [] } const props = Array.isArray(prop) ? prop : [prop] this.ast.variance.props.push(...(props as string[])) if (opts?.mode) { this.ast.variance.samplingMode = opts.mode } - return this + return this as any } - groupBy(prop: string, step?: StepInput): this { + groupBy

( + prop: P, + step?: StepInput, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + P + > { this.ast.groupBy = { prop } if (step) { if (typeof step === 'object') { @@ -212,7 +331,7 @@ class QueryBranch< this.ast.groupBy.step = step } } - return this + return this as any } #filterGroup?: FilterAst @@ -288,7 +407,19 @@ export class BasedQuery2< | { field: any; select: any } | string = '*', IsSingle extends boolean = false, -> extends QueryBranch { + Aggregate = {}, + GroupedKey extends string | undefined = undefined, +> extends QueryBranch< + S, + T, + K, + IsSingle, + undefined, + true, + {}, + Aggregate, + GroupedKey +> { constructor( db: DbClient, type: T, @@ -301,14 +432,32 @@ export class BasedQuery2< } db: DbClient async get(): Promise< - IsSingle extends true - ? PickOutput, K>> | null - : PickOutput, K>>[] + [keyof Aggregate] extends [never] + ? IsSingle extends true + ? PickOutput< + S, + T, + ResolveInclude, K> + > | null + : PickOutput, K>>[] + : GroupedKey extends string + ? Record + : Aggregate > { - if (!this.ast.props) { + if ( + !this.ast.props && + !this.ast.sum && + !this.ast.count && + !this.ast.avg && + !this.ast.hmean && + !this.ast.max && + !this.ast.min && + !this.ast.stddev && + !this.ast.variance && + !this.ast.cardinality + ) { this.include('*') } - if (!this.db.schema) { await this.db.once('schema') } @@ -316,9 +465,10 @@ export class BasedQuery2< const ctx = astToQueryCtx( this.db.schema!, this.ast, - new AutoSizedUint8Array(1000), + new AutoSizedUint8Array(), ) const result = await this.db.hooks.getQueryBuf(ctx.query) + return proxyResult(result, ctx.readSchema) as any } } @@ -390,6 +540,8 @@ export type ResolveIncludeArgs = T extends ( infer Single, infer SourceField, any, + any, + any, any > ? { field: SourceField; select: K } @@ -397,6 +549,36 @@ export type ResolveIncludeArgs = T extends ( ? ResolveDotPath : T +// Helper type to simplify method return types +type NextBranch< + S extends { types: any }, + T extends keyof S['types'], + K extends + | keyof ResolvedProps + | '*' + | '**' + | { field: any; select: any } + | string, + IsSingle extends boolean, + SourceField extends string | number | symbol | undefined, + IsRoot extends boolean, + EdgeProps extends Record, + Aggregate, + GroupedKey extends string | undefined, +> = IsRoot extends true + ? BasedQuery2 + : QueryBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + GroupedKey + > + function traverse(target: any, prop: string) { const path = prop.split('.') for (const key of path) { diff --git a/src/db-client/query2/result.ts b/src/db-client/query2/result.ts index 61365fdea5..f820bab212 100644 --- a/src/db-client/query2/result.ts +++ b/src/db-client/query2/result.ts @@ -67,6 +67,9 @@ const handler: ProxyHandler = { } export const proxyResult = (buffer: Uint8Array, schema: ReaderSchema) => { + if ('aggregate' in schema) { + return resultToObject(schema, buffer, buffer.byteLength - 4) + } const single = schema.type === ReaderSchemaEnum.single const length = readUint32(buffer, 0) if (length === 0) return single ? null : [] diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index b548063165..49a3491306 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -70,7 +70,7 @@ const isRootCountOnly = (ast: QueryAst) => { !ast.max && !ast.stddev && !ast.variance && - !ast.harmonicMean && + !ast.hmean && !ast.cardinality ) } @@ -213,7 +213,7 @@ export const isAggregateAst = (ast: QueryAst) => { ast.max || ast.stddev || ast.variance || - ast.harmonicMean || + ast.hmean || ast.cardinality ) } diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 5341687984..7e1d528aae 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -72,7 +72,7 @@ export type QueryAst = { sum?: { props: string[] } cardinality?: { props: string[] } avg?: { props: string[] } - harmonicMean?: { props: string[] } + hmean?: { props: string[] } max?: { props: string[] } min?: { props: string[] } stddev?: { props: string[]; samplingMode?: 'sample' | 'population' } diff --git a/src/protocol/db-read/aggregate.ts b/src/protocol/db-read/aggregate.ts index 3e8f6475ba..16c83f7b23 100644 --- a/src/protocol/db-read/aggregate.ts +++ b/src/protocol/db-read/aggregate.ts @@ -140,8 +140,9 @@ const readAggValues = ( const val = readFn(result, baseOffset + agg.resultPos) - const pathSuffix = - agg.type === AggFunction.count ? [] : [AggFunctionInverse[agg.type]] + const typeName = AggFunctionInverse[agg.type] + + const pathSuffix = agg.type === AggFunction.count ? [] : [typeName] // MV: check for edgesagg.path[1][0] == '$` setByPath(targetObject, [...agg.path, ...pathSuffix], val) diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 0c8011742a..5c5b40611d 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -54,6 +54,7 @@ export class AutoSizedUint8Array { const currentLength = this.data.byteLength if (currentLength >= requiredLength) return if (requiredLength > this.maxLength) { + console.trace({ requiredLength }, this.maxLength) throw AutoSizedUint8Array.ERR_OVERFLOW } diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 7b382a3c62..b803ac860e 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -541,7 +541,7 @@ await test('numeric types', async (t) => { deepEqual( await db .query('vote') - .harmonicMean('NL', 'PT', 'FI') + .hmean('NL', 'PT', 'FI') .groupBy('region') .get() .toObject(), @@ -562,7 +562,7 @@ await test('numeric types', async (t) => { FI: { hmean: -50.99900000000001 }, // harmonic mean is not designed for negative numbers but possible }, }, - 'harmonic_mean, main, group by', + 'hmean, main, group by', ) deepEqual( await db @@ -751,7 +751,7 @@ await test('numeric types', async (t) => { deepEqual( await db .query('sequence') - .include((q) => q('votes').harmonicMean('NL')) + .include((q) => q('votes').hmean('NL')) .get() .toObject(), [ @@ -762,7 +762,7 @@ await test('numeric types', async (t) => { }, }, ], - 'harmonic_mean, references, not grouped', + 'hmean, references, not grouped', ) deepEqual( await db @@ -948,7 +948,7 @@ await test('numeric types', async (t) => { deepEqual( await db .query('sequence') - .include((q) => q('votes').groupBy('region').harmonicMean('NL')) + .include((q) => q('votes').groupBy('region').hmean('NL')) .get() .toObject(), [ @@ -967,7 +967,7 @@ await test('numeric types', async (t) => { }, }, ], - 'harmonic_mean, references, group by', + 'hmean, references, group by', ) }) diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index b71e5908a8..10056393f0 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -415,12 +415,7 @@ await test('enums', async (t) => { ) deepEqual( - await db - .query('beer') - .harmonicMean('price') - .groupBy('type') - .get() - .toObject(), + await db.query('beer').hmean('price').groupBy('type').get().toObject(), { Tripel: { price: { hmean: 11.839662447257384 }, @@ -429,7 +424,7 @@ await test('enums', async (t) => { price: { hmean: 7.199999999999999 }, // 7.2 should be approximated }, }, - 'harmonic_mean by enum in main', + 'hmean by enum in main', ) }) diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index 67ffec15bb..51cb99f824 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -28,7 +28,7 @@ await test('kev', async (t) => { // console.log((await db.query('trip').include('distance').get()).debug()) // console.log( // ( - // await db.query('trip').harmonicMean('distance').avg('distance').get() + // await db.query('trip').hmean('distance').avg('distance').get() // ).debug(), // ) diff --git a/test/aggregate/validation.ts b/test/aggregate/validation.ts index 42ee757b74..cbb975f96e 100644 --- a/test/aggregate/validation.ts +++ b/test/aggregate/validation.ts @@ -57,19 +57,14 @@ await test('undefined numbers', async (t) => { ) deepEqual( - await db - .query('vote') - .harmonicMean('AU', 'FI') - .groupBy('region') - .get() - .toObject(), + await db.query('vote').hmean('AU', 'FI').groupBy('region').get().toObject(), { EU: { AU: { hmean: 13.93939393939394 }, FI: { hmean: 0 }, }, }, - 'harmonic_mean affected by count because number is initialized with zero', + 'hmean affected by count because number is initialized with zero', ) }) diff --git a/test/query/ast.ts b/test/query/ast.ts index af2dea0eb6..ea0184eb95 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -183,7 +183,7 @@ await test('query ast creation', async (t) => { count: {}, cardinality: { props: ['name'] }, avg: { props: ['age'] }, - harmonicMean: { props: ['age'] }, + hmean: { props: ['age'] }, max: { props: ['age'] }, min: { props: ['age'] }, stddev: { props: ['age'], samplingMode: 'population' }, diff --git a/test/query/db.ts b/test/query/db.ts new file mode 100644 index 0000000000..da6e34babc --- /dev/null +++ b/test/query/db.ts @@ -0,0 +1,62 @@ +import { deepEqual, testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('query db', async (t) => { + const db = await testDb(t, { + locales: { + en: true, + nl: true, + }, + types: { + user: { + name: 'string', + isNice: 'boolean', + age: 'number', + }, + }, + }) + + db.create('user', { + name: 'john', + isNice: false, + age: 21, + }) + + db.create('user', { + name: 'billy', + isNice: true, + age: 49, + }) + + { + const res = await db + .query2('user') + .include('name') + .filter('isNice', '=', false) + .get() + deepEqual(res, [{ id: 1, name: 'john' }]) + } + + { + const res = await db + .query2('user') + .include('name') + .filter('isNice', '=', true) + .or('age', '=', 21) + .get() + deepEqual(res, [ + { id: 1, name: 'john' }, + { id: 2, name: 'billy' }, + ]) + } + + { + const res = await db.query2('user').sum('age').get() + deepEqual(res, { age: { sum: 70 } }) + } + + { + const res = await db.query2('user').sum('age').groupBy('name').get() + deepEqual(res, { john: { age: { sum: 21 } }, billy: { age: { sum: 49 } } }) + } +}) diff --git a/test/query/types.ts b/test/query/types.ts index de511dd9ad..747b50eba2 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -282,6 +282,31 @@ await test('query types', async (t) => { // @ts-expect-error queryInvalid.var('s') } + + { + // Aggregate return type check + const query = db.query2('everything').sum('n') + const res = await query.get() + + if (res) { + const n: number = res.n.sum + // @ts-expect-error + const s = res.s + } + + const queryGroup = db.query2('everything').groupBy('s').sum('n') + const resGroup = await queryGroup.get() + + // resGroup should be Record + if (resGroup) { + const group = resGroup['some-group'] + if (group) { + const n: number = group.n.sum + // @ts-expect-error + const s = group.s + } + } + } }) await test('query types', async (t) => { diff --git a/test/scenarios/nycTaxi.ts b/test/scenarios/nycTaxi.ts index 98201b710e..64055b2385 100644 --- a/test/scenarios/nycTaxi.ts +++ b/test/scenarios/nycTaxi.ts @@ -683,7 +683,7 @@ await test.skip('taxi', async (t) => { .or((t) => t.filter('pickupHour', '>=', 16).filter('pickupHour', '<=', 19)) .groupBy('pickup', { step: 'dow', timeZone: 'America/New_York' }) //.groupBy('pickupDropoffLocs') - .harmonicMean('avgSpeed') + .hmean('avgSpeed') .get() .inspect() From cd3ca48ad49c1d87dc651837049fb7c670d9263b Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 15:57:49 +0100 Subject: [PATCH 252/449] add .range --- src/db-client/query2/index.ts | 19 +++++++++++++++++++ src/utils/AutoSizedUint8Array.ts | 1 - test/query/db.ts | 22 ++++++++++++++++++++++ test/query/types.ts | 2 +- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index da2df05737..f8e0aa057b 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -306,6 +306,25 @@ class QueryBranch< return this as any } + range( + start: number, + end?: number, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + GroupedKey + > { + const limit = end ? end - start : 1000 + this.ast.range = { start, end: limit } + return this as any + } + groupBy

( prop: P, step?: StepInput, diff --git a/src/utils/AutoSizedUint8Array.ts b/src/utils/AutoSizedUint8Array.ts index 5c5b40611d..0c8011742a 100644 --- a/src/utils/AutoSizedUint8Array.ts +++ b/src/utils/AutoSizedUint8Array.ts @@ -54,7 +54,6 @@ export class AutoSizedUint8Array { const currentLength = this.data.byteLength if (currentLength >= requiredLength) return if (requiredLength > this.maxLength) { - console.trace({ requiredLength }, this.maxLength) throw AutoSizedUint8Array.ERR_OVERFLOW } diff --git a/test/query/db.ts b/test/query/db.ts index da6e34babc..1a688e11de 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -50,6 +50,18 @@ await test('query db', async (t) => { ]) } + { + const res = await db + .query2('user') + .include('name') + .filter('isNice', '=', true) + .or('age', '=', 21) + .range(0, 1) + .get() + + deepEqual(res, [{ id: 1, name: 'john' }]) + } + { const res = await db.query2('user').sum('age').get() deepEqual(res, { age: { sum: 70 } }) @@ -59,4 +71,14 @@ await test('query db', async (t) => { const res = await db.query2('user').sum('age').groupBy('name').get() deepEqual(res, { john: { age: { sum: 21 } }, billy: { age: { sum: 49 } } }) } + + { + const res = await db + .query2('user') + .filter('isNice', '=', true) + .sum('age') + .groupBy('name') + .get() + deepEqual(res, { billy: { age: { sum: 49 } } }) + } }) diff --git a/test/query/types.ts b/test/query/types.ts index 747b50eba2..f6959850fe 100644 --- a/test/query/types.ts +++ b/test/query/types.ts @@ -1,7 +1,7 @@ import { testDb } from '../shared/index.js' import test from '../shared/test.js' -await test('query types', async (t) => { +await test.skip('query types', async (t) => { const db = await testDb(t, { locales: { en: true }, types: { From 57b35412255019ac9934f892957597c569f4cc4a Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 16:21:14 +0100 Subject: [PATCH 253/449] init sort --- src/db-client/query2/index.ts | 85 +++++++++++++++++++++++------------ test/query/ast.ts | 25 +++++++++++ test/query/db.ts | 14 ++++++ 3 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index f8e0aa057b..510751724b 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -22,7 +22,7 @@ import type { aggFnOptions, } from '../query/aggregates/types.js' -class QueryBranch< +class Query< S extends { types: any } = { types: any }, T extends keyof S['types'] = any, K extends @@ -48,7 +48,7 @@ class QueryBranch< | Path | '*' | '**' - | ((q: SelectFn) => QueryBranch) + | ((q: SelectFn) => Query) )[], >( ...props: F @@ -65,7 +65,7 @@ class QueryBranch< > { for (const prop of props as (string | Function)[]) { if (typeof prop === 'function') { - prop((prop: string) => new QueryBranch(traverse(this.ast, prop))) + prop((prop: string) => new Query(traverse(this.ast, prop))) } else { traverse(this.ast, prop).include = {} } @@ -76,7 +76,7 @@ class QueryBranch< filter( fn: ( filter: FilterFn, - ) => FilterBranch>, + ) => FilterBranch>, ): FilterBranch filter< P extends @@ -96,7 +96,7 @@ class QueryBranch< and( fn: ( filter: FilterFn, - ) => FilterBranch>, + ) => FilterBranch>, ): FilterBranch and< P extends @@ -115,7 +115,7 @@ class QueryBranch< or( fn: ( filter: FilterFn, - ) => FilterBranch>, + ) => FilterBranch>, ): FilterBranch or< P extends @@ -306,6 +306,45 @@ class QueryBranch< return this as any } + sort

( + prop: P, + order?: 'asc' | 'desc', + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + GroupedKey + > { + this.ast.sort = { prop, order: order || 'asc' } + return this as any + } + + order( + order: 'asc' | 'desc', + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + GroupedKey + > { + if (this.ast.sort) { + this.ast.sort.order = order + } else { + this.ast.sort = { prop: 'id', order } + } + return this as any + } + range( start: number, end?: number, @@ -366,7 +405,7 @@ class QueryBranch< const target = isOr ? this.#filterGroup! : (this.#filterGroup!.and ??= {}) - const branch = new QueryBranch(target) + const branch = new Query(target) branch.#filterGroup = target ;(branch.filter as any)(...args) return branch @@ -393,7 +432,7 @@ type FilterMethods = { export function query< S extends { types: any } = { types: any }, T extends keyof S['types'] & string = keyof S['types'] & string, ->(type: T): QueryBranch +>(type: T): Query // This overload is for when the user provides NO schema argument + ID, rely on generic default or explicit generic export function query< @@ -402,7 +441,7 @@ export function query< >( type: T, id: number | Partial>, -): QueryBranch +): Query export function query< S extends { types: any }, @@ -410,10 +449,10 @@ export function query< >( type: T, id?: number | Partial>, -): QueryBranch { +): Query { const ast: any = { type } if (id) ast.target = id - return new QueryBranch(ast) + return new Query(ast) } export class BasedQuery2< @@ -428,17 +467,7 @@ export class BasedQuery2< IsSingle extends boolean = false, Aggregate = {}, GroupedKey extends string | undefined = undefined, -> extends QueryBranch< - S, - T, - K, - IsSingle, - undefined, - true, - {}, - Aggregate, - GroupedKey -> { +> extends Query { constructor( db: DbClient, type: T, @@ -500,7 +529,7 @@ type FilterFn< S, T, EdgeProps, - FilterBranch> + FilterBranch> > type FilterSignature< @@ -512,7 +541,7 @@ type FilterSignature< ( fn: ( filter: FilterFn, - ) => FilterBranch>, + ) => FilterBranch>, ): Result < P extends @@ -530,7 +559,7 @@ type SelectFn = < P extends keyof ResolvedProps, >( field: P, -) => QueryBranch< +) => Query< S, ResolvedProps[P] extends { ref: infer R extends string } ? R @@ -549,10 +578,10 @@ type SelectFn = < : {}) > -// ResolveIncludeArgs needs to stay here because it refers to QueryBranch +// ResolveIncludeArgs needs to stay here because it refers to Query export type ResolveIncludeArgs = T extends ( q: any, -) => QueryBranch< +) => Query< infer S, infer T, infer K, @@ -586,7 +615,7 @@ type NextBranch< GroupedKey extends string | undefined, > = IsRoot extends true ? BasedQuery2 - : QueryBranch< + : Query< S, T, K, diff --git a/test/query/ast.ts b/test/query/ast.ts index ea0184eb95..aee0bd1535 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -199,4 +199,29 @@ await test('query ast creation', async (t) => { groupBy: { prop: 'age', step: 10 }, }) } + { + const q1 = query('user').sort('age') + deepEqual(q1.ast, { + type: 'user', + sort: { prop: 'age', order: 'asc' }, + }) + + const q2 = query('user').sort('age', 'desc') + deepEqual(q2.ast, { + type: 'user', + sort: { prop: 'age', order: 'desc' }, + }) + + const q3 = query('user').order('desc') + deepEqual(q3.ast, { + type: 'user', + sort: { prop: 'id', order: 'desc' }, + }) + + const q4 = query('user').sort('age').order('desc') + deepEqual(q4.ast, { + type: 'user', + sort: { prop: 'age', order: 'desc' }, + }) + } }) diff --git a/test/query/db.ts b/test/query/db.ts index 1a688e11de..12bbe02f98 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -50,6 +50,20 @@ await test('query db', async (t) => { ]) } + // { + // const res = await db + // .query2('user') + // .include('name') + // .filter('isNice', '=', true) + // .or('age', '=', 21) + // .sort('name') + // .get() + // deepEqual(res, [ + // { id: 2, name: 'billy' }, + // { id: 1, name: 'john' }, + // ]) + // } + { const res = await db .query2('user') From e06a8018b7bfca082f56c23d9062634378c70d04 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 16:51:19 +0100 Subject: [PATCH 254/449] add tests for validation --- src/schema/defs/props/alias.ts | 3 + src/schema/defs/props/fixed.ts | 2 +- src/schema/defs/props/strings.ts | 8 ++ test/modify/props/validation.ts | 166 ---------------------------- test/modify/type_display.ts | 67 ----------- test/modify/validation/alias.ts | 42 +++++++ test/modify/validation/binary.ts | 42 +++++++ test/modify/validation/boolean.ts | 44 ++++++++ test/modify/validation/enum.ts | 43 +++++++ test/modify/validation/integers.ts | 81 ++++++++++++++ test/modify/validation/json.ts | 29 +++++ test/modify/validation/number.ts | 55 +++++++++ test/modify/validation/string.ts | 47 ++++++++ test/modify/validation/text.ts | 56 ++++++++++ test/modify/validation/timestamp.ts | 42 +++++++ 15 files changed, 493 insertions(+), 234 deletions(-) delete mode 100644 test/modify/props/validation.ts delete mode 100644 test/modify/type_display.ts create mode 100644 test/modify/validation/alias.ts create mode 100644 test/modify/validation/binary.ts create mode 100644 test/modify/validation/boolean.ts create mode 100644 test/modify/validation/enum.ts create mode 100644 test/modify/validation/integers.ts create mode 100644 test/modify/validation/json.ts create mode 100644 test/modify/validation/number.ts create mode 100644 test/modify/validation/string.ts create mode 100644 test/modify/validation/text.ts create mode 100644 test/modify/validation/timestamp.ts diff --git a/src/schema/defs/props/alias.ts b/src/schema/defs/props/alias.ts index c544269efc..99bd0d2019 100644 --- a/src/schema/defs/props/alias.ts +++ b/src/schema/defs/props/alias.ts @@ -11,6 +11,9 @@ export const alias = class Alias extends BasePropDef { if (typeof value !== 'string') { throw new Error('Invalid type for alias ' + this.path.join('.')) } + if (!value.trim()) { + throw new Error('Invalid alias ' + this.path.join('.')) + } buf.pushString(value) } override pushSelvaSchema(buf: AutoSizedUint8Array) { diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index dfa00409c9..c003543524 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -26,7 +26,7 @@ export const number = class Number extends BasePropDef { min = -globalThis.Number.MAX_VALUE max = globalThis.Number.MAX_VALUE override validate(value: unknown): asserts value is number { - if (typeof value !== 'number') { + if (typeof value !== 'number' || !globalThis.Number.isFinite(value)) { throw new Error( `Invalid type for ${this.schema.type} ${this.path.join('.')}`, ) diff --git a/src/schema/defs/props/strings.ts b/src/schema/defs/props/strings.ts index d1b7356134..bc4085b174 100644 --- a/src/schema/defs/props/strings.ts +++ b/src/schema/defs/props/strings.ts @@ -144,11 +144,19 @@ export const text = class Text extends string { lang: LangCodeEnum = LangCode.none, ) { if (typeof value === 'string') { + if (lang === LangCode.none) { + throw new Error( + `Invalid type, text needs to be an object ${this.path.join('.')}`, + ) + } const index = buf.reserveUint32() const start = buf.length super.pushValue(buf, value, op, lang) buf.writeUint32(buf.length - start, index) } else if (typeof value === 'object' && value !== null) { + if (Array.isArray(value)) { + throw new Error('Invalid type for text ' + this.path.join('.')) + } for (const key in value) { if (!(key in LangCode)) { throw new Error( diff --git a/test/modify/props/validation.ts b/test/modify/props/validation.ts deleted file mode 100644 index 81f2e204ad..0000000000 --- a/test/modify/props/validation.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { throws } from '../../shared/assert.js' -import { testDb } from '../../shared/index.js' -import test from '../../shared/test.js' - -await test('modify - validation', async (t) => { - const db = await testDb(t, { - locales: { en: true }, - types: { - thing: { - name: { type: 'string', min: 2, max: 5 }, - myString: { type: 'string' }, - score: { type: 'number', min: 10, max: 20 }, - myNumber: { type: 'number' }, - isActive: { type: 'boolean' }, - myEnum: { enum: ['a', 'b'] }, - myJson: { type: 'json' }, - myText: { type: 'text' }, - myTs: { type: 'timestamp', min: 1000, max: 2000 }, - myInt8: { type: 'int8' }, // -128 to 127 - myUint8: { type: 'uint8' }, // 0 to 255 - myInt16: { type: 'int16' }, // -32768 to 32767 - myUint16: { type: 'uint16' }, // 0 to 65535 - myInt32: { type: 'int32' }, - myUint32: { type: 'uint32' }, - myBlob: { type: 'binary', maxBytes: 10 }, - myAlias: { type: 'alias' }, - }, - }, - }) - - // String - await throws( - () => db.create('thing', { name: 123 as any }), - 'string should fail with number', - ) - await throws( - () => db.create('thing', { name: 'a' }), - 'string should fail if too short', - ) - await throws( - () => db.create('thing', { name: 'aaaaaa' }), - 'string should fail if too long', - ) - await db.create('thing', { name: 'abc' }) - - // Number - await throws( - () => db.create('thing', { score: '123' as any }), - 'number should fail with string', - ) - await throws( - () => db.create('thing', { score: 9 }), - 'number should fail if too small', - ) - await throws( - () => db.create('thing', { score: 21 }), - 'number should fail if too large', - ) - await db.create('thing', { score: 15 }) - - // Boolean - await throws( - () => db.create('thing', { isActive: 'true' as any }), - 'boolean should fail with string', - ) - await throws( - () => db.create('thing', { isActive: 1 as any }), - 'boolean should fail with number', - ) - await db.create('thing', { isActive: true }) - - // Enum - await throws( - () => db.create('thing', { myEnum: 'c' as any }), - 'enum should fail with invalid value', - ) - await db.create('thing', { myEnum: 'b' }) - - // Json - // Passed undefined is checked in separate.ts but undefined in object usually means "ignore" or "delete". - // Explicit null might be allowed as valid JSON. - // Validation for JSON is loose (just checks serialization usually). - // No explicit tests for JSON structure as checking JSON validity is tricky via TS object (which is already valid if passed). - - // Text - await throws( - () => db.create('thing', { myText: 123 as any }), - 'text should fail with number', - ) - await throws( - () => db.create('thing', { myText: { en: 123 as any } }), - 'text value should fail with number', - ) - await throws( - () => db.create('thing', { myText: { xx: 'hello' } as any }), - 'text should fail with invalid locale', - ) - await db.create('thing', { myText: { en: 'works' } }) - - // Timestamp - await throws(() => db.create('thing', { myTs: 500 }), 'timestamp too small') - await throws(() => db.create('thing', { myTs: 3000 }), 'timestamp too large') - await db.create('thing', { myTs: 1500 }) - - // Int8 (-128 to 127) - await throws( - () => db.create('thing', { myInt8: '1' as any }), - 'int8 fail string', - ) - await throws(() => db.create('thing', { myInt8: 1.5 }), 'int8 fail float') - await throws(() => db.create('thing', { myInt8: 128 }), 'int8 overflow') - await throws(() => db.create('thing', { myInt8: -129 }), 'int8 underflow') - await db.create('thing', { myInt8: 127 }) - - // Uint8 (0 to 255) - await throws(() => db.create('thing', { myUint8: -1 }), 'uint8 negative') - await throws(() => db.create('thing', { myUint8: 256 }), 'uint8 overflow') - await db.create('thing', { myUint8: 255 }) - - // Int16 (-32768 to 32767) - await throws(() => db.create('thing', { myInt16: 32768 }), 'int16 overflow') - await throws(() => db.create('thing', { myInt16: -32769 }), 'int16 underflow') - await db.create('thing', { myInt16: 32767 }) - - // Uint16 (0 to 65535) - await throws(() => db.create('thing', { myUint16: 65536 }), 'uint16 overflow') - await db.create('thing', { myUint16: 65535 }) - - // Int32 - await throws( - () => db.create('thing', { myInt32: 2147483648 }), - 'int32 overflow', - ) - await db.create('thing', { myInt32: 2147483647 }) - - // Uint32 - await throws( - () => db.create('thing', { myUint32: 4294967296 }), - 'uint32 overflow', - ) - await db.create('thing', { myUint32: 4294967295 }) - - // Binary - await throws( - () => db.create('thing', { myBlob: 'not a buffer' as any }), - 'binary fail with string', - ) - await throws( - () => db.create('thing', { myBlob: new Uint8Array(20) }), - 'binary maxBytes', - ) - await db.create('thing', { myBlob: new Uint8Array(5) }) - - // Alias - await throws( - () => db.create('thing', { myAlias: 123 as any }), - 'alias fail with number', - ) - const id1 = await db.create('thing', { myAlias: 'cool-alias' }) - - // Alias collision (should throw?) - // db aliases are unique. - // Although not strictly "prop validation" logic (it's uniqueness constraint), alias implies unique. - // But strict type validation is what we tested above. - // Let's stop at type validation as requested. -}) diff --git a/test/modify/type_display.ts b/test/modify/type_display.ts deleted file mode 100644 index 98b3b16030..0000000000 --- a/test/modify/type_display.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { BasedTestDb, testDb } from '../../test/shared/index.js' - -const schema = { - locales: { - en: true, - nl: true, - }, - types: { - soAnnoy: { - title: 'string', - users: { - items: { - ref: 'user', - prop: 'annoyingThings', - $validEdge: 'boolean', - }, - }, - }, - user: { - name: 'string', - isNice: 'boolean', - textField: 'text', - friend: { - ref: 'user', - prop: 'friend', - $rank: 'number', - }, - otherUsers: { - items: { - ref: 'user', - prop: 'otherUsers', - $role: 'string', - }, - }, - }, - }, -} as const - -// Simulated usage -async function run() { - const t = { after: () => {}, tmp: './tmp' } - const db: BasedTestDb = await testDb(t, schema) - // Inspect this function signature via d.ts - checkTypes(db) -} - -export function checkTypes(db: BasedTestDb) { - const userA = db.create('user', { - isNice: true, - textField: { - nl: 'mijn text', - en: 'my text', - }, - annoyingThings: [ - { - id: 2, - // @ts-expect-error - $invalidEdge: true, // Should error - $validEdge: true, - }, - { - id: 3, - // correct - }, - ], - }) -} diff --git a/test/modify/validation/alias.ts b/test/modify/validation/alias.ts new file mode 100644 index 0000000000..9a6910cbe5 --- /dev/null +++ b/test/modify/validation/alias.ts @@ -0,0 +1,42 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - alias', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myAlias: { type: 'alias' }, + }, + }, + }) + + // Alias + await throws( + // @ts-expect-error + () => db.create('thing', { myAlias: 123 }), + 'alias fail with number', + ) + const id1 = await db.create('thing', { myAlias: 'cool-alias' }) + + // Extended validation + // Checking validation, not collision (collision is a separate constraint) + await throws( + () => db.create('thing', { myAlias: '' }), + 'alias fail with empty string', + ) + await throws( + () => db.create('thing', { myAlias: ' ' }), + 'alias fail with spaces string', // often desirable to prevent + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myAlias: ['a'] }), + 'alias fail with array', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myAlias: {} }), + 'alias fail with object', + ) +}) diff --git a/test/modify/validation/binary.ts b/test/modify/validation/binary.ts new file mode 100644 index 0000000000..6ce4e9571d --- /dev/null +++ b/test/modify/validation/binary.ts @@ -0,0 +1,42 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - binary', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myBlob: { type: 'binary', maxBytes: 10 }, + }, + }, + }) + + // Binary + await throws( + // @ts-expect-error + () => db.create('thing', { myBlob: 'not a buffer' }), + 'binary fail with string', + ) + await throws( + () => db.create('thing', { myBlob: new Uint8Array(20) }), + 'binary maxBytes', + ) + await db.create('thing', { myBlob: new Uint8Array(5) }) + + // Extended validation + await throws( + // @ts-expect-error + () => db.create('thing', { myBlob: 123 }), + 'binary fail with number', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myBlob: [1, 2, 3] }), + 'binary fail with array of numbers', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myBlob: {} }), + 'binary fail with object', + ) +}) diff --git a/test/modify/validation/boolean.ts b/test/modify/validation/boolean.ts new file mode 100644 index 0000000000..be97eebfd5 --- /dev/null +++ b/test/modify/validation/boolean.ts @@ -0,0 +1,44 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - boolean', async (t) => { + const db = await testDb(t, { + types: { + thing: { + isActive: { type: 'boolean' }, + }, + }, + }) + + // Boolean + await throws( + // @ts-expect-error + () => db.create('thing', { isActive: 'true' }), + 'boolean should fail with string', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { isActive: 1 }), + 'boolean should fail with number', + ) + await db.create('thing', { isActive: true }) + + // Extended validation + + await throws( + // @ts-expect-error + () => db.create('thing', { isActive: {} }), + 'boolean should fail with object', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { isActive: [] }), + 'boolean should fail with array', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { isActive: 'false' }), + 'boolean should fail with "false" string', + ) +}) diff --git a/test/modify/validation/enum.ts b/test/modify/validation/enum.ts new file mode 100644 index 0000000000..3208dce832 --- /dev/null +++ b/test/modify/validation/enum.ts @@ -0,0 +1,43 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - enum', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myEnum: { enum: ['a', 'b'] }, + }, + }, + }) + + // Enum + await throws( + // @ts-expect-error + () => db.create('thing', { myEnum: 'c' }), + 'enum should fail with invalid value', + ) + await db.create('thing', { myEnum: 'b' }) + + // Extended validation + await throws( + // @ts-expect-error + () => db.create('thing', { myEnum: 'A' }), + 'enum should be case sensitive', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myEnum: 1 }), + 'enum should fail with number', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myEnum: true }), + 'enum should fail with boolean', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myEnum: [] }), + 'enum should fail with array', + ) +}) diff --git a/test/modify/validation/integers.ts b/test/modify/validation/integers.ts new file mode 100644 index 0000000000..d52824bd80 --- /dev/null +++ b/test/modify/validation/integers.ts @@ -0,0 +1,81 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - integers', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myInt8: { type: 'int8' }, // -128 to 127 + myUint8: { type: 'uint8' }, // 0 to 255 + myInt16: { type: 'int16' }, // -32768 to 32767 + myUint16: { type: 'uint16' }, // 0 to 65535 + myInt32: { type: 'int32' }, + myUint32: { type: 'uint32' }, + }, + }, + }) + + // Int8 (-128 to 127) + await throws( + // @ts-expect-error + () => db.create('thing', { myInt8: '1' }), + 'int8 fail string', + ) + await throws(() => db.create('thing', { myInt8: 1.5 }), 'int8 fail float') + await throws(() => db.create('thing', { myInt8: 128 }), 'int8 overflow') + await throws(() => db.create('thing', { myInt8: -129 }), 'int8 underflow') + await db.create('thing', { myInt8: 127 }) + + // Uint8 (0 to 255) + await throws(() => db.create('thing', { myUint8: -1 }), 'uint8 negative') + await throws(() => db.create('thing', { myUint8: 256 }), 'uint8 overflow') + await db.create('thing', { myUint8: 255 }) + + // Int16 (-32768 to 32767) + await throws(() => db.create('thing', { myInt16: 32768 }), 'int16 overflow') + await throws(() => db.create('thing', { myInt16: -32769 }), 'int16 underflow') + await db.create('thing', { myInt16: 32767 }) + + // Uint16 (0 to 65535) + await throws(() => db.create('thing', { myUint16: 65536 }), 'uint16 overflow') + await db.create('thing', { myUint16: 65535 }) + + // Int32 + await throws( + () => db.create('thing', { myInt32: 2147483648 }), + 'int32 overflow', + ) + await db.create('thing', { myInt32: 2147483647 }) + + // Uint32 + await throws( + () => db.create('thing', { myUint32: 4294967296 }), + 'uint32 overflow', + ) + await db.create('thing', { myUint32: 4294967295 }) + + // Extended validation (General invalid types for integers) + await throws(() => db.create('thing', { myInt8: NaN }), 'int8 fail NaN') + await throws( + () => db.create('thing', { myInt8: Infinity }), + 'int8 fail Infinity', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myInt8: true }), + 'int8 fail boolean', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myInt8: [] }), + 'int8 fail array', + ) + + // Test float fail for others + await throws(() => db.create('thing', { myUint8: 1.5 }), 'uint8 fail float') + await throws(() => db.create('thing', { myInt16: 1.5 }), 'int16 fail float') + await throws(() => db.create('thing', { myUint16: 1.5 }), 'uint16 fail float') + await throws(() => db.create('thing', { myInt32: 1.5 }), 'int32 fail float') + await throws(() => db.create('thing', { myUint32: 1.5 }), 'uint32 fail float') +}) diff --git a/test/modify/validation/json.ts b/test/modify/validation/json.ts new file mode 100644 index 0000000000..acda8e2be8 --- /dev/null +++ b/test/modify/validation/json.ts @@ -0,0 +1,29 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - json', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myJson: { type: 'json' }, + }, + }, + }) + + // Json Valid Values + await db.create('thing', { myJson: { a: 1 } }) + await db.create('thing', { myJson: [1, 2] }) + await db.create('thing', { myJson: 'string' }) + await db.create('thing', { myJson: 123 }) + await db.create('thing', { myJson: true }) + await db.create('thing', { myJson: null }) + + // Json Invalid structure (Circular) + const circular: any = { a: 1 } + circular.b = circular + await throws( + () => db.create('thing', { myJson: circular }), + 'json should fail with circular structure', + ) +}) diff --git a/test/modify/validation/number.ts b/test/modify/validation/number.ts new file mode 100644 index 0000000000..07db6d1742 --- /dev/null +++ b/test/modify/validation/number.ts @@ -0,0 +1,55 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - number', async (t) => { + const db = await testDb(t, { + types: { + thing: { + score: { type: 'number', min: 10, max: 20 }, + myNumber: { type: 'number' }, + }, + }, + }) + + // Number + await throws( + // @ts-expect-error + () => db.create('thing', { score: '123' }), + 'number should fail with string', + ) + await throws( + () => db.create('thing', { score: 9 }), + 'number should fail if too small', + ) + await throws( + () => db.create('thing', { score: 21 }), + 'number should fail if too large', + ) + await db.create('thing', { score: 15 }) + + // Extended validation + await throws( + () => db.create('thing', { score: NaN }), + 'number should fail with NaN', + ) + await throws( + () => db.create('thing', { score: Infinity }), + 'number should fail with Infinity', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { score: true }), + 'number should fail with boolean', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { score: [] }), + 'number should fail with array', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { score: {} }), + 'number should fail with object', + ) +}) diff --git a/test/modify/validation/string.ts b/test/modify/validation/string.ts new file mode 100644 index 0000000000..327976dc45 --- /dev/null +++ b/test/modify/validation/string.ts @@ -0,0 +1,47 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - string', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: { type: 'string', min: 2, max: 5 }, + myString: { type: 'string' }, + }, + }, + }) + + // String + await throws( + // @ts-expect-error + () => db.create('thing', { name: 123 }), + 'string should fail with number', + ) + await throws( + () => db.create('thing', { name: 'a' }), + 'string should fail if too short', + ) + await throws( + () => db.create('thing', { name: 'aaaaaa' }), + 'string should fail if too long', + ) + await db.create('thing', { name: 'abc' }) + + // Extended validation + await throws( + // @ts-expect-error + () => db.create('thing', { name: ['a'] }), + 'string should fail with array', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { name: {} }), + 'string should fail with object', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { name: true }), + 'string should fail with boolean', + ) +}) diff --git a/test/modify/validation/text.ts b/test/modify/validation/text.ts new file mode 100644 index 0000000000..986ff178d1 --- /dev/null +++ b/test/modify/validation/text.ts @@ -0,0 +1,56 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - text', async (t) => { + const db = await testDb(t, { + locales: { + en: true, + de: true, + }, + types: { + thing: { + myText: 'text', + }, + }, + }) + + // Text + await throws( + // @ts-expect-error + () => db.create('thing', { myText: 123 }), + 'text should fail with number', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myText: { en: 123 } }), + 'text value should fail with number', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myText: { xx: 'hello' } }), + 'text should fail with invalid locale', + ) + await db.create('thing', { myText: { en: 'works' } }) + + // Extended validation + await throws( + () => db.create('thing', { myText: 'hello' }), + 'text should fail if passed as string not object', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myText: { en: {} } }), + 'text value should fail with object', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myText: { en: [] } }), + 'text value should fail with array', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myText: [] }), + 'text should fail with array', + ) +}) diff --git a/test/modify/validation/timestamp.ts b/test/modify/validation/timestamp.ts new file mode 100644 index 0000000000..7085a246c5 --- /dev/null +++ b/test/modify/validation/timestamp.ts @@ -0,0 +1,42 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - timestamp', async (t) => { + const db = await testDb(t, { + types: { + thing: { + myTs: { type: 'timestamp', min: 1000, max: 2000 }, + }, + }, + }) + + // Timestamp + await throws(() => db.create('thing', { myTs: 500 }), 'timestamp too small') + await throws(() => db.create('thing', { myTs: 3000 }), 'timestamp too large') + await db.create('thing', { myTs: 1500 }) + + // Extended validation + await throws( + () => db.create('thing', { myTs: '2022-01-01' }), + 'timestamp should fail with date string', + ) + await throws( + () => db.create('thing', { myTs: NaN }), + 'timestamp should fail with NaN', + ) + await throws( + () => db.create('thing', { myTs: Infinity }), + 'timestamp should fail with Infinity', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myTs: true }), + 'timestamp should fail with boolean', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { myTs: {} }), + 'timestamp should fail with object', + ) +}) From 90580d4fa59bb6c6b304f22a74fa3890ec846114 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 15:44:52 +0100 Subject: [PATCH 255/449] remove whtsp --- clibs/lib/selva/include/mempool.h | 1 - 1 file changed, 1 deletion(-) diff --git a/clibs/lib/selva/include/mempool.h b/clibs/lib/selva/include/mempool.h index 1f1c5340d3..48e9ff44bc 100644 --- a/clibs/lib/selva/include/mempool.h +++ b/clibs/lib/selva/include/mempool.h @@ -142,7 +142,6 @@ static inline struct mempool_chunk *get_first_chunk(struct mempool_slab * restri return (struct mempool_chunk *)p; } - /** * For each slab in the mempool. * The current slab will be available as the pointer variable `slab`. From bcc32df8469cb56625c21b822b1db6def20c28fb Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 16:51:56 +0100 Subject: [PATCH 256/449] Try init mempool while loading nodes --- clibs/include/selva/db.h | 1 + clibs/lib/selva/db.c | 66 +++++++++++++++++++++++++++++++ clibs/lib/selva/include/mempool.h | 2 +- clibs/lib/selva/io/dump.c | 56 +++++++++++++++++--------- clibs/lib/selva/mem/mempool.c | 13 ++++-- 5 files changed, 116 insertions(+), 22 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index fb4cca276a..030dd378c2 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -487,6 +487,7 @@ struct SelvaNodeRes selva_nfind_node(struct SelvaTypeEntry *type, node_id_t node */ SELVA_EXPORT struct SelvaNodeRes selva_upsert_node(struct SelvaTypeEntry *type, node_id_t node_id) __attribute__((nonnull)); +struct SelvaNodeRes selva_upsert_node2(struct SelvaTypeEntry *type, node_id_t node_id, struct SelvaNode *node) __attribute__((nonnull)); /** * **Example** diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 177854992b..fe6694ca9f 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -670,6 +670,72 @@ struct SelvaNodeRes selva_upsert_node(struct SelvaTypeEntry *type, node_id_t nod return res; } +struct SelvaNodeRes selva_upsert_node2(struct SelvaTypeEntry *type, node_id_t node_id, struct SelvaNode *node) +{ + if (unlikely(node_id == 0)) { + return (struct SelvaNodeRes){}; + } + + struct SelvaTypeBlocks *blocks = type->blocks; + struct SelvaNodeRes res = { + .block = selva_node_id2block_i(blocks, node_id), + }; + + struct SelvaTypeBlock *block = &blocks->blocks[res.block]; + res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_acquire); + constexpr enum SelvaTypeBlockStatus mask = SELVA_TYPE_BLOCK_STATUS_FS | SELVA_TYPE_BLOCK_STATUS_INMEM; + if ((res.block_status & mask) == SELVA_TYPE_BLOCK_STATUS_FS) { + /* + * Note that this is tricky because we want to normally bail if the block + * is only in fs to avoid creating two versions of the block (one in fs + * and one in mem. However, we must be able to upsert while loading the + * block. The trick is to set `SELVA_TYPE_BLOCK_STATUS_INMEM` before + * upsert if the caller is loading. + */ + goto out; + } + +#if 0 + struct SelvaNode *node = mempool_get(&type->nodepool); +#endif + + node->node_id = node_id; + node->type = type->type; + + if (type->max_node &&type->max_node->node_id < node_id && + selva_node_id2block_i2(type, type->max_node->node_id) == res.block) { + /* + * We can assume that node_id almost always grows monotonically. + */ + RB_INSERT_NEXT(SelvaNodeIndex, &block->nodes, type->max_node, node); + } else { + struct SelvaNode *prev; + + prev = RB_INSERT(SelvaNodeIndex, &block->nodes, node); + if (prev) { + mempool_return(&type->nodepool, node); + res.node = prev; + goto out; + } + } + + /* Don't set defaults if we are loading. */ + const bool set_defaults = !(res.block_status & SELVA_TYPE_BLOCK_STATUS_LOADING); + selva_fields_init_node(type, node, set_defaults); + + atomic_fetch_or_explicit(&block->status.atomic, (uint32_t)(SELVA_TYPE_BLOCK_STATUS_INMEM | SELVA_TYPE_BLOCK_STATUS_DIRTY), memory_order_release); + block->nr_nodes_in_block++; + type->nr_nodes++; + if (!type->max_node || type->max_node->node_id < node_id) { + type->max_node = node; + } + + res.node = node; +out: + res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_relaxed); + return res; +} + /** * Find the min node starting from block `start`. */ diff --git a/clibs/lib/selva/include/mempool.h b/clibs/lib/selva/include/mempool.h index 48e9ff44bc..c08f1349cd 100644 --- a/clibs/lib/selva/include/mempool.h +++ b/clibs/lib/selva/include/mempool.h @@ -105,7 +105,7 @@ void mempool_gc(struct mempool *mempool) */ void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, const void*)); -void mempool_prealloc(struct mempool *mempool, size_t nr_objects); +size_t mempool_alloc_slabs(struct mempool *mempool, size_t nr_objects, struct mempool_slab **slabs_out); /** * Get a new object from the pool. diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 6b80605a57..64e32ec9ac 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -715,7 +715,7 @@ static int load_node_fields(struct selva_io *io, struct SelvaDb *db, struct Selv } __attribute__((warn_unused_result)) -static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te) +static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te, struct SelvaNode *node_buf) { int err; @@ -727,7 +727,7 @@ static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct Selva node_id_t node_id; io->sdb_read(&node_id, sizeof(node_id), 1, io); - struct SelvaNodeRes res = selva_upsert_node(te, node_id); + struct SelvaNodeRes res = selva_upsert_node2(te, node_id, node_buf); if (!res.node) { return SELVA_ENOENT; } @@ -752,24 +752,44 @@ static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeE return 0; } -#if 0 - /* - * Prealloc slabs before loading. - * TODO Partials. - * This is not always optimal with partials because we may already have - * enough free objects. Perhaps only do this on startup. - * TODO This is also problematic for very large allocs. - */ - mempool_prealloc(&te->nodepool, nr_nodes); -#endif + struct mempool_slab *slabs; + size_t nr_slabs = mempool_alloc_slabs(&te->nodepool, nr_nodes, &slabs); + const struct mempool_slab_info slab_nfo = mempool_slab_info(&te->nodepool); + size_t node_i = 0; + int err = 0; - for (sdb_nr_nodes_t i = 0; i < nr_nodes; i++) { - node_id_t node_id; + for (size_t i = 0; i < nr_slabs; i++) { + struct mempool_slab *slab = (typeof(slabs))((uint8_t *)slabs + i * slab_nfo.slab_size); + /* TODO MEMPOOL_GROWING_FREE_LIST not working now */ +#ifdef MEMPOOL_GROWING_FREE_LIST + struct mempool_chunk *prev = nullptr; +#endif - node_id = load_node(io, db, te); - if (unlikely(node_id == 0)) { - return SELVA_EINVAL; - } + MEMPOOL_FOREACH_CHUNK_BEGIN(slab_nfo, slab) { + if (!err && node_i++ < nr_nodes) { + node_id_t node_id; + struct SelvaNode *node; + + chunk->slab = (uintptr_t)slab | 1; /* also marked as in use. */ + node = (struct SelvaNode *)mempool_get_obj(&te->nodepool, chunk); + node_id = load_node(io, db, te, node); + if (unlikely(node_id == 0)) { + err = SELVA_EINVAL; + } + } else { + chunk->slab = (uintptr_t)slab; /* also marked as free. */ +#ifdef MEMPOOL_GROWING_FREE_LIST + if (prev) { + LIST_INSERT_AFTER(prev, chunk, next_free); + } else { +#endif + LIST_INSERT_HEAD(&te->nodepool.free_chunks, chunk, next_free); +#ifdef MEMPOOL_GROWING_FREE_LIST + } + prev = chunk; +#endif + } + } MEMPOOL_FOREACH_CHUNK_END(); } return 0; diff --git a/clibs/lib/selva/mem/mempool.c b/clibs/lib/selva/mem/mempool.c index 153bb76b8b..aaaf1804c8 100644 --- a/clibs/lib/selva/mem/mempool.c +++ b/clibs/lib/selva/mem/mempool.c @@ -339,11 +339,11 @@ static void mempool_new_slab(struct mempool *mempool) add_new_slab2freelist(mempool, slab); } -void mempool_prealloc(struct mempool *mempool, size_t nr_objects) +size_t mempool_alloc_slabs(struct mempool *mempool, size_t nr_objects, struct mempool_slab **slabs_out) { struct mempool_slab_info nfo = mempool_slab_info(mempool); const size_t nr_slabs = (nr_objects + nfo.nr_objects - 1) / nfo.nr_objects; - const size_t slab_size = nr_slabs * mempool->slab_size_kb * 1024; + const size_t slab_size = mempool->slab_size_kb * 1024; const size_t bsize = nr_slabs * slab_size; #if !defined(__linux__) constexpr @@ -398,9 +398,16 @@ void mempool_prealloc(struct mempool *mempool, size_t nr_objects) } #endif + const struct mempool_slab_info info = mempool_slab_info(mempool); for (size_t i = 0; i < nr_slabs; i++) { - add_new_slab2freelist(mempool, (typeof(slabs))((uint8_t *)slabs + i * slab_size)); + struct mempool_slab *slab = (typeof(slabs))((uint8_t *)slabs + i * slab_size); + + slab->nr_free = info.nr_objects; + SLIST_INSERT_HEAD(&mempool->slabs, slab, next_slab); } + + *slabs_out = slabs; + return nr_slabs; } void *mempool_get(struct mempool *mempool) From a02973738181bd6f19b0fb5dbb6e40ff8a653684 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 17:06:49 +0100 Subject: [PATCH 257/449] Revert "Try init mempool while loading nodes" This wasn't any faster. This reverts commit 17b17f0bb4170365cbae9b32a7cba2d8c1f354a0. - loading 10M nodes: - original: 1.5313329999989946 ms - prealloc: 1.5921659999949043 ms - unitintialized pool slabs: 1.6173750000016298 ms --- clibs/include/selva/db.h | 1 - clibs/lib/selva/db.c | 66 ------------------------------- clibs/lib/selva/include/mempool.h | 2 +- clibs/lib/selva/io/dump.c | 56 +++++++++----------------- clibs/lib/selva/mem/mempool.c | 13 ++---- 5 files changed, 22 insertions(+), 116 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 030dd378c2..fb4cca276a 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -487,7 +487,6 @@ struct SelvaNodeRes selva_nfind_node(struct SelvaTypeEntry *type, node_id_t node */ SELVA_EXPORT struct SelvaNodeRes selva_upsert_node(struct SelvaTypeEntry *type, node_id_t node_id) __attribute__((nonnull)); -struct SelvaNodeRes selva_upsert_node2(struct SelvaTypeEntry *type, node_id_t node_id, struct SelvaNode *node) __attribute__((nonnull)); /** * **Example** diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index fe6694ca9f..177854992b 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -670,72 +670,6 @@ struct SelvaNodeRes selva_upsert_node(struct SelvaTypeEntry *type, node_id_t nod return res; } -struct SelvaNodeRes selva_upsert_node2(struct SelvaTypeEntry *type, node_id_t node_id, struct SelvaNode *node) -{ - if (unlikely(node_id == 0)) { - return (struct SelvaNodeRes){}; - } - - struct SelvaTypeBlocks *blocks = type->blocks; - struct SelvaNodeRes res = { - .block = selva_node_id2block_i(blocks, node_id), - }; - - struct SelvaTypeBlock *block = &blocks->blocks[res.block]; - res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_acquire); - constexpr enum SelvaTypeBlockStatus mask = SELVA_TYPE_BLOCK_STATUS_FS | SELVA_TYPE_BLOCK_STATUS_INMEM; - if ((res.block_status & mask) == SELVA_TYPE_BLOCK_STATUS_FS) { - /* - * Note that this is tricky because we want to normally bail if the block - * is only in fs to avoid creating two versions of the block (one in fs - * and one in mem. However, we must be able to upsert while loading the - * block. The trick is to set `SELVA_TYPE_BLOCK_STATUS_INMEM` before - * upsert if the caller is loading. - */ - goto out; - } - -#if 0 - struct SelvaNode *node = mempool_get(&type->nodepool); -#endif - - node->node_id = node_id; - node->type = type->type; - - if (type->max_node &&type->max_node->node_id < node_id && - selva_node_id2block_i2(type, type->max_node->node_id) == res.block) { - /* - * We can assume that node_id almost always grows monotonically. - */ - RB_INSERT_NEXT(SelvaNodeIndex, &block->nodes, type->max_node, node); - } else { - struct SelvaNode *prev; - - prev = RB_INSERT(SelvaNodeIndex, &block->nodes, node); - if (prev) { - mempool_return(&type->nodepool, node); - res.node = prev; - goto out; - } - } - - /* Don't set defaults if we are loading. */ - const bool set_defaults = !(res.block_status & SELVA_TYPE_BLOCK_STATUS_LOADING); - selva_fields_init_node(type, node, set_defaults); - - atomic_fetch_or_explicit(&block->status.atomic, (uint32_t)(SELVA_TYPE_BLOCK_STATUS_INMEM | SELVA_TYPE_BLOCK_STATUS_DIRTY), memory_order_release); - block->nr_nodes_in_block++; - type->nr_nodes++; - if (!type->max_node || type->max_node->node_id < node_id) { - type->max_node = node; - } - - res.node = node; -out: - res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_relaxed); - return res; -} - /** * Find the min node starting from block `start`. */ diff --git a/clibs/lib/selva/include/mempool.h b/clibs/lib/selva/include/mempool.h index c08f1349cd..48e9ff44bc 100644 --- a/clibs/lib/selva/include/mempool.h +++ b/clibs/lib/selva/include/mempool.h @@ -105,7 +105,7 @@ void mempool_gc(struct mempool *mempool) */ void mempool_defrag(struct mempool *mempool, int (*obj_compar)(const void *, const void*)); -size_t mempool_alloc_slabs(struct mempool *mempool, size_t nr_objects, struct mempool_slab **slabs_out); +void mempool_prealloc(struct mempool *mempool, size_t nr_objects); /** * Get a new object from the pool. diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 64e32ec9ac..6b80605a57 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -715,7 +715,7 @@ static int load_node_fields(struct selva_io *io, struct SelvaDb *db, struct Selv } __attribute__((warn_unused_result)) -static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te, struct SelvaNode *node_buf) +static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te) { int err; @@ -727,7 +727,7 @@ static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct Selva node_id_t node_id; io->sdb_read(&node_id, sizeof(node_id), 1, io); - struct SelvaNodeRes res = selva_upsert_node2(te, node_id, node_buf); + struct SelvaNodeRes res = selva_upsert_node(te, node_id); if (!res.node) { return SELVA_ENOENT; } @@ -752,44 +752,24 @@ static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeE return 0; } - struct mempool_slab *slabs; - size_t nr_slabs = mempool_alloc_slabs(&te->nodepool, nr_nodes, &slabs); - const struct mempool_slab_info slab_nfo = mempool_slab_info(&te->nodepool); - size_t node_i = 0; - int err = 0; - - for (size_t i = 0; i < nr_slabs; i++) { - struct mempool_slab *slab = (typeof(slabs))((uint8_t *)slabs + i * slab_nfo.slab_size); - /* TODO MEMPOOL_GROWING_FREE_LIST not working now */ -#ifdef MEMPOOL_GROWING_FREE_LIST - struct mempool_chunk *prev = nullptr; +#if 0 + /* + * Prealloc slabs before loading. + * TODO Partials. + * This is not always optimal with partials because we may already have + * enough free objects. Perhaps only do this on startup. + * TODO This is also problematic for very large allocs. + */ + mempool_prealloc(&te->nodepool, nr_nodes); #endif - MEMPOOL_FOREACH_CHUNK_BEGIN(slab_nfo, slab) { - if (!err && node_i++ < nr_nodes) { - node_id_t node_id; - struct SelvaNode *node; - - chunk->slab = (uintptr_t)slab | 1; /* also marked as in use. */ - node = (struct SelvaNode *)mempool_get_obj(&te->nodepool, chunk); - node_id = load_node(io, db, te, node); - if (unlikely(node_id == 0)) { - err = SELVA_EINVAL; - } - } else { - chunk->slab = (uintptr_t)slab; /* also marked as free. */ -#ifdef MEMPOOL_GROWING_FREE_LIST - if (prev) { - LIST_INSERT_AFTER(prev, chunk, next_free); - } else { -#endif - LIST_INSERT_HEAD(&te->nodepool.free_chunks, chunk, next_free); -#ifdef MEMPOOL_GROWING_FREE_LIST - } - prev = chunk; -#endif - } - } MEMPOOL_FOREACH_CHUNK_END(); + for (sdb_nr_nodes_t i = 0; i < nr_nodes; i++) { + node_id_t node_id; + + node_id = load_node(io, db, te); + if (unlikely(node_id == 0)) { + return SELVA_EINVAL; + } } return 0; diff --git a/clibs/lib/selva/mem/mempool.c b/clibs/lib/selva/mem/mempool.c index aaaf1804c8..153bb76b8b 100644 --- a/clibs/lib/selva/mem/mempool.c +++ b/clibs/lib/selva/mem/mempool.c @@ -339,11 +339,11 @@ static void mempool_new_slab(struct mempool *mempool) add_new_slab2freelist(mempool, slab); } -size_t mempool_alloc_slabs(struct mempool *mempool, size_t nr_objects, struct mempool_slab **slabs_out) +void mempool_prealloc(struct mempool *mempool, size_t nr_objects) { struct mempool_slab_info nfo = mempool_slab_info(mempool); const size_t nr_slabs = (nr_objects + nfo.nr_objects - 1) / nfo.nr_objects; - const size_t slab_size = mempool->slab_size_kb * 1024; + const size_t slab_size = nr_slabs * mempool->slab_size_kb * 1024; const size_t bsize = nr_slabs * slab_size; #if !defined(__linux__) constexpr @@ -398,16 +398,9 @@ size_t mempool_alloc_slabs(struct mempool *mempool, size_t nr_objects, struct me } #endif - const struct mempool_slab_info info = mempool_slab_info(mempool); for (size_t i = 0; i < nr_slabs; i++) { - struct mempool_slab *slab = (typeof(slabs))((uint8_t *)slabs + i * slab_size); - - slab->nr_free = info.nr_objects; - SLIST_INSERT_HEAD(&mempool->slabs, slab, next_slab); + add_new_slab2freelist(mempool, (typeof(slabs))((uint8_t *)slabs + i * slab_size)); } - - *slabs_out = slabs; - return nr_slabs; } void *mempool_get(struct mempool *mempool) From 2f881e5c80b411b2be6a444b75aa45fd311ca366 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 17:12:22 +0100 Subject: [PATCH 258/449] Prealloc nodepool only makes partials harder --- clibs/lib/selva/io/dump.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 6b80605a57..2b14f68e75 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -752,17 +752,6 @@ static int load_nodes(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeE return 0; } -#if 0 - /* - * Prealloc slabs before loading. - * TODO Partials. - * This is not always optimal with partials because we may already have - * enough free objects. Perhaps only do this on startup. - * TODO This is also problematic for very large allocs. - */ - mempool_prealloc(&te->nodepool, nr_nodes); -#endif - for (sdb_nr_nodes_t i = 0; i < nr_nodes; i++) { node_id_t node_id; From 8f83638b51c4a739bf308a6d707cda3530c02540 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 13 Feb 2026 17:13:07 +0100 Subject: [PATCH 259/449] Add more type testing --- test/types.perf.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/types.perf.ts b/test/types.perf.ts index 2d2c830abb..7efe1ff61f 100644 --- a/test/types.perf.ts +++ b/test/types.perf.ts @@ -35,3 +35,29 @@ await test('create and access many types', async (t) => { await db.drain() }) + +await test('create many nodes', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => t.backup(db)) + + const client = await db.setSchema({ + types: { + type: { bool: 'boolean' }, + }, + }) + + await perf( + () => { + client.create('type', { + bool: true, + }) + }, + 'create booleans', + { repeat: 10_000_000 }, + ) + + await db.drain() +}) From d5ed08ac2e5fd7ccd2692c902bb95bbeb05f70fd Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 17:50:06 +0100 Subject: [PATCH 260/449] update typeDef and add required --- src/db-client/modify/props.ts | 30 +++++---- src/db-query/ast/filter/condition.ts | 1 - src/db-query/ast/filter/filter.ts | 2 +- src/db-query/ast/include.ts | 6 +- src/schema/defs/getTypeDefs.ts | 18 +++--- src/schema/defs/index.ts | 5 +- src/schema/defs/props/fixed.ts | 63 +++++++++++++++---- test/modify/validation/binary.ts | 7 +++ test/modify/validation/enum.ts | 26 +++++++- test/modify/validation/integers.ts | 55 ++++++++++------- test/modify/validation/number.ts | 12 ++++ test/modify/validation/required.ts | 92 ++++++++++++++++++++++++++++ test/modify/validation/string.ts | 12 ++++ test/modify/validation/timestamp.ts | 12 ++++ 14 files changed, 280 insertions(+), 61 deletions(-) create mode 100644 test/modify/validation/required.ts diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index fc72e7b4cb..24e9c74b1e 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -1,4 +1,8 @@ -import type { PropDef, PropTree } from '../../schema/defs/index.js' +import { + isPropDef, + type PropDef, + type PropTree, +} from '../../schema/defs/index.js' import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { ModifyIncrement, @@ -17,22 +21,12 @@ export const serializeProps = ( lang: LangCodeEnum, ) => { for (const key in data) { - const def = tree.get(key) + const def = tree.props.get(key) if (def === undefined) { continue } const val = data[key] - if (def.constructor === Map) { - if (typeof val === 'object') { - if (val === null) { - const empty = {} - for (const [key] of def) empty[key] = null - serializeProps(def, empty, buf, op, lang) - } else { - serializeProps(def, val, buf, op, lang) - } - } - } else { + if (isPropDef(def)) { const prop = def as PropDef if (prop.id === 0) { // main @@ -62,6 +56,16 @@ export const serializeProps = ( writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) } } + } else { + if (typeof val === 'object') { + if (val === null) { + const empty = {} + for (const [key] of def.props) empty[key] = null + serializeProps(def, empty, buf, op, lang) + } else { + serializeProps(def, val, buf, op, lang) + } + } } } } diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts index cd6d68f4d2..c155722830 100644 --- a/src/db-query/ast/filter/condition.ts +++ b/src/db-query/ast/filter/condition.ts @@ -71,7 +71,6 @@ const getFilterOp = ( } let write = (buf: Uint8Array, val: any, offset: number) => { - console.log('write', val) prop.write(buf, val, offset) } diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index 277c16df3c..c5a4bc789e 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -30,7 +30,7 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { const { tree, main } = walkCtx for (const field in ast.props) { - const prop = tree.get(field) + const prop = tree.props.get(field) const astProp = ast.props[field] const ops = astProp.ops diff --git a/src/db-query/ast/include.ts b/src/db-query/ast/include.ts index 7bd53f1f60..fc76d55939 100644 --- a/src/db-query/ast/include.ts +++ b/src/db-query/ast/include.ts @@ -76,7 +76,7 @@ const walkProp = ( field: string, ) => { const { main, tree } = walkCtx - const prop = tree.get(field) + const prop = tree.props.get(field) const include = astProp.include if (isPropDef(prop)) { @@ -117,13 +117,13 @@ const walk = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { continue } if (field === '*') { - for (const [field, prop] of walkCtx.tree) { + for (const [field, prop] of walkCtx.tree.props) { if (!('ref' in prop)) { walkProp(astProp, ctx, typeDef, walkCtx, field) } } } else if (field === '**') { - for (const [field, prop] of typeDef.tree) { + for (const [field, prop] of typeDef.tree.props) { if ('ref' in prop) { walkProp(astProp, ctx, typeDef, walkCtx, field) } diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index decf49ceed..97f5fa9d7b 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -78,7 +78,7 @@ const getTypeDef = ( separate: [], props: new Map(), main: [], - tree: new Map(), + tree: { props: new Map(), required: [] }, schema, schemaRoot, } @@ -87,21 +87,25 @@ const getTypeDef = ( props: SchemaProps, pPath: string[], tree: TypeDef['tree'], - ): void => { + ): boolean | undefined => { for (const key in props) { const prop = props[key] const path = [...pPath, key] - + let required = prop.required if (prop.type === 'object') { - const branch = new Map() - walk(prop.props, path, branch) - tree.set(key, branch) + const branch = { props: new Map(), required: [] } + if (walk(prop.props, path, branch)) required = true + tree.props.set(key, branch) } else { const def = addPropDef(prop, path, typeDef) typeDef.props.set(path.join('.'), def) - tree.set(key, def) + tree.props.set(key, def) + } + if (required) { + tree.required.push(key) } } + return !!tree.required.length } walk(props, [], typeDef.tree) diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index effdc71b90..fc94f771c5 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -13,7 +13,10 @@ import * as cardinality from './props/cardinality.js' import * as strings from './props/strings.js' import * as vector from './props/vector.js' -export type PropTree = Map +export type PropTree = { + props: Map + required: string[] +} export type TypeDef = { id: number diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index c003543524..12ada4d095 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -79,10 +79,18 @@ class integer extends number { } export const uint8 = class Uint8 extends integer { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min === undefined) this.min = 0 + else if (schema.min < 0) this.min = 0 + if (schema.max === undefined) this.max = 255 + else if (schema.max > 255) this.max = 255 + } override type: PropTypeEnum = PropType.uint8 override size = 1 - override min = 0 - override max = 255 + override validate(value: unknown): asserts value is number { + super.validate(value) + } override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -93,16 +101,29 @@ export const uint8 = class Uint8 extends integer { } export const int8 = class Int8 extends uint8 { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min === undefined) this.min = -128 + else if (schema.min < -128) this.min = -128 + if (schema.max === undefined) this.max = 127 + else if (schema.max > 127) this.max = 127 + } override type = PropType.int8 - override min = -128 - override max = 127 } export const uint16 = class Uint16 extends integer { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min === undefined) this.min = 0 + else if (schema.min < 0) this.min = 0 + if (schema.max === undefined) this.max = 65535 + else if (schema.max > 65535) this.max = 65535 + } override type: PropTypeEnum = PropType.uint16 override size = 2 - override min = 0 - override max = 65535 + override validate(value: unknown): asserts value is number { + super.validate(value) + } override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -113,16 +134,29 @@ export const uint16 = class Uint16 extends integer { } export const int16 = class Int16 extends uint16 { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min === undefined) this.min = -32768 + else if (schema.min < -32768) this.min = -32768 + if (schema.max === undefined) this.max = 32767 + else if (schema.max > 32767) this.max = 32767 + } override type = PropType.int16 - override min = -32768 - override max = 32767 } export const uint32 = class Uint32 extends integer { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min === undefined) this.min = 0 + else if (schema.min < 0) this.min = 0 + if (schema.max === undefined) this.max = 4294967295 + else if (schema.max > 4294967295) this.max = 4294967295 + } override type: PropTypeEnum = PropType.uint32 override size = 4 - override min = 0 - override max = 4294967295 + override validate(value: unknown): asserts value is number { + super.validate(value) + } override pushValue( buf: AutoSizedUint8Array, value: unknown, @@ -133,9 +167,14 @@ export const uint32 = class Uint32 extends integer { } export const int32 = class Int32 extends uint32 { + constructor(schema: SchemaNumber, path: string[], typeDef: TypeDef) { + super(schema, path, typeDef) + if (schema.min === undefined) this.min = -2147483648 + else if (schema.min < -2147483648) this.min = -2147483648 + if (schema.max === undefined) this.max = 2147483647 + else if (schema.max > 2147483647) this.max = 2147483647 + } override type = PropType.int32 - override min = -2147483648 - override max = 2147483647 } export const enum_ = class Enum extends BasePropDef { diff --git a/test/modify/validation/binary.ts b/test/modify/validation/binary.ts index 6ce4e9571d..7a5e503a23 100644 --- a/test/modify/validation/binary.ts +++ b/test/modify/validation/binary.ts @@ -23,6 +23,13 @@ await test('modify - validation - binary', async (t) => { ) await db.create('thing', { myBlob: new Uint8Array(5) }) + // MaxBytes validation (configured maxBytes: 10) + await throws( + () => db.create('thing', { myBlob: new Uint8Array(11) }), + 'binary fail with maxBytes exceeded', + ) + await db.create('thing', { myBlob: new Uint8Array(10) }) + // Extended validation await throws( // @ts-expect-error diff --git a/test/modify/validation/enum.ts b/test/modify/validation/enum.ts index 3208dce832..5d7d536e7a 100644 --- a/test/modify/validation/enum.ts +++ b/test/modify/validation/enum.ts @@ -7,11 +7,13 @@ await test('modify - validation - enum', async (t) => { types: { thing: { myEnum: { enum: ['a', 'b'] }, + numEnum: { enum: [1, 2, 3] }, + mixedEnum: { enum: ['a', 1] }, }, }, }) - // Enum + // Enum (string) await throws( // @ts-expect-error () => db.create('thing', { myEnum: 'c' }), @@ -19,6 +21,28 @@ await test('modify - validation - enum', async (t) => { ) await db.create('thing', { myEnum: 'b' }) + // Enum (number) + await throws( + // @ts-expect-error + () => db.create('thing', { numEnum: 4 }), + 'numEnum should fail with invalid value', + ) + await db.create('thing', { numEnum: 2 }) + + // Enum (mixed) + await throws( + // @ts-expect-error + () => db.create('thing', { mixedEnum: 'b' }), + 'mixedEnum should fail with invalid string value', + ) + await throws( + // @ts-expect-error + () => db.create('thing', { mixedEnum: 2 }), + 'mixedEnum should fail with invalid number value', + ) + await db.create('thing', { mixedEnum: 'a' }) + await db.create('thing', { mixedEnum: 1 }) + // Extended validation await throws( // @ts-expect-error diff --git a/test/modify/validation/integers.ts b/test/modify/validation/integers.ts index d52824bd80..ff169bf1a0 100644 --- a/test/modify/validation/integers.ts +++ b/test/modify/validation/integers.ts @@ -6,12 +6,12 @@ await test('modify - validation - integers', async (t) => { const db = await testDb(t, { types: { thing: { - myInt8: { type: 'int8' }, // -128 to 127 - myUint8: { type: 'uint8' }, // 0 to 255 - myInt16: { type: 'int16' }, // -32768 to 32767 - myUint16: { type: 'uint16' }, // 0 to 65535 - myInt32: { type: 'int32' }, - myUint32: { type: 'uint32' }, + myInt8: { type: 'int8', min: -10, max: 10 }, + myUint8: { type: 'uint8', min: 10, max: 20 }, + myInt16: { type: 'int16', min: 1000, max: 2000 }, + myUint16: { type: 'uint16', min: 1000, max: 2000 }, + myInt32: { type: 'int32', min: 100000, max: 200000 }, + myUint32: { type: 'uint32', min: 100000, max: 200000 }, }, }, }) @@ -25,35 +25,46 @@ await test('modify - validation - integers', async (t) => { await throws(() => db.create('thing', { myInt8: 1.5 }), 'int8 fail float') await throws(() => db.create('thing', { myInt8: 128 }), 'int8 overflow') await throws(() => db.create('thing', { myInt8: -129 }), 'int8 underflow') - await db.create('thing', { myInt8: 127 }) + await db.create('thing', { myInt8: 10 }) + // This check fails because -11 is < -10, so it should throw + await throws(() => db.create('thing', { myInt8: -11 }), 'int8 below min') + await throws(() => db.create('thing', { myInt8: 11 }), 'int8 above max') // Uint8 (0 to 255) await throws(() => db.create('thing', { myUint8: -1 }), 'uint8 negative') - await throws(() => db.create('thing', { myUint8: 256 }), 'uint8 overflow') - await db.create('thing', { myUint8: 255 }) + // Uint8 (min 10 max 20) + await db.create('thing', { myUint8: 10 }) + await throws(() => db.create('thing', { myUint8: 9 }), 'uint8 below min') + await throws(() => db.create('thing', { myUint8: 21 }), 'uint8 above max') // Int16 (-32768 to 32767) - await throws(() => db.create('thing', { myInt16: 32768 }), 'int16 overflow') - await throws(() => db.create('thing', { myInt16: -32769 }), 'int16 underflow') - await db.create('thing', { myInt16: 32767 }) + // Int16 (min 1000 max 2000) + await db.create('thing', { myInt16: 1000 }) + await throws(() => db.create('thing', { myInt16: 999 }), 'int16 below min') + await throws(() => db.create('thing', { myInt16: 2001 }), 'int16 above max') // Uint16 (0 to 65535) - await throws(() => db.create('thing', { myUint16: 65536 }), 'uint16 overflow') - await db.create('thing', { myUint16: 65535 }) + // Uint16 (min 1000 max 2000) + await db.create('thing', { myUint16: 1000 }) + await throws(() => db.create('thing', { myUint16: 999 }), 'uint16 below min') + await throws(() => db.create('thing', { myUint16: 2001 }), 'uint16 above max') // Int32 + // Int32 (min 100000 max 200000) + await db.create('thing', { myInt32: 100000 }) + await throws(() => db.create('thing', { myInt32: 99999 }), 'int32 below min') + await throws(() => db.create('thing', { myInt32: 200001 }), 'int32 above max') + + // Uint32 (min 100000 max 200000) + await db.create('thing', { myUint32: 100000 }) await throws( - () => db.create('thing', { myInt32: 2147483648 }), - 'int32 overflow', + () => db.create('thing', { myUint32: 99999 }), + 'uint32 below min', ) - await db.create('thing', { myInt32: 2147483647 }) - - // Uint32 await throws( - () => db.create('thing', { myUint32: 4294967296 }), - 'uint32 overflow', + () => db.create('thing', { myUint32: 200001 }), + 'uint32 above max', ) - await db.create('thing', { myUint32: 4294967295 }) // Extended validation (General invalid types for integers) await throws(() => db.create('thing', { myInt8: NaN }), 'int8 fail NaN') diff --git a/test/modify/validation/number.ts b/test/modify/validation/number.ts index 07db6d1742..27681adcc7 100644 --- a/test/modify/validation/number.ts +++ b/test/modify/validation/number.ts @@ -28,6 +28,18 @@ await test('modify - validation - number', async (t) => { ) await db.create('thing', { score: 15 }) + // Range validation + await throws( + () => db.create('thing', { score: 9 }), + 'number should fail if below min (10)', + ) + await throws( + () => db.create('thing', { score: 21 }), + 'number should fail if above max (20)', + ) + await db.create('thing', { score: 10 }) + await db.create('thing', { score: 20 }) + // Extended validation await throws( () => db.create('thing', { score: NaN }), diff --git a/test/modify/validation/required.ts b/test/modify/validation/required.ts new file mode 100644 index 0000000000..d9f9c7d731 --- /dev/null +++ b/test/modify/validation/required.ts @@ -0,0 +1,92 @@ +import { throws } from '../../shared/assert.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - validation - required', async (t) => { + const db = await testDb(t, { + types: { + thing: { + name: { type: 'string', required: true }, + description: { type: 'string' }, + nested: { + type: 'object', + required: true, + props: { + reqInNested: { type: 'string', required: true }, + }, + }, + optionalNested: { + type: 'object', + props: { + reqInOptional: { type: 'string', required: true }, + }, + }, + }, + }, + }) + // 1. Top-level required + await throws( + () => + // @ts-expect-error + db.create('thing', { + description: 'stuff', + nested: { reqInNested: 'yes' }, + }), + 'fail missing required top-level', + ) + + // 2. Required object itself missing + await throws( + () => + // @ts-expect-error + db.create('thing', { + name: 'cool', + // nested is missing + }), + 'fail missing required object', + ) + + // 3. Required nested field missing (in required object) + await throws( + () => + db.create('thing', { + name: 'cool', + // @ts-expect-error + nested: {}, // reqInNested missing + }), + 'fail missing required nested field', + ) + + // 4. Required nested field missing (in optional object, if object is provided) + await throws( + () => + db.create('thing', { + name: 'cool', + nested: { reqInNested: 'yes' }, + // @ts-expect-error + optionalNested: {}, // reqInOptional missing + }), + 'fail missing required nested field in optional object', + ) + + // 5. Success cases + const id1 = await db.create('thing', { + name: 'cool', + nested: { reqInNested: 'yes' }, + // optionalNested is optional, so this is valid + }) + + const id2 = await db.create('thing', { + name: 'cool', + nested: { reqInNested: 'yes' }, + optionalNested: { reqInOptional: 'also yes' }, + }) + + // 6. Update should not trigger required validation + // @ts-ignore + await db.update('thing', id1, { description: 'updated' }) + + // Update nested field without others + // @ts-ignore + await db.update('thing', id2, { nested: { reqInNested: 'updated' } }) +}) diff --git a/test/modify/validation/string.ts b/test/modify/validation/string.ts index 327976dc45..dfe585f420 100644 --- a/test/modify/validation/string.ts +++ b/test/modify/validation/string.ts @@ -28,6 +28,18 @@ await test('modify - validation - string', async (t) => { ) await db.create('thing', { name: 'abc' }) + // Length validation (min: 2, max: 5) + await throws( + () => db.create('thing', { name: 'a' }), + 'string should fail if too short (1 char)', + ) + await throws( + () => db.create('thing', { name: 'aaaaaa' }), + 'string should fail if too long (6 chars)', + ) + await db.create('thing', { name: 'ab' }) + await db.create('thing', { name: 'abcde' }) + // Extended validation await throws( // @ts-expect-error diff --git a/test/modify/validation/timestamp.ts b/test/modify/validation/timestamp.ts index 7085a246c5..b98f2e4f93 100644 --- a/test/modify/validation/timestamp.ts +++ b/test/modify/validation/timestamp.ts @@ -16,6 +16,18 @@ await test('modify - validation - timestamp', async (t) => { await throws(() => db.create('thing', { myTs: 3000 }), 'timestamp too large') await db.create('thing', { myTs: 1500 }) + // Range validation (min: 1000, max: 2000) + await throws( + () => db.create('thing', { myTs: 999 }), + 'timestamp should fail below min', + ) + await throws( + () => db.create('thing', { myTs: 2001 }), + 'timestamp should fail above max', + ) + await db.create('thing', { myTs: 1000 }) + await db.create('thing', { myTs: 2000 }) + // Extended validation await throws( () => db.create('thing', { myTs: '2022-01-01' }), From 1ffec1e814715b2eb63cb4802f5cae68ebf89ef8 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 18:11:06 +0100 Subject: [PATCH 261/449] required validation --- src/db-client/modify/props.ts | 27 ++++++++++++++------- test/modify/validation/required.ts | 39 ++++++++++++++++++------------ 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index 24e9c74b1e..97b34ae2a8 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -5,6 +5,7 @@ import { } from '../../schema/defs/index.js' import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { + Modify, ModifyIncrement, pushModifyMainHeader, pushModifyPropHeader, @@ -20,6 +21,16 @@ export const serializeProps = ( op: ModifyEnum, lang: LangCodeEnum, ) => { + if (op !== Modify.update) { + for (const key of tree.required) { + if (!(key in data)) { + const def = tree.props.get(key)! + throw new Error( + `Field ${'path' in def ? def.path.join('.') : key} is required`, + ) + } + } + } for (const key in data) { const def = tree.props.get(key) if (def === undefined) { @@ -56,15 +67,13 @@ export const serializeProps = ( writeModifyPropHeaderProps.size(buf.data, buf.length - start, index) } } - } else { - if (typeof val === 'object') { - if (val === null) { - const empty = {} - for (const [key] of def.props) empty[key] = null - serializeProps(def, empty, buf, op, lang) - } else { - serializeProps(def, val, buf, op, lang) - } + } else if (typeof val === 'object') { + if (val === null) { + const empty = {} + for (const [key] of def.props) empty[key] = null + serializeProps(def, empty, buf, op, lang) + } else { + serializeProps(def, val, buf, op, lang) } } } diff --git a/test/modify/validation/required.ts b/test/modify/validation/required.ts index d9f9c7d731..41b098f038 100644 --- a/test/modify/validation/required.ts +++ b/test/modify/validation/required.ts @@ -25,40 +25,54 @@ await test('modify - validation - required', async (t) => { }, }) // 1. Top-level required - await throws( + const throwsMatch = async (fn: () => Promise, re: RegExp) => { + try { + await fn() + } catch (err: any) { + if (re.test(err.message)) return + throw new Error(`Error message "${err.message}" does not match ${re}`) + } + throw new Error('Function should have thrown') + } + + // 1. Top-level required + await throwsMatch( () => // @ts-expect-error db.create('thing', { description: 'stuff', nested: { reqInNested: 'yes' }, }), - 'fail missing required top-level', + /Field name is required/, ) // 2. Required object itself missing - await throws( + await throwsMatch( () => // @ts-expect-error db.create('thing', { name: 'cool', // nested is missing }), - 'fail missing required object', + /Field nested is required/, ) // 3. Required nested field missing (in required object) - await throws( + await throwsMatch( () => db.create('thing', { name: 'cool', // @ts-expect-error nested: {}, // reqInNested missing + optionalNested: { + reqInOptional: 'xx', + }, }), - 'fail missing required nested field', + /Field nested\.reqInNested is required/, ) // 4. Required nested field missing (in optional object, if object is provided) - await throws( + await throwsMatch( () => db.create('thing', { name: 'cool', @@ -66,17 +80,10 @@ await test('modify - validation - required', async (t) => { // @ts-expect-error optionalNested: {}, // reqInOptional missing }), - 'fail missing required nested field in optional object', + /Field optionalNested.reqInOptional is required/, ) - // 5. Success cases const id1 = await db.create('thing', { - name: 'cool', - nested: { reqInNested: 'yes' }, - // optionalNested is optional, so this is valid - }) - - const id2 = await db.create('thing', { name: 'cool', nested: { reqInNested: 'yes' }, optionalNested: { reqInOptional: 'also yes' }, @@ -88,5 +95,5 @@ await test('modify - validation - required', async (t) => { // Update nested field without others // @ts-ignore - await db.update('thing', id2, { nested: { reqInNested: 'updated' } }) + await db.update('thing', id1, { nested: { reqInNested: 'updated' } }) }) From f44f56a4ca46889907d0e92f551aa0f31d617f6a Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 18:27:50 +0100 Subject: [PATCH 262/449] add test for delete --- native/modify/modify.zig | 7 ++----- src/db-client/modify/delete.ts | 7 ++++++- test/modify/delete.ts | 28 ++++++++++++++++++++++++++++ test/modify/validation/alias.ts | 2 -- test/modify/validation/required.ts | 2 +- 5 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 test/modify/delete.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 44b3bc058e..1989517227 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -374,16 +374,13 @@ pub fn modify( const typeEntry = try Node.getType(db, delete.type); var id = delete.id; if (delete.isTmp) id = utils.read(u32, items, id * resItemSize); + utils.write(result, id, j); + utils.write(result, t.ModifyError.null, j + 4); if (Node.getNode(typeEntry, id)) |node| { Node.deleteNode(db, typeEntry, node) catch { // handle errors }; - utils.write(result, id, j); - utils.write(result, t.ModifyError.null, j + 4); selva.markDirty(db, delete.type, id); - } else { - utils.write(result, id, j); - utils.write(result, t.ModifyError.nx, j + 4); } }, } diff --git a/src/db-client/modify/delete.ts b/src/db-client/modify/delete.ts index 914aae86bc..2bb235dc26 100644 --- a/src/db-client/modify/delete.ts +++ b/src/db-client/modify/delete.ts @@ -20,5 +20,10 @@ export const serializeDelete = < op: Modify.delete, type: typeDef.id, }) - pushModifyDeleteHeader(buf, header) + pushModifyDeleteHeader(buf, { + op: Modify.delete, + isTmp: header.isTmp, + id: header.id, + type: typeDef.id, + }) } diff --git a/test/modify/delete.ts b/test/modify/delete.ts new file mode 100644 index 0000000000..7c7018f941 --- /dev/null +++ b/test/modify/delete.ts @@ -0,0 +1,28 @@ +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('delete', async (t) => { + const db = await testDb(t, { + types: { + user: { + name: 'string', + }, + }, + }) + + // 1. Create + const id = await db.create('user', { name: 'hello' }) + const res = await db.query2('user', id).get() + deepEqual(res?.name, 'hello') + + // 2. Delete + await db.delete('user', id) + + // 3. Verify + const res2 = await db.query2('user', id).get() + deepEqual(res2, null, 'Should be null after delete') + + // 4. Delete again (should not throw, maybe return false?) + await db.delete('user', id) +}) diff --git a/test/modify/validation/alias.ts b/test/modify/validation/alias.ts index 9a6910cbe5..2e6a37811f 100644 --- a/test/modify/validation/alias.ts +++ b/test/modify/validation/alias.ts @@ -17,10 +17,8 @@ await test('modify - validation - alias', async (t) => { () => db.create('thing', { myAlias: 123 }), 'alias fail with number', ) - const id1 = await db.create('thing', { myAlias: 'cool-alias' }) // Extended validation - // Checking validation, not collision (collision is a separate constraint) await throws( () => db.create('thing', { myAlias: '' }), 'alias fail with empty string', diff --git a/test/modify/validation/required.ts b/test/modify/validation/required.ts index 41b098f038..2a6c969eda 100644 --- a/test/modify/validation/required.ts +++ b/test/modify/validation/required.ts @@ -80,7 +80,7 @@ await test('modify - validation - required', async (t) => { // @ts-expect-error optionalNested: {}, // reqInOptional missing }), - /Field optionalNested.reqInOptional is required/, + /Field optionalNested\.reqInOptional is required/, ) const id1 = await db.create('thing', { From 902fcaebd5f78972a43a4dbfdc51bdea9b974ed5 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 18:31:43 +0100 Subject: [PATCH 263/449] wip --- test/modify/props/references.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/modify/props/references.ts b/test/modify/props/references.ts index e6c8c50f51..899dd26a6c 100644 --- a/test/modify/props/references.ts +++ b/test/modify/props/references.ts @@ -93,7 +93,7 @@ await test('modify references', async (t) => { const check = async (ids: number[], msg) => { const res = await db.query2('holder', h1).include('dests').get() - const currentIds = res.dests?.map((v: any) => v.id) || [] + const currentIds = res?.dests?.map((v: any) => v.id) || [] currentIds.sort() ids.sort() deepEqual(currentIds, ids, msg) @@ -166,7 +166,7 @@ await test('modify references no await', async (t) => { .include('dests.id') .get() - const currentIds = res.dests?.map((v: any) => v.id) || [] + const currentIds = res?.dests?.map((v: any) => v.id) || [] currentIds.sort() const expected = [id1, id3] expected.sort() @@ -211,8 +211,8 @@ await test('modify single reference on edge', async (t) => { .include('toThing.$edgeRef.id') .get() - return res.toThing && !Array.isArray(res.toThing) - ? res.toThing.$edgeRef + return res?.toThing && !Array.isArray(res?.toThing) + ? res?.toThing.$edgeRef : undefined } @@ -280,7 +280,7 @@ await test('modify references on edge', async (t) => { .include('toThing.$edgeRefs.id') .get() - const edge = res.toThing && !Array.isArray(res.toThing) ? res.toThing : {} + const edge = res?.toThing && !Array.isArray(res.toThing) ? res.toThing : {} const currentIds = edge.$edgeRefs?.map((v: any) => v.id) || [] currentIds.sort() ids.sort() From 5cd630d7f40304e7cc3de9e6e65f2dda33236369 Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 13 Feb 2026 18:38:47 +0100 Subject: [PATCH 264/449] edge refs --- src/db-client/query2/types.ts | 17 +++++++++++++++-- test/modify/props/references.ts | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 1de440ed4a..4f71ea0be9 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -220,6 +220,19 @@ export type Operator = type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +// Helper to generate paths from edges +export type EdgePaths = { + [K in keyof FilterEdges & string]: + | K + | (FilterEdges[K] extends { ref: infer R extends string } + ? `${K}.${Path | 'id' | '*' | '**'}` + : FilterEdges[K] extends { + items: { ref: infer R extends string } + } + ? `${K}.${Path | 'id' | '*' | '**'}` + : never) +}[keyof FilterEdges & string] + export type Path = [ Depth, ] extends [never] @@ -230,7 +243,7 @@ export type Path = [ | (ResolvedProps[K] extends { ref: infer R extends string } ? `${K}.${ | Path - | (keyof FilterEdges[K]> & string) + | EdgePaths[K], Prev[Depth]> | 'id' | '*' | '**'}` @@ -239,7 +252,7 @@ export type Path = [ } ? `${K}.${ | Path - | (keyof FilterEdges & string) + | EdgePaths | 'id' | '*' | '**'}` diff --git a/test/modify/props/references.ts b/test/modify/props/references.ts index 899dd26a6c..283081b4ea 100644 --- a/test/modify/props/references.ts +++ b/test/modify/props/references.ts @@ -251,7 +251,6 @@ await test('modify references on edge', async (t) => { type: 'references', items: { ref: 'thing', - // prop: 'edgeRefsHolders', }, }, }, @@ -280,7 +279,8 @@ await test('modify references on edge', async (t) => { .include('toThing.$edgeRefs.id') .get() - const edge = res?.toThing && !Array.isArray(res.toThing) ? res.toThing : {} + const edge: any = + res?.toThing && !Array.isArray(res.toThing) ? res.toThing : {} const currentIds = edge.$edgeRefs?.map((v: any) => v.id) || [] currentIds.sort() ids.sort() From 6096b2ffb3f437f85d33ec77e9991ca05b9fafef Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 14 Feb 2026 00:06:13 +0100 Subject: [PATCH 265/449] update types --- src/db-client/index.ts | 6 +- src/schema/index.ts | 15 +- src/schema/schema/payload.ts | 137 ++++++ src/schema/schema/schema.ts | 7 +- src/zigTsExports.ts | 744 ++++++++++++++------------------- test/modify/hooks/create.ts | 74 ++++ test/modify/hooks/type_test.ts | 94 +++++ test/shared/index.ts | 50 +-- 8 files changed, 643 insertions(+), 484 deletions(-) create mode 100644 src/schema/schema/payload.ts create mode 100644 test/modify/hooks/create.ts create mode 100644 test/modify/hooks/type_test.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 05a9908027..0dee500f04 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -12,6 +12,8 @@ import { type ResolveSchema, type Schema, type ResolvedProps, + type ValidateSchema, + type StrictSchema, } from '../schema/index.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' import { LangCode, Modify } from '../zigTsExports.js' @@ -78,10 +80,10 @@ export class DbClient extends DbShared { } async setSchema( - schema: T, + schema: StrictSchema, transformFns?: SchemaMigrateFns, ): Promise>> { - const strictSchema = parse(schema).schema + const strictSchema = parse(schema as any).schema await this.drain() const schemaChecksum = await this.hooks.setSchema( strictSchema as SchemaOut, diff --git a/src/schema/index.ts b/src/schema/index.ts index f98fd52034..20d4425bfc 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -13,7 +13,13 @@ import { BLOCK_CAPACITY_DEFAULT, } from './def/types.js' import { propIsNumerical } from './def/utils.js' -import { parseSchema, type SchemaIn, type SchemaOut } from './schema/schema.js' +import { + parseSchema, + type SchemaIn, + type SchemaOut, + type ValidateSchema, + type StrictSchema, +} from './schema/schema.js' export * from './schema/alias.js' export * from './schema/base.js' @@ -40,8 +46,11 @@ export * from './serialize.js' export * from './infer.js' export * as semver from './semver/mod.js' -export const parse = (schema: SchemaIn): { schema: SchemaOut } => ({ - schema: parseSchema(schema), +// eslint-disable-next-line +export const parse = ( + schema: StrictSchema, +): { schema: SchemaOut } => ({ + schema: parseSchema(schema as any) as unknown as SchemaOut, }) export const MAX_ID = 4294967295 export const MIN_ID = 1 diff --git a/src/schema/schema/payload.ts b/src/schema/schema/payload.ts new file mode 100644 index 0000000000..e2f2cb880a --- /dev/null +++ b/src/schema/schema/payload.ts @@ -0,0 +1,137 @@ +import type { SchemaOut } from './schema.js' + +// import type { BasedModify } from '../../db-client/modify/index.js' +type BasedModify = any // Mock BasedModify to avoid circular dependency + +type TypedArray = + | Uint8Array + | Float32Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + +type NumInc = number | { increment: number } + +type TypeMap = { + string: string + number: NumInc + int8: NumInc + uint8: NumInc + int16: NumInc + uint16: NumInc + int32: NumInc + uint32: NumInc + boolean: boolean + text: string | Record + json: any + timestamp: NumInc | string | Date + binary: Uint8Array + alias: string + vector: TypedArray + colvec: TypedArray + cardinality: string | string[] +} + +type EdgeKeys = keyof T extends infer K + ? K extends string + ? string extends K + ? never + : K extends `$${string}` + ? K + : never + : never + : never + +type InferEdgeProps< + Prop, + Types, + Locales extends Record = Record, +> = { + [K in EdgeKeys]?: Prop[K] extends keyof TypeMap + ? TypeMap[Prop[K]] + : InferProp +} + +type InferRefValue< + Prop, + Types, + Locales extends Record = Record, +> = + | number + | BasedModify + | (EdgeKeys extends never + ? { id: number | BasedModify } + : { id: number | BasedModify } & InferEdgeProps< + Prop, + Types, + Locales + >) + +type InferReferences< + Prop, + Types, + Locales extends Record = Record, +> = + | InferRefValue[] + | { + add?: Prettify>[] + update?: Prettify>[] + delete?: (number | BasedModify)[] + } + +type InferProp< + Prop, + Types, + Locales extends Record = Record, +> = Prop extends { type: 'text' } + ? string | Partial> + : Prop extends { type: 'object'; props: infer P } + ? InferType + : Prop extends { type: infer T extends keyof TypeMap } + ? TypeMap[T] + : Prop extends { enum: infer E extends readonly any[] } + ? E[number] + : Prop extends { ref: string } + ? Prettify> + : Prop extends { items: { ref: string } } + ? Prettify> + : Prop extends keyof TypeMap + ? TypeMap[Prop] + : never + +type Prettify = Target extends any + ? Target extends (infer U)[] + ? Prettify[] + : Target extends object + ? { + -readonly [K in keyof Target]: Target[K] + } + : Target + : never + +export type InferType< + Props, + Types, + Locales extends Record = Record, +> = { + [K in keyof Props as Props[K] extends { required: true } + ? K + : never]: InferProp +} & { + [K in keyof Props as Props[K] extends { required: true } + ? never + : K]?: InferProp | null +} + +export type InferPayload< + S extends SchemaOut, + T extends keyof S['types'], +> = InferType< + S['types'][T]['props'], + S['types'], + S['locales'] extends Record ? S['locales'] : {} +> diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index f8dcfd72b6..6e44763fc3 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -7,6 +7,7 @@ import { isString, type RequiredIfStrict, } from './shared.js' +import { type LangName, type SchemaLocale } from './locales.js' import { parseType, type SchemaType } from './type.js' import { inspect } from 'node:util' import { postParseRefs } from './reference.js' @@ -104,8 +105,6 @@ type GetBackRefs = UnionToIntersection< }[keyof Types] > -import type { SchemaProp } from './prop.js' - // ResolvedProps combines explicit props with inferred back-reference props export type ResolvedProps< Types, @@ -200,7 +199,7 @@ export type ValidateSchema = Omit & { } } -import { type LangName, type SchemaLocale } from './locales.js' +export type StrictSchema = S & ValidateSchema type Prettify = { [K in keyof T]: T[K] @@ -282,7 +281,7 @@ const track =

>(input: P): P => { This returns a "public" parsed schema, suitable for external users */ export const parseSchema = ( - input: S, + input: StrictSchema, ): ResolveSchema => { const v: unknown = track(input) assert(isRecord(v), 'Schema should be record') diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 39f2bb705d..08ee41aeb9 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1,20 +1,12 @@ -import { - writeUint16, - writeInt16, - writeUint32, - writeInt32, - writeUint64, - writeInt64, - writeFloatLE, - writeDoubleLE, - readUint16, - readInt16, - readUint32, - readInt32, - readUint64, - readInt64, - readFloatLE, - readDoubleLE, +import { + writeUint16, writeInt16, + writeUint32, writeInt32, + writeUint64, writeInt64, + writeFloatLE, writeDoubleLE, + readUint16, readInt16, + readUint32, readInt32, + readUint64, readInt64, + readFloatLE, readDoubleLE } from './utils/index.js' import { AutoSizedUint8Array } from './utils/AutoSizedUint8Array.js' @@ -44,8 +36,7 @@ export const BridgeResponseInverse = { flushQuery, flushModify */ -export type BridgeResponseEnum = - (typeof BridgeResponse)[keyof typeof BridgeResponse] +export type BridgeResponseEnum = (typeof BridgeResponse)[keyof typeof BridgeResponse] export const OpType = { id: 0, @@ -275,7 +266,7 @@ export const readModifyHeader = ( ): ModifyHeader => { const value: ModifyHeader = { opId: readUint32(buf, offset), - opType: buf[offset + 4] as OpTypeEnum, + opType: (buf[offset + 4]) as OpTypeEnum, schema: readUint64(buf, offset + 5), count: readUint32(buf, offset + 13), } @@ -283,10 +274,10 @@ export const readModifyHeader = ( } export const readModifyHeaderProps = { - opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - opType: (buf: Uint8Array, offset: number) => buf[offset + 4] as OpTypeEnum, - schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), - count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), + opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + opType: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as OpTypeEnum, + schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), + count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), } export const createModifyHeader = (header: ModifyHeader): Uint8Array => { @@ -362,9 +353,9 @@ export const readModifyUpdateHeader = ( offset: number, ): ModifyUpdateHeader => { const value: ModifyUpdateHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, - isTmp: ((buf[offset + 3] >>> 0) & 1) === 1, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, + isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, id: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -372,18 +363,14 @@ export const readModifyUpdateHeader = ( } export const readModifyUpdateHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createModifyUpdateHeader = ( - header: ModifyUpdateHeader, -): Uint8Array => { +export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { const buffer = new Uint8Array(ModifyUpdateHeaderByteSize) writeModifyUpdateHeader(buffer, header, 0) return buffer @@ -453,26 +440,22 @@ export const readModifyDeleteHeader = ( offset: number, ): ModifyDeleteHeader => { const value: ModifyDeleteHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, - isTmp: ((buf[offset + 3] >>> 0) & 1) === 1, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, + isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, id: readUint32(buf, offset + 4), } return value } export const readModifyDeleteHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), } -export const createModifyDeleteHeader = ( - header: ModifyDeleteHeader, -): Uint8Array => { +export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { const buffer = new Uint8Array(ModifyDeleteHeaderByteSize) writeModifyDeleteHeader(buffer, header, 0) return buffer @@ -533,23 +516,20 @@ export const readModifyCreateHeader = ( offset: number, ): ModifyCreateHeader => { const value: ModifyCreateHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, size: readUint32(buf, offset + 3), } return value } export const readModifyCreateHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), } -export const createModifyCreateHeader = ( - header: ModifyCreateHeader, -): Uint8Array => { +export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCreateHeaderByteSize) writeModifyCreateHeader(buffer, header, 0) return buffer @@ -613,8 +593,8 @@ export const readModifyCreateRingHeader = ( offset: number, ): ModifyCreateRingHeader => { const value: ModifyCreateRingHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, maxNodeId: readUint32(buf, offset + 3), size: readUint32(buf, offset + 7), } @@ -622,16 +602,13 @@ export const readModifyCreateRingHeader = ( } export const readModifyCreateRingHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } -export const createModifyCreateRingHeader = ( - header: ModifyCreateRingHeader, -): Uint8Array => { +export const createModifyCreateRingHeader = (header: ModifyCreateRingHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCreateRingHeaderByteSize) writeModifyCreateRingHeader(buffer, header, 0) return buffer @@ -666,8 +643,7 @@ export const ModifyIncrementInverse = { increment, decrement */ -export type ModifyIncrementEnum = - (typeof ModifyIncrement)[keyof typeof ModifyIncrement] +export type ModifyIncrementEnum = (typeof ModifyIncrement)[keyof typeof ModifyIncrement] export type ModifyMainHeader = { id: number @@ -723,8 +699,8 @@ export const readModifyMainHeader = ( ): ModifyMainHeader => { const value: ModifyMainHeader = { id: buf[offset], - type: buf[offset + 1] as PropTypeEnum, - increment: buf[offset + 2] as ModifyIncrementEnum, + type: (buf[offset + 1]) as PropTypeEnum, + increment: (buf[offset + 2]) as ModifyIncrementEnum, size: buf[offset + 3], start: readUint16(buf, offset + 4), } @@ -732,17 +708,14 @@ export const readModifyMainHeader = ( } export const readModifyMainHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, - increment: (buf: Uint8Array, offset: number) => - buf[offset + 2] as ModifyIncrementEnum, - size: (buf: Uint8Array, offset: number) => buf[offset + 3], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + increment: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as ModifyIncrementEnum, + size: (buf: Uint8Array, offset: number) => buf[offset + 3], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), } -export const createModifyMainHeader = ( - header: ModifyMainHeader, -): Uint8Array => { +export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { const buffer = new Uint8Array(ModifyMainHeaderByteSize) writeModifyMainHeader(buffer, header, 0) return buffer @@ -803,21 +776,19 @@ export const readModifyPropHeader = ( ): ModifyPropHeader => { const value: ModifyPropHeader = { id: buf[offset], - type: buf[offset + 1] as PropTypeEnum, + type: (buf[offset + 1]) as PropTypeEnum, size: readUint32(buf, offset + 2), } return value } export const readModifyPropHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyPropHeader = ( - header: ModifyPropHeader, -): Uint8Array => { +export const createModifyPropHeader = (header: ModifyPropHeader): Uint8Array => { const buffer = new Uint8Array(ModifyPropHeaderByteSize) writeModifyPropHeader(buffer, header, 0) return buffer @@ -860,8 +831,7 @@ export const ModifyReferencesInverse = { delIds, delTmpIds */ -export type ModifyReferencesEnum = - (typeof ModifyReferences)[keyof typeof ModifyReferences] +export type ModifyReferencesEnum = (typeof ModifyReferences)[keyof typeof ModifyReferences] export type ModifyReferencesHeader = { op: ModifyReferencesEnum @@ -898,20 +868,18 @@ export const readModifyReferencesHeader = ( offset: number, ): ModifyReferencesHeader => { const value: ModifyReferencesHeader = { - op: buf[offset] as ModifyReferencesEnum, + op: (buf[offset]) as ModifyReferencesEnum, size: readUint32(buf, offset + 1), } return value } export const readModifyReferencesHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyReferencesEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyReferencesEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } -export const createModifyReferencesHeader = ( - header: ModifyReferencesHeader, -): Uint8Array => { +export const createModifyReferencesHeader = (header: ModifyReferencesHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesHeaderByteSize) writeModifyReferencesHeader(buffer, header, 0) return buffer @@ -982,8 +950,8 @@ export const readModifyReferencesMetaHeader = ( ): ModifyReferencesMetaHeader => { const value: ModifyReferencesMetaHeader = { id: readUint32(buf, offset), - isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, - withIndex: ((buf[offset + 4] >>> 1) & 1) === 1, + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (((buf[offset + 4] >>> 1) & 1)) === 1, index: readUint32(buf, offset + 5), size: readUint32(buf, offset + 9), } @@ -991,18 +959,14 @@ export const readModifyReferencesMetaHeader = ( } export const readModifyReferencesMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - withIndex: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 1) & 1) === 1, - index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), } -export const createModifyReferencesMetaHeader = ( - header: ModifyReferencesMetaHeader, -): Uint8Array => { +export const createModifyReferencesMetaHeader = (header: ModifyReferencesMetaHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesMetaHeaderByteSize) writeModifyReferencesMetaHeader(buffer, header, 0) return buffer @@ -1067,22 +1031,19 @@ export const readModifyReferenceMetaHeader = ( ): ModifyReferenceMetaHeader => { const value: ModifyReferenceMetaHeader = { id: readUint32(buf, offset), - isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, size: readUint32(buf, offset + 5), } return value } export const readModifyReferenceMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), } -export const createModifyReferenceMetaHeader = ( - header: ModifyReferenceMetaHeader, -): Uint8Array => { +export const createModifyReferenceMetaHeader = (header: ModifyReferenceMetaHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferenceMetaHeaderByteSize) writeModifyReferenceMetaHeader(buffer, header, 0) return buffer @@ -1110,18 +1071,14 @@ export const ModifyCardinalityHeaderByteSize = 2 export const ModifyCardinalityHeaderAlignOf = 2 -export const packModifyCardinalityHeader = ( - obj: ModifyCardinalityHeader, -): number => { +export const packModifyCardinalityHeader = (obj: ModifyCardinalityHeader): number => { let val = 0 val |= ((obj.sparse ? 1 : 0) & 1) << 0 val |= (Number(obj.precision) & 255) << 8 return val } -export const unpackModifyCardinalityHeader = ( - val: number, -): ModifyCardinalityHeader => { +export const unpackModifyCardinalityHeader = (val: number): ModifyCardinalityHeader => { return { sparse: ((val >>> 0) & 1) === 1, precision: Number((val >>> 8) & 255), @@ -1156,20 +1113,18 @@ export const readModifyCardinalityHeader = ( offset: number, ): ModifyCardinalityHeader => { const value: ModifyCardinalityHeader = { - sparse: ((buf[offset] >>> 0) & 1) === 1, + sparse: (((buf[offset] >>> 0) & 1)) === 1, precision: buf[offset + 1], } return value } export const readModifyCardinalityHeaderProps = { - sparse: (buf: Uint8Array, offset: number) => ((buf[offset] >>> 0) & 1) === 1, - precision: (buf: Uint8Array, offset: number) => buf[offset + 1], + sparse: (buf: Uint8Array, offset: number) => (((buf[offset] >>> 0) & 1)) === 1, + precision: (buf: Uint8Array, offset: number) => buf[offset + 1], } -export const createModifyCardinalityHeader = ( - header: ModifyCardinalityHeader, -): Uint8Array => { +export const createModifyCardinalityHeader = (header: ModifyCardinalityHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCardinalityHeaderByteSize) writeModifyCardinalityHeader(buffer, header, 0) return buffer @@ -1223,19 +1178,17 @@ export const readModifyResultItem = ( ): ModifyResultItem => { const value: ModifyResultItem = { id: readUint32(buf, offset), - err: buf[offset + 4] as ModifyErrorEnum, + err: (buf[offset + 4]) as ModifyErrorEnum, } return value } export const readModifyResultItemProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - err: (buf: Uint8Array, offset: number) => buf[offset + 4] as ModifyErrorEnum, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + err: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyErrorEnum, } -export const createModifyResultItem = ( - header: ModifyResultItem, -): Uint8Array => { +export const createModifyResultItem = (header: ModifyResultItem): Uint8Array => { const buffer = new Uint8Array(ModifyResultItemByteSize) writeModifyResultItem(buffer, header, 0) return buffer @@ -1399,8 +1352,7 @@ export const PropTypeSelvaInverse = { aliases, colVec */ -export type PropTypeSelvaEnum = - (typeof PropTypeSelva)[keyof typeof PropTypeSelva] +export type PropTypeSelvaEnum = (typeof PropTypeSelva)[keyof typeof PropTypeSelva] export const RefOp = { clear: 0, @@ -1475,8 +1427,7 @@ export const ReferencesSelectInverse = { any, all */ -export type ReferencesSelectEnum = - (typeof ReferencesSelect)[keyof typeof ReferencesSelect] +export type ReferencesSelectEnum = (typeof ReferencesSelect)[keyof typeof ReferencesSelect] export const RefEdgeOp = { noEdgeNoIndexRealId: 0, @@ -2209,28 +2160,30 @@ export const writeSortHeaderProps = { }, } -export const readSortHeader = (buf: Uint8Array, offset: number): SortHeader => { +export const readSortHeader = ( + buf: Uint8Array, + offset: number, +): SortHeader => { const value: SortHeader = { - order: buf[offset] as OrderEnum, + order: (buf[offset]) as OrderEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, start: readUint16(buf, offset + 3), len: readUint16(buf, offset + 5), - lang: buf[offset + 7] as LangCodeEnum, + lang: (buf[offset + 7]) as LangCodeEnum, edgeType: readUint16(buf, offset + 8), } return value } export const readSortHeaderProps = { - order: (buf: Uint8Array, offset: number) => buf[offset] as OrderEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - lang: (buf: Uint8Array, offset: number) => buf[offset + 7] as LangCodeEnum, - edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + order: (buf: Uint8Array, offset: number) => (buf[offset]) as OrderEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + lang: (buf: Uint8Array, offset: number) => (buf[offset + 7]) as LangCodeEnum, + edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } export const createSortHeader = (header: SortHeader): Uint8Array => { @@ -2364,8 +2317,7 @@ export const QueryIteratorTypeInverse = { groupBy, groupByFilter */ -export type QueryIteratorTypeEnum = - (typeof QueryIteratorType)[keyof typeof QueryIteratorType] +export type QueryIteratorTypeEnum = (typeof QueryIteratorType)[keyof typeof QueryIteratorType] export const QueryType = { id: 0, @@ -2510,18 +2462,17 @@ export const readIncludeHeader = ( offset: number, ): IncludeHeader => { const value: IncludeHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, } return value } export const readIncludeHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, } export const createIncludeHeader = (header: IncludeHeader): Uint8Array => { @@ -2598,23 +2549,20 @@ export const readIncludeMetaHeader = ( offset: number, ): IncludeMetaHeader => { const value: IncludeMetaHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, } return value } export const readIncludeMetaHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, } -export const createIncludeMetaHeader = ( - header: IncludeMetaHeader, -): Uint8Array => { +export const createIncludeMetaHeader = (header: IncludeMetaHeader): Uint8Array => { const buffer = new Uint8Array(IncludeMetaHeaderByteSize) writeIncludeMetaHeader(buffer, header, 0) return buffer @@ -2678,25 +2626,22 @@ export const readIncludePartialHeader = ( offset: number, ): IncludePartialHeader => { const value: IncludePartialHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, amount: readUint16(buf, offset + 3), } return value } export const readIncludePartialHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, - amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createIncludePartialHeader = ( - header: IncludePartialHeader, -): Uint8Array => { +export const createIncludePartialHeader = (header: IncludePartialHeader): Uint8Array => { const buffer = new Uint8Array(IncludePartialHeaderByteSize) writeIncludePartialHeader(buffer, header, 0) return buffer @@ -2770,13 +2715,11 @@ export const readIncludePartialProp = ( } export const readIncludePartialPropProps = { - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), } -export const createIncludePartialProp = ( - header: IncludePartialProp, -): Uint8Array => { +export const createIncludePartialProp = (header: IncludePartialProp): Uint8Array => { const buffer = new Uint8Array(IncludePartialPropByteSize) writeIncludePartialProp(buffer, header, 0) return buffer @@ -2847,22 +2790,20 @@ export const readIncludeOpts = ( ): IncludeOpts => { const value: IncludeOpts = { end: readUint32(buf, offset), - isChars: ((buf[offset + 4] >>> 0) & 1) === 1, - hasOpts: ((buf[offset + 4] >>> 1) & 1) === 1, + isChars: (((buf[offset + 4] >>> 0) & 1)) === 1, + hasOpts: (((buf[offset + 4] >>> 1) & 1)) === 1, langFallbackSize: buf[offset + 5], - lang: buf[offset + 6] as LangCodeEnum, + lang: (buf[offset + 6]) as LangCodeEnum, } return value } export const readIncludeOptsProps = { - end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isChars: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - hasOpts: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 1) & 1) === 1, - langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], - lang: (buf: Uint8Array, offset: number) => buf[offset + 6] as LangCodeEnum, + end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isChars: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + hasOpts: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], + lang: (buf: Uint8Array, offset: number) => (buf[offset + 6]) as LangCodeEnum, } export const createIncludeOpts = (header: IncludeOpts): Uint8Array => { @@ -2928,8 +2869,8 @@ export const readIncludeResponse = ( } export const readIncludeResponseProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + prop: (buf: Uint8Array, offset: number) => buf[offset], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } export const createIncludeResponse = (header: IncludeResponse): Uint8Array => { @@ -3009,10 +2950,10 @@ export const readIncludeResponseMeta = ( offset: number, ): IncludeResponseMeta => { const value: IncludeResponseMeta = { - op: buf[offset] as ReadOpEnum, + op: (buf[offset]) as ReadOpEnum, prop: buf[offset + 1], - lang: buf[offset + 2] as LangCodeEnum, - compressed: ((buf[offset + 3] >>> 0) & 1) === 1, + lang: (buf[offset + 2]) as LangCodeEnum, + compressed: (((buf[offset + 3] >>> 0) & 1)) === 1, crc32: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -3020,18 +2961,15 @@ export const readIncludeResponseMeta = ( } export const readIncludeResponseMetaProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ReadOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - lang: (buf: Uint8Array, offset: number) => buf[offset + 2] as LangCodeEnum, - compressed: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ReadOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + lang: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as LangCodeEnum, + compressed: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createIncludeResponseMeta = ( - header: IncludeResponseMeta, -): Uint8Array => { +export const createIncludeResponseMeta = (header: IncludeResponseMeta): Uint8Array => { const buffer = new Uint8Array(IncludeResponseMetaByteSize) writeIncludeResponseMeta(buffer, header, 0) return buffer @@ -3100,8 +3038,8 @@ export const readSubscriptionHeader = ( offset: number, ): SubscriptionHeader => { const value: SubscriptionHeader = { - op: buf[offset] as OpTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as OpTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, fieldsLen: buf[offset + 3], partialLen: buf[offset + 4], } @@ -3109,16 +3047,13 @@ export const readSubscriptionHeader = ( } export const readSubscriptionHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as OpTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], - partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], + op: (buf: Uint8Array, offset: number) => (buf[offset]) as OpTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], + partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], } -export const createSubscriptionHeader = ( - header: SubscriptionHeader, -): Uint8Array => { +export const createSubscriptionHeader = (header: SubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(SubscriptionHeaderByteSize) writeSubscriptionHeader(buffer, header, 0) return buffer @@ -3229,11 +3164,7 @@ export const writeQueryHeaderProps = { includeSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 22) }, - iteratorType: ( - buf: Uint8Array, - value: QueryIteratorTypeEnum, - offset: number, - ) => { + iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { buf[offset + 24] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3249,10 +3180,10 @@ export const readQueryHeader = ( offset: number, ): QueryHeader => { const value: QueryHeader = { - op: buf[offset] as QueryTypeEnum, + op: (buf[offset]) as QueryTypeEnum, prop: buf[offset + 1], - typeId: readUint16(buf, offset + 2) as TypeId, - edgeTypeId: readUint16(buf, offset + 4) as TypeId, + typeId: (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, offset: readUint32(buf, offset + 6), limit: readUint32(buf, offset + 10), filterSize: readUint16(buf, offset + 14), @@ -3260,34 +3191,28 @@ export const readQueryHeader = ( edgeSize: readUint16(buf, offset + 18), edgeFilterSize: readUint16(buf, offset + 20), includeSize: readUint16(buf, offset + 22), - iteratorType: buf[offset + 24] as QueryIteratorTypeEnum, + iteratorType: (buf[offset + 24]) as QueryIteratorTypeEnum, size: readUint16(buf, offset + 25), - sort: ((buf[offset + 27] >>> 0) & 1) === 1, + sort: (((buf[offset + 27] >>> 0) & 1)) === 1, } return value } export const readQueryHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 4) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), - edgeFilterSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 20), - includeSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 22), - iteratorType: (buf: Uint8Array, offset: number) => - buf[offset + 24] as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), - sort: (buf: Uint8Array, offset: number) => - ((buf[offset + 27] >>> 0) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), + edgeFilterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 20), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 22), + iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 24]) as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), + sort: (buf: Uint8Array, offset: number) => (((buf[offset + 27] >>> 0) & 1)) === 1, } export const createQueryHeader = (header: QueryHeader): Uint8Array => { @@ -3385,8 +3310,8 @@ export const readQueryHeaderSingle = ( offset: number, ): QueryHeaderSingle => { const value: QueryHeaderSingle = { - op: buf[offset] as QueryTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as QueryTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, prop: buf[offset + 3], id: readUint32(buf, offset + 4), filterSize: readUint16(buf, offset + 8), @@ -3397,20 +3322,16 @@ export const readQueryHeaderSingle = ( } export const readQueryHeaderSingleProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - prop: (buf: Uint8Array, offset: number) => buf[offset + 3], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - includeSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 10), - aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), -} - -export const createQueryHeaderSingle = ( - header: QueryHeaderSingle, -): Uint8Array => { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + prop: (buf: Uint8Array, offset: number) => buf[offset + 3], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), + aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), +} + +export const createQueryHeaderSingle = (header: QueryHeaderSingle): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleByteSize) writeQueryHeaderSingle(buffer, header, 0) return buffer @@ -3490,10 +3411,10 @@ export const readQueryHeaderSingleReference = ( offset: number, ): QueryHeaderSingleReference => { const value: QueryHeaderSingleReference = { - op: buf[offset] as QueryTypeEnum, + op: (buf[offset]) as QueryTypeEnum, prop: buf[offset + 1], - typeId: readUint16(buf, offset + 2) as TypeId, - edgeTypeId: readUint16(buf, offset + 4) as TypeId, + typeId: (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, edgeSize: readUint16(buf, offset + 6), includeSize: readUint16(buf, offset + 8), } @@ -3501,19 +3422,15 @@ export const readQueryHeaderSingleReference = ( } export const readQueryHeaderSingleReferenceProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 4) as TypeId, - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } -export const createQueryHeaderSingleReference = ( - header: QueryHeaderSingleReference, -): Uint8Array => { +export const createQueryHeaderSingleReference = (header: QueryHeaderSingleReference): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleReferenceByteSize) writeQueryHeaderSingleReference(buffer, header, 0) return buffer @@ -3565,8 +3482,7 @@ export const VectorBaseTypeInverse = { float32, float64 */ -export type VectorBaseTypeEnum = - (typeof VectorBaseType)[keyof typeof VectorBaseType] +export type VectorBaseTypeEnum = (typeof VectorBaseType)[keyof typeof VectorBaseType] export type AggHeader = { op: QueryTypeEnum @@ -3630,11 +3546,7 @@ export const writeAggHeaderProps = { filterSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 11) }, - iteratorType: ( - buf: Uint8Array, - value: QueryIteratorTypeEnum, - offset: number, - ) => { + iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { buf[offset + 13] = Number(value) }, resultsSize: (buf: Uint8Array, value: number, offset: number) => { @@ -3651,39 +3563,36 @@ export const writeAggHeaderProps = { }, } -export const readAggHeader = (buf: Uint8Array, offset: number): AggHeader => { +export const readAggHeader = ( + buf: Uint8Array, + offset: number, +): AggHeader => { const value: AggHeader = { - op: buf[offset] as QueryTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as QueryTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, offset: readUint32(buf, offset + 3), limit: readUint32(buf, offset + 7), filterSize: readUint16(buf, offset + 11), - iteratorType: buf[offset + 13] as QueryIteratorTypeEnum, + iteratorType: (buf[offset + 13]) as QueryIteratorTypeEnum, resultsSize: readUint16(buf, offset + 14), accumulatorSize: readUint16(buf, offset + 16), - hasGroupBy: ((buf[offset + 18] >>> 0) & 1) === 1, - isSamplingSet: ((buf[offset + 18] >>> 1) & 1) === 1, + hasGroupBy: (((buf[offset + 18] >>> 0) & 1)) === 1, + isSamplingSet: (((buf[offset + 18] >>> 1) & 1)) === 1, } return value } export const readAggHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), - iteratorType: (buf: Uint8Array, offset: number) => - buf[offset + 13] as QueryIteratorTypeEnum, - resultsSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 14), - accumulatorSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 16), - hasGroupBy: (buf: Uint8Array, offset: number) => - ((buf[offset + 18] >>> 0) & 1) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => - ((buf[offset + 18] >>> 1) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), + iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 13]) as QueryIteratorTypeEnum, + resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 18] >>> 0) & 1)) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 18] >>> 1) & 1)) === 1, } export const createAggHeader = (header: AggHeader): Uint8Array => { @@ -3784,30 +3693,27 @@ export const readAggRefsHeader = ( offset: number, ): AggRefsHeader => { const value: AggRefsHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, targetProp: buf[offset + 1], offset: readUint32(buf, offset + 2), filterSize: readUint16(buf, offset + 6), resultsSize: readUint16(buf, offset + 8), accumulatorSize: readUint16(buf, offset + 10), - hasGroupBy: ((buf[offset + 12] >>> 0) & 1) === 1, - isSamplingSet: ((buf[offset + 12] >>> 1) & 1) === 1, + hasGroupBy: (((buf[offset + 12] >>> 0) & 1)) === 1, + isSamplingSet: (((buf[offset + 12] >>> 1) & 1)) === 1, } return value } export const readAggRefsHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - targetProp: (buf: Uint8Array, offset: number) => buf[offset + 1], - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - accumulatorSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 10), - hasGroupBy: (buf: Uint8Array, offset: number) => - ((buf[offset + 12] >>> 0) & 1) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => - ((buf[offset + 12] >>> 1) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + targetProp: (buf: Uint8Array, offset: number) => buf[offset + 1], + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), + hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 12] >>> 0) & 1)) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 12] >>> 1) & 1)) === 1, } export const createAggRefsHeader = (header: AggRefsHeader): Uint8Array => { @@ -3842,17 +3748,13 @@ export const addMultiSubscriptionHeaderByteSize = 2 export const addMultiSubscriptionHeaderAlignOf = 2 -export const packaddMultiSubscriptionHeader = ( - obj: addMultiSubscriptionHeader, -): number => { +export const packaddMultiSubscriptionHeader = (obj: addMultiSubscriptionHeader): number => { let val = 0 val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackaddMultiSubscriptionHeader = ( - val: number, -): addMultiSubscriptionHeader => { +export const unpackaddMultiSubscriptionHeader = (val: number): addMultiSubscriptionHeader => { return { typeId: Number((val >>> 0) & 65535), } @@ -3885,12 +3787,10 @@ export const readaddMultiSubscriptionHeader = ( } export const readaddMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createaddMultiSubscriptionHeader = ( - header: addMultiSubscriptionHeader, -): Uint8Array => { +export const createaddMultiSubscriptionHeader = (header: addMultiSubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(addMultiSubscriptionHeaderByteSize) writeaddMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -3913,17 +3813,13 @@ export const removeMultiSubscriptionHeaderByteSize = 2 export const removeMultiSubscriptionHeaderAlignOf = 2 -export const packremoveMultiSubscriptionHeader = ( - obj: removeMultiSubscriptionHeader, -): number => { +export const packremoveMultiSubscriptionHeader = (obj: removeMultiSubscriptionHeader): number => { let val = 0 val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackremoveMultiSubscriptionHeader = ( - val: number, -): removeMultiSubscriptionHeader => { +export const unpackremoveMultiSubscriptionHeader = (val: number): removeMultiSubscriptionHeader => { return { typeId: Number((val >>> 0) & 65535), } @@ -3956,12 +3852,10 @@ export const readremoveMultiSubscriptionHeader = ( } export const readremoveMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createremoveMultiSubscriptionHeader = ( - header: removeMultiSubscriptionHeader, -): Uint8Array => { +export const createremoveMultiSubscriptionHeader = (header: removeMultiSubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(removeMultiSubscriptionHeaderByteSize) writeremoveMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -4030,12 +3924,15 @@ export const writeAggPropProps = { }, } -export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { +export const readAggProp = ( + buf: Uint8Array, + offset: number, +): AggProp => { const value: AggProp = { propId: buf[offset], - propType: buf[offset + 1] as PropTypeEnum, + propType: (buf[offset + 1]) as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), - aggFunction: buf[offset + 4] as AggFunctionEnum, + aggFunction: (buf[offset + 4]) as AggFunctionEnum, resultPos: readUint16(buf, offset + 5), accumulatorPos: readUint16(buf, offset + 7), } @@ -4043,16 +3940,12 @@ export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { } export const readAggPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 1] as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2), - aggFunction: (buf: Uint8Array, offset: number) => - buf[offset + 4] as AggFunctionEnum, - resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - accumulatorPos: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 7), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + aggFunction: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as AggFunctionEnum, + resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + accumulatorPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), } export const createAggProp = (header: AggProp): Uint8Array => { @@ -4135,7 +4028,7 @@ export const readGroupByKeyProp = ( ): GroupByKeyProp => { const value: GroupByKeyProp = { propId: buf[offset], - propType: buf[offset + 1] as PropTypeEnum, + propType: (buf[offset + 1]) as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), stepType: buf[offset + 4], stepRange: readUint32(buf, offset + 5), @@ -4145,14 +4038,12 @@ export const readGroupByKeyProp = ( } export const readGroupByKeyPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 1] as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2), - stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], - stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], + stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), } export const createGroupByKeyProp = (header: GroupByKeyProp): Uint8Array => { @@ -4190,6 +4081,8 @@ export const FilterOpCompare = { le: 21, inc: 22, ninc: 23, + incBatch: 24, + nincBatch: 25, selectLargeRefs: 203, selectRef: 204, selectSmallRefs: 205, @@ -4212,6 +4105,10 @@ export const FilterOpCompareInverse = { 15: 'lt', 20: 'ge', 21: 'le', + 22: 'inc', + 23: 'ninc', + 24: 'incBatch', + 25: 'nincBatch', 203: 'selectLargeRefs', 204: 'selectRef', 205: 'selectSmallRefs', @@ -4234,6 +4131,10 @@ export const FilterOpCompareInverse = { lt, ge, le, + inc, + ninc, + incBatch, + nincBatch, selectLargeRefs, selectRef, selectSmallRefs, @@ -4242,8 +4143,7 @@ export const FilterOpCompareInverse = { nextOrIndex, andOp */ -export type FilterOpCompareEnum = - (typeof FilterOpCompare)[keyof typeof FilterOpCompare] +export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] export type FilterOp = { prop: PropTypeEnum @@ -4289,18 +4189,20 @@ export const writeFilterOpProps = { }, } -export const readFilterOp = (buf: Uint8Array, offset: number): FilterOp => { +export const readFilterOp = ( + buf: Uint8Array, + offset: number, +): FilterOp => { const value: FilterOp = { - prop: buf[offset] as PropTypeEnum, - compare: buf[offset + 1] as FilterOpCompareEnum, + prop: (buf[offset]) as PropTypeEnum, + compare: (buf[offset + 1]) as FilterOpCompareEnum, } return value } export const readFilterOpProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset] as PropTypeEnum, - compare: (buf: Uint8Array, offset: number) => - buf[offset + 1] as FilterOpCompareEnum, + prop: (buf: Uint8Array, offset: number) => (buf[offset]) as PropTypeEnum, + compare: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as FilterOpCompareEnum, } export const createFilterOp = (header: FilterOp): Uint8Array => { @@ -4396,15 +4298,13 @@ export const readFilterCondition = ( } export const readFilterConditionProps = { - op: (buf: Uint8Array, offset: number) => - unpackFilterOp(readUint16(buf, offset)), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - prop: (buf: Uint8Array, offset: number) => buf[offset + 6], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), - len: (buf: Uint8Array, offset: number) => buf[offset + 9], - fieldSchema: (buf: Uint8Array, offset: number) => - readUint64(buf, offset + 10), - offset: (buf: Uint8Array, offset: number) => buf[offset + 18], + op: (buf: Uint8Array, offset: number) => unpackFilterOp(readUint16(buf, offset)), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + prop: (buf: Uint8Array, offset: number) => buf[offset + 6], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + len: (buf: Uint8Array, offset: number) => buf[offset + 9], + fieldSchema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 10), + offset: (buf: Uint8Array, offset: number) => buf[offset + 18], } export const createFilterCondition = (header: FilterCondition): Uint8Array => { @@ -4471,16 +4371,15 @@ export const readFilterSelect = ( const value: FilterSelect = { size: readUint32(buf, offset), typeEntry: readUint64(buf, offset + 4), - typeId: readUint16(buf, offset + 12) as TypeId, + typeId: (readUint16(buf, offset + 12)) as TypeId, } return value } export const readFilterSelectProps = { - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 12) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 12)) as TypeId, } export const createFilterSelect = (header: FilterSelect): Uint8Array => { @@ -4563,16 +4462,14 @@ export const readSelvaSchemaHeader = ( } export const readSelvaSchemaHeaderProps = { - blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], - nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], - nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], - sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], + blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], + nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], + nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], + sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], } -export const createSelvaSchemaHeader = ( - header: SelvaSchemaHeader, -): Uint8Array => { +export const createSelvaSchemaHeader = (header: SelvaSchemaHeader): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaHeaderByteSize) writeSelvaSchemaHeader(buffer, header, 0) return buffer @@ -4601,9 +4498,7 @@ export const SelvaSchemaMicroBufferByteSize = 4 export const SelvaSchemaMicroBufferAlignOf = 4 -export const packSelvaSchemaMicroBuffer = ( - obj: SelvaSchemaMicroBuffer, -): number => { +export const packSelvaSchemaMicroBuffer = (obj: SelvaSchemaMicroBuffer): number => { let val = 0 val |= (Number(obj.type) & 255) << 0 val |= (Number(obj.len) & 65535) << 8 @@ -4611,9 +4506,7 @@ export const packSelvaSchemaMicroBuffer = ( return val } -export const unpackSelvaSchemaMicroBuffer = ( - val: number, -): SelvaSchemaMicroBuffer => { +export const unpackSelvaSchemaMicroBuffer = (val: number): SelvaSchemaMicroBuffer => { return { type: Number((val >>> 0) & 255), len: Number((val >>> 8) & 65535), @@ -4660,14 +4553,12 @@ export const readSelvaSchemaMicroBuffer = ( } export const readSelvaSchemaMicroBufferProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], + type: (buf: Uint8Array, offset: number) => buf[offset], + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], } -export const createSelvaSchemaMicroBuffer = ( - header: SelvaSchemaMicroBuffer, -): Uint8Array => { +export const createSelvaSchemaMicroBuffer = (header: SelvaSchemaMicroBuffer): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaMicroBufferByteSize) writeSelvaSchemaMicroBuffer(buffer, header, 0) return buffer @@ -4733,14 +4624,12 @@ export const readSelvaSchemaString = ( } export const readSelvaSchemaStringProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], - defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + type: (buf: Uint8Array, offset: number) => buf[offset], + fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], + defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createSelvaSchemaString = ( - header: SelvaSchemaString, -): Uint8Array => { +export const createSelvaSchemaString = (header: SelvaSchemaString): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaStringByteSize) writeSelvaSchemaString(buffer, header, 0) return buffer @@ -4813,8 +4702,8 @@ export const readSelvaSchemaText = ( } export const readSelvaSchemaTextProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], + type: (buf: Uint8Array, offset: number) => buf[offset], + nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], } export const createSelvaSchemaText = (header: SelvaSchemaText): Uint8Array => { @@ -4894,23 +4783,21 @@ export const readSelvaSchemaRef = ( const value: SelvaSchemaRef = { type: buf[offset], flags: buf[offset + 1], - dstNodeType: readUint16(buf, offset + 2) as TypeId, + dstNodeType: (readUint16(buf, offset + 2)) as TypeId, inverseField: buf[offset + 4], - edgeNodeType: readUint16(buf, offset + 5) as TypeId, + edgeNodeType: (readUint16(buf, offset + 5)) as TypeId, capped: readUint32(buf, offset + 7), } return value } export const readSelvaSchemaRefProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - flags: (buf: Uint8Array, offset: number) => buf[offset + 1], - dstNodeType: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], - edgeNodeType: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 5) as TypeId, - capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + type: (buf: Uint8Array, offset: number) => buf[offset], + flags: (buf: Uint8Array, offset: number) => buf[offset + 1], + dstNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], + edgeNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 5)) as TypeId, + capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } export const createSelvaSchemaRef = (header: SelvaSchemaRef): Uint8Array => { @@ -4989,15 +4876,13 @@ export const readSelvaSchemaColvec = ( } export const readSelvaSchemaColvecProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], + type: (buf: Uint8Array, offset: number) => buf[offset], + vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], } -export const createSelvaSchemaColvec = ( - header: SelvaSchemaColvec, -): Uint8Array => { +export const createSelvaSchemaColvec = (header: SelvaSchemaColvec): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaColvecByteSize) writeSelvaSchemaColvec(buffer, header, 0) return buffer @@ -5014,3 +4899,4 @@ export const pushSelvaSchemaColvec = ( buf.pushUint8(Number(header.hasDefault)) return index } + diff --git a/test/modify/hooks/create.ts b/test/modify/hooks/create.ts new file mode 100644 index 0000000000..7afa9f1ccb --- /dev/null +++ b/test/modify/hooks/create.ts @@ -0,0 +1,74 @@ +import type { InferPayload } from '../../../src/db-client/modify/types.js' +import { parseSchema } from '../../../src/schema.js' +import { testDb } from '../../shared/index.js' +import test from '../../shared/test.js' + +await test('modify - hooks - create', async (t) => { + // const db = await testDb(t, { + // types: { + // user: { + // props: { + // isNice: { + // type: 'boolean', + // hooks: { + // create(value) { + // // @ts-expect-error + // const isNiceNumber: number = value + // }, + // }, + // }, + // }, + // hooks: { + // create(payload) { + // const isNice: boolean = payload.isNice + // // @ts-expect-error + // const randomKey: any = payload.randomKey + // }, + // }, + // }, + // }, + // }) + + parseSchema({ + types: { + user: { + props: { + isNice: 'boolean', + }, + hooks: { + create(payload) { + const isNice: boolean | null | undefined = payload.isNice + // @ts-expect-error + const isWrong: boolean | null | undefined = payload.isWrong + }, + }, + }, + }, + }) + + const schema = { + types: { + user: { + props: { + isNice: 'boolean', + }, + hooks: { + create(payload) {}, + }, + }, + }, + } as const + + const payload: InferPayload = {} + const isNice: boolean | null | undefined = payload.isNice + // @ts-expect-error + const isWrong: boolean | null | undefined = payload.isWrong + + // const schema: SchemaIn = { + + // } + + // await db.create('user', { + // isNice: true, + // }) +}) diff --git a/test/modify/hooks/type_test.ts b/test/modify/hooks/type_test.ts new file mode 100644 index 0000000000..9770a07920 --- /dev/null +++ b/test/modify/hooks/type_test.ts @@ -0,0 +1,94 @@ +import { parseSchema } from '../../../src/schema.js' +import { testDb } from '../../shared/index.js' + +async function check() { + const schemaOut = parseSchema({ + types: { + user: { + other: { + ref: 'other', + prop: 'user', + }, + }, + other: { + user: { + ref: 'user', + prop: 'other', + }, + }, + }, + }) + + // @ts-expect-error + schemaOut.types.foo = {} + + const db = await testDb(null as any, { + types: { + user: { + other: { + ref: 'other', + prop: 'user', + }, + }, + other: { + user: { + ref: 'user', + prop: 'other', + }, + }, + }, + }) + + db.create('user', {}) +} + +// import type { InferPayload } from '../../../src/db-client/modify/types.js' +// import { parseSchema, type SchemaIn } from '../../../src/schema.js' + +// // Simple test case to verify strict typing of create hook payload +// const bla = parseSchema({ +// types: { +// user: { +// props: { +// isNice: 'boolean', +// }, +// // testPayload: { +// // // @ts-expect-error +// // isNice: number, +// // }, +// hooks: { +// create(payload) { +// const isNice: boolean | null | undefined = payload!.isNice +// // @ts-expect-error +// const isWrong: boolean | null | undefined = payload.isWrong + +// return payload +// }, +// }, +// // testhooks: { +// // create(payload) { +// // const isNice: boolean | null | undefined = payload.isNice +// // // @ts-expect-error +// // const isWrong: boolean | null | undefined = payload.isWrong +// // }, +// // }, +// }, +// }, +// }) + +// bla.types.haha + +// const schema = { +// types: { +// user: { +// props: { +// isNice: 'boolean', +// }, +// }, +// }, +// } as const + +// const payload: InferPayload = {} +// const isNice: boolean | null | undefined = payload.isNice +// // @ts-expect-error +// const isWrong: boolean | null | undefined = payload.isWrong diff --git a/test/shared/index.ts b/test/shared/index.ts index 6cd5736f4c..f65e1a8a80 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -4,6 +4,7 @@ import type { ResolveSchema, SchemaIn, ValidateSchema, + StrictSchema, } from '../../src/schema.js' import { BasedDb, DbServer, type DbClient } from '../../src/sdk.js' import test from './test.js' @@ -39,57 +40,14 @@ export function logMemoryUsage() { } } -import { - BasedCreatePromise, - BasedDeletePromise, - BasedInsertPromise, - BasedUpdatePromise, - BasedUpsertPromise, - ModifyOpts, - InferPayload, - InferTarget, - BasedModify, -} from '../../src/sdk.js' - -export interface BasedTestDb - extends DbClient> { - create( - type: T, - obj: InferPayload, T>, - opts?: ModifyOpts, - ): BasedCreatePromise - update( - type: T, - target: number | BasedModify, - obj: InferPayload, T>, - opts?: ModifyOpts, - ): BasedUpdatePromise - upsert( - type: T, - target: InferTarget, T>, - obj: InferPayload, T>, - opts?: ModifyOpts, - ): BasedUpsertPromise - insert( - type: T, - target: InferTarget, T>, - obj: InferPayload, T>, - opts?: ModifyOpts, - ): BasedInsertPromise - delete( - type: keyof S['types'] & string, - target: number | BasedModify, - ): BasedDeletePromise -} - export const testDb = async ( t: Parameters[1]>[0], - schema: S & ValidateSchema, -): Promise> => { + schema: StrictSchema, +): Promise>> => { const db = new BasedDb({ path: t.tmp }) await db.start({ clean: true }) t.after(() => db.destroy()) - return db.setSchema(schema) as unknown as BasedTestDb + return db.setSchema(schema) } export async function countDirtyBlocks(server: DbServer) { From 3cc77eee24fdaa3d42a08e041a7196a51df212ad Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 14 Feb 2026 00:39:04 +0100 Subject: [PATCH 266/449] wip --- src/db-client/index.ts | 3 --- src/schema/index.ts | 1 - src/schema/schema/hooks.ts | 8 ++++---- src/schema/schema/payload.ts | 2 +- src/schema/schema/schema.ts | 26 +++++++++++++------------- src/schema/schema/type.ts | 2 +- test/modify/hooks/type_test.ts | 28 ++++++++++++++++++++++++---- 7 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 0dee500f04..ac1def1d81 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -10,9 +10,6 @@ import { type SchemaMigrateFns, type SchemaOut, type ResolveSchema, - type Schema, - type ResolvedProps, - type ValidateSchema, type StrictSchema, } from '../schema/index.js' import { AutoSizedUint8Array } from '../utils/AutoSizedUint8Array.js' diff --git a/src/schema/index.ts b/src/schema/index.ts index 20d4425bfc..e7efab8a5a 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -17,7 +17,6 @@ import { parseSchema, type SchemaIn, type SchemaOut, - type ValidateSchema, type StrictSchema, } from './schema/schema.js' diff --git a/src/schema/schema/hooks.ts b/src/schema/schema/hooks.ts index 77e708293b..6daf11bcf3 100644 --- a/src/schema/schema/hooks.ts +++ b/src/schema/schema/hooks.ts @@ -3,10 +3,10 @@ import { isFunction, isRecord } from './shared.js' type BasedDbQuery = any type Operator = any -export type SchemaHooks = { - create?: (payload: Record) => void | Record - update?: (payload: Record) => void | Record - read?: (result: Record) => void | null | Record +export type SchemaHooks> = { + create?: (payload: Payload) => void | Payload + update?: (payload: Payload) => void | Payload + read?: (result: Payload) => void | null | Payload search?: (query: BasedDbQuery, fields: Set) => void include?: ( query: BasedDbQuery, diff --git a/src/schema/schema/payload.ts b/src/schema/schema/payload.ts index e2f2cb880a..0340b5b2f2 100644 --- a/src/schema/schema/payload.ts +++ b/src/schema/schema/payload.ts @@ -128,7 +128,7 @@ export type InferType< } export type InferPayload< - S extends SchemaOut, + S extends { types: any; locales?: any }, T extends keyof S['types'], > = InferType< S['types'][T]['props'], diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index 6e44763fc3..972f10d79b 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -13,6 +13,8 @@ import { inspect } from 'node:util' import { postParseRefs } from './reference.js' import hash from '../../hash/hash.js' import { parseLocales, type SchemaLocales } from './locales.js' +import { type SchemaHooks } from './hooks.js' +import type { InferPayload, InferType } from './payload.js' export type SchemaTypes = Record> export type SchemaMigrateFn = ( node: Record, @@ -179,22 +181,20 @@ type ValidateProps = { [K in keyof Props]: ValidateProp } -export type ValidateSchema = Omit & { +type ValidateSchema = Omit & { types: { [K in keyof S['types']]: S['types'][K] extends { props: infer P } - ? { props: ValidateProps } & Omit< - S['types'][K], - 'props' - > + ? { + props: ValidateProps + hooks?: SchemaHooks> + } & Omit : { - [P in keyof S['types'][K]]: P extends - | 'hooks' - | 'blockCapacity' - | 'insertOnly' - | 'capped' - | 'partial' - ? S['types'][K][P] - : ValidateProp + [P in keyof S['types'][K]]: ValidateProp< + S['types'][K][P], + S['types'], + K & string, + P & string + > } } } diff --git a/src/schema/schema/type.ts b/src/schema/schema/type.ts index a193026253..867b57a603 100644 --- a/src/schema/schema/type.ts +++ b/src/schema/schema/type.ts @@ -12,7 +12,7 @@ import type { SchemaOut } from './schema.js' export type SchemaProps = Record> type SchemaTypeObj = { - hooks?: SchemaHooks + hooks?: unknown blockCapacity?: number insertOnly?: boolean capped?: number diff --git a/test/modify/hooks/type_test.ts b/test/modify/hooks/type_test.ts index 9770a07920..d2981c862a 100644 --- a/test/modify/hooks/type_test.ts +++ b/test/modify/hooks/type_test.ts @@ -1,13 +1,23 @@ import { parseSchema } from '../../../src/schema.js' +import type { InferPayload } from '../../../src/schema/schema/payload.js' import { testDb } from '../../shared/index.js' async function check() { const schemaOut = parseSchema({ types: { user: { - other: { - ref: 'other', - prop: 'user', + props: { + other: { + ref: 'other', + prop: 'user', + }, + }, + hooks: { + create(userPayload) { + const other = userPayload!.other + // @ts-expect-error + const nothing = userPayload.nothing + }, }, }, other: { @@ -19,6 +29,12 @@ async function check() { }, }) + const userPayload: InferPayload = { + other: 1, + // @ts-expect-error + nothing: 'oops', + } + // @ts-expect-error schemaOut.types.foo = {} @@ -39,7 +55,11 @@ async function check() { }, }) - db.create('user', {}) + db.create('user', { + other: 1, + // @ts-expect-error + nothing: 'oops', + }) } // import type { InferPayload } from '../../../src/db-client/modify/types.js' From 68fd4fb1174d85746bafcc02f30393864d3e91c5 Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 14 Feb 2026 00:49:30 +0100 Subject: [PATCH 267/449] init hooks --- src/schema/schema/schema.ts | 2 +- test/modify/hooks/create.ts | 2 -- test/modify/hooks/type_test.ts | 7 +++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index 972f10d79b..e5ce2a811a 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -186,7 +186,7 @@ type ValidateSchema = Omit & { [K in keyof S['types']]: S['types'][K] extends { props: infer P } ? { props: ValidateProps - hooks?: SchemaHooks> + hooks?: SchemaHooks } & Omit : { [P in keyof S['types'][K]]: ValidateProp< diff --git a/test/modify/hooks/create.ts b/test/modify/hooks/create.ts index 7afa9f1ccb..3f0fc408a3 100644 --- a/test/modify/hooks/create.ts +++ b/test/modify/hooks/create.ts @@ -38,8 +38,6 @@ await test('modify - hooks - create', async (t) => { hooks: { create(payload) { const isNice: boolean | null | undefined = payload.isNice - // @ts-expect-error - const isWrong: boolean | null | undefined = payload.isWrong }, }, }, diff --git a/test/modify/hooks/type_test.ts b/test/modify/hooks/type_test.ts index d2981c862a..e01d62cd3b 100644 --- a/test/modify/hooks/type_test.ts +++ b/test/modify/hooks/type_test.ts @@ -14,9 +14,7 @@ async function check() { }, hooks: { create(userPayload) { - const other = userPayload!.other - // @ts-expect-error - const nothing = userPayload.nothing + const other = userPayload.other }, }, }, @@ -29,7 +27,8 @@ async function check() { }, }) - const userPayload: InferPayload = { + type UserPayload = InferPayload + const userPayload: UserPayload = { other: 1, // @ts-expect-error nothing: 'oops', From 5f5c8d2a6b73fbca2b808683903cfdc85dc475d8 Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 14 Feb 2026 00:53:38 +0100 Subject: [PATCH 268/449] wip --- src/schema/schema/schema.ts | 2 -- test/modify/hooks/create.ts | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/schema/schema/schema.ts b/src/schema/schema/schema.ts index e5ce2a811a..0be0156de8 100644 --- a/src/schema/schema/schema.ts +++ b/src/schema/schema/schema.ts @@ -14,7 +14,6 @@ import { postParseRefs } from './reference.js' import hash from '../../hash/hash.js' import { parseLocales, type SchemaLocales } from './locales.js' import { type SchemaHooks } from './hooks.js' -import type { InferPayload, InferType } from './payload.js' export type SchemaTypes = Record> export type SchemaMigrateFn = ( node: Record, @@ -73,7 +72,6 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( ? I : never -// Helper to find Props in other types that reference TName with a specific 'prop' field // Helper to find Props in other types that reference TName with a specific 'prop' field type GetBackRefs = UnionToIntersection< { diff --git a/test/modify/hooks/create.ts b/test/modify/hooks/create.ts index 3f0fc408a3..13ff290ca2 100644 --- a/test/modify/hooks/create.ts +++ b/test/modify/hooks/create.ts @@ -38,6 +38,7 @@ await test('modify - hooks - create', async (t) => { hooks: { create(payload) { const isNice: boolean | null | undefined = payload.isNice + return payload }, }, }, From a940bdaff583a5c694530ddeb2f3b9bd655d9d31 Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 14 Feb 2026 09:51:43 +0100 Subject: [PATCH 269/449] add hooks --- native/modify/modify.zig | 2 - src/db-client/modify/create.ts | 38 ++++++++-------- src/db-client/modify/index.ts | 19 +++++++- src/db-client/modify/update.ts | 22 +++++----- src/db-client/modify/upsert.ts | 16 ++++++- src/schema/defs/getTypeDefs.ts | 35 ++++++++++++--- src/schema/defs/index.ts | 11 ++++- src/schema/schema/type.ts | 2 +- test/modify/hooks/create.ts | 79 ++++++++++------------------------ 9 files changed, 128 insertions(+), 96 deletions(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 1989517227..46acae1fcb 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -288,14 +288,12 @@ pub fn modify( const typeEntry = try Node.getType(db, create.type); const data: []u8 = buf[i .. i + create.size]; const nextId = db.ids[create.type - 1] % create.maxNodeId + 1; - var node = Node.getNode(typeEntry, nextId); if (node) |oldNode| { Node.flushNode(db, typeEntry, oldNode); } else { node = try Node.upsertNode(typeEntry, nextId); } - modifyProps(db, typeEntry, node.?, data, items) catch { // handle errors }; diff --git a/src/db-client/modify/create.ts b/src/db-client/modify/create.ts index 18b317306e..3c21aabef8 100644 --- a/src/db-client/modify/create.ts +++ b/src/db-client/modify/create.ts @@ -8,33 +8,32 @@ import { writeModifyCreateRingHeaderProps, type LangCodeEnum, } from '../../zigTsExports.js' -import { getTypeDef } from './index.js' +import { execHooks, getTypeDef } from './index.js' import { serializeProps } from './props.js' -import type { InferPayload } from './types.js' -export const serializeCreate = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, - payload: InferPayload, +export const serializeCreate = ( + schema: SchemaOut, + type: string, + payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum, ) => { const typeDef = getTypeDef(schema, type) - const maxNodeId = typeDef.schema.capped ?? 0 - - if (maxNodeId ?? 0 > 0) { + if (typeDef.schema.capped) { const index = pushModifyCreateRingHeader(buf, { op: Modify.createRing, type: typeDef.id, - maxNodeId, + maxNodeId: typeDef.schema.capped, size: 0, }) const start = buf.length - - serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + serializeProps( + typeDef.tree, + execHooks(typeDef, payload, 'create'), + buf, + Modify.create, + lang, + ) writeModifyCreateRingHeaderProps.size(buf.data, buf.length - start, index) } else { const index = pushModifyCreateHeader(buf, { @@ -43,8 +42,13 @@ export const serializeCreate = < size: 0, }) const start = buf.length - - serializeProps(typeDef.tree, payload, buf, Modify.create, lang) + serializeProps( + typeDef.tree, + execHooks(typeDef, payload, 'create'), + buf, + Modify.create, + lang, + ) writeModifyCreateHeaderProps.size(buf.data, buf.length - start, index) } } diff --git a/src/db-client/modify/index.ts b/src/db-client/modify/index.ts index 99c1884706..a01b2bf3b1 100644 --- a/src/db-client/modify/index.ts +++ b/src/db-client/modify/index.ts @@ -7,9 +7,26 @@ import type { serializeCreate } from './create.js' import type { serializeUpdate } from './update.js' import type { serializeDelete } from './delete.js' import type { serializeUpsert } from './upsert.js' +import type { TypeDef } from '../../schema/defs/index.js' +import { getByPath, setByPath } from '../../utils/path.js' export { getTypeDefs } -export const getTypeDef = (schema: SchemaOut, type: string) => { +export const execHooks = ( + typeDef: TypeDef, + payload: Record, + key: 'create' | 'update', +) => { + const typeHook = typeDef.schema.hooks?.[key] + if (typeHook) payload = typeHook(payload) ?? payload + for (const def of typeDef.propHooks[key]) { + const propHook = def.schema.hooks![key]! + const res = propHook(getByPath(payload, def.path), payload) + if (res !== undefined) setByPath(payload, def.path, res) + } + return payload +} + +export const getTypeDef = (schema: SchemaOut, type: string): TypeDef => { const typeDef = getTypeDefs(schema).get(type) if (!typeDef) { throw new Error(`Type ${type} not found`) diff --git a/src/db-client/modify/update.ts b/src/db-client/modify/update.ts index 626e14294f..47627aa6f3 100644 --- a/src/db-client/modify/update.ts +++ b/src/db-client/modify/update.ts @@ -6,18 +6,14 @@ import { writeModifyUpdateHeaderProps, type LangCodeEnum, } from '../../zigTsExports.js' -import { assignTarget, BasedModify, getTypeDef } from './index.js' +import { assignTarget, BasedModify, execHooks, getTypeDef } from './index.js' import { serializeProps } from './props.js' -import type { InferPayload } from './types.js' -export const serializeUpdate = < - S extends SchemaOut = SchemaOut, - T extends keyof S['types'] & string = keyof S['types'] & string, ->( - schema: S, - type: T, +export const serializeUpdate = ( + schema: SchemaOut, + type: string, item: number | BasedModify, - payload: InferPayload, + payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum, ) => { @@ -29,6 +25,12 @@ export const serializeUpdate = < }) const index = pushModifyUpdateHeader(buf, header) const start = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + serializeProps( + typeDef.tree, + execHooks(typeDef, payload, 'update'), + buf, + Modify.update, + lang, + ) writeModifyUpdateHeaderProps.size(buf.data, buf.length - start, index) } diff --git a/src/db-client/modify/upsert.ts b/src/db-client/modify/upsert.ts index 840fcedf11..d6dfbea8b4 100644 --- a/src/db-client/modify/upsert.ts +++ b/src/db-client/modify/upsert.ts @@ -6,7 +6,7 @@ import { type LangCodeEnum, writeModifyCreateHeaderProps, } from '../../zigTsExports.js' -import { getTypeDef } from './index.js' +import { getTypeDef, execHooks } from './index.js' import { serializeProps } from './props.js' import type { InferPayload, InferTarget } from './types.js' @@ -36,6 +36,18 @@ export const serializeUpsert = < // serialize payload const sizePos = buf.reserveUint32() const startPayload = buf.length - serializeProps(typeDef.tree, payload, buf, Modify.update, lang) + serializeProps( + typeDef.tree, + serializeProps( + typeDef.tree, + execHooks(typeDef, payload, 'create'), + buf, + Modify.create, + lang, + ), + buf, + Modify.update, + lang, + ) buf.writeUint32(buf.length - startPayload, sizePos) } diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 97f5fa9d7b..20de34ebac 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -1,11 +1,13 @@ import { type SchemaOut, type SchemaProp, + type SchemaPropHooks, type SchemaProps, type SchemaType, } from '../../schema.js' +import { getByPath, setByPath } from '../../utils/path.js' import { PropType } from '../../zigTsExports.js' -import { defs, type PropDef, type TypeDef } from './index.js' +import { defs, type PropDef, type PropTree, type TypeDef } from './index.js' const mainSorter = (a, b) => { if (a.size === 8) return -1 @@ -81,6 +83,16 @@ const getTypeDef = ( tree: { props: new Map(), required: [] }, schema, schemaRoot, + propHooks: { + create: [], + update: [], + read: [], + search: [], + include: [], + filter: [], + groupBy: [], + aggregate: [], + }, } const walk = ( @@ -92,18 +104,31 @@ const getTypeDef = ( const prop = props[key] const path = [...pPath, key] let required = prop.required + let def: PropTree | PropDef if (prop.type === 'object') { - const branch = { props: new Map(), required: [] } - if (walk(prop.props, path, branch)) required = true - tree.props.set(key, branch) + def = { + path, + schema: prop, + props: new Map(), + required: [], + } + if (walk(prop.props, path, def)) required = true + tree.props.set(key, def) } else { - const def = addPropDef(prop, path, typeDef) + def = addPropDef(prop, path, typeDef) typeDef.props.set(path.join('.'), def) tree.props.set(key, def) } if (required) { tree.required.push(key) } + if (prop.hooks) { + for (const key in typeDef.propHooks) { + if (prop.hooks[key]) { + typeDef.propHooks[key].push(def) + } + } + } } return !!tree.required.length } diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index fc94f771c5..99608a2a37 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -1,4 +1,10 @@ -import type { SchemaOut, SchemaProp, SchemaType } from '../../schema.js' +import type { + SchemaHooks, + SchemaObject, + SchemaOut, + SchemaProp, + SchemaType, +} from '../../schema.js' import type { LangCodeEnum, ModifyEnum, @@ -16,6 +22,8 @@ import * as vector from './props/vector.js' export type PropTree = { props: Map required: string[] + path: string[] + schema: SchemaObject } export type TypeDef = { @@ -27,6 +35,7 @@ export type TypeDef = { tree: PropTree schema: SchemaType schemaRoot: SchemaOut + propHooks: Record } export type PropDef = { diff --git a/src/schema/schema/type.ts b/src/schema/schema/type.ts index 867b57a603..a193026253 100644 --- a/src/schema/schema/type.ts +++ b/src/schema/schema/type.ts @@ -12,7 +12,7 @@ import type { SchemaOut } from './schema.js' export type SchemaProps = Record> type SchemaTypeObj = { - hooks?: unknown + hooks?: SchemaHooks blockCapacity?: number insertOnly?: boolean capped?: number diff --git a/test/modify/hooks/create.ts b/test/modify/hooks/create.ts index 13ff290ca2..112b6016c2 100644 --- a/test/modify/hooks/create.ts +++ b/test/modify/hooks/create.ts @@ -1,73 +1,38 @@ -import type { InferPayload } from '../../../src/db-client/modify/types.js' -import { parseSchema } from '../../../src/schema.js' -import { testDb } from '../../shared/index.js' +import { deepEqual, testDb } from '../../shared/index.js' import test from '../../shared/test.js' await test('modify - hooks - create', async (t) => { - // const db = await testDb(t, { - // types: { - // user: { - // props: { - // isNice: { - // type: 'boolean', - // hooks: { - // create(value) { - // // @ts-expect-error - // const isNiceNumber: number = value - // }, - // }, - // }, - // }, - // hooks: { - // create(payload) { - // const isNice: boolean = payload.isNice - // // @ts-expect-error - // const randomKey: any = payload.randomKey - // }, - // }, - // }, - // }, - // }) - - parseSchema({ + const db = await testDb(t, { types: { user: { props: { - isNice: 'boolean', + rating: { + type: 'number', + hooks: { + create(value) { + if (value < 5) { + return value + 1 + } + }, + }, + }, }, hooks: { create(payload) { - const isNice: boolean | null | undefined = payload.isNice - return payload + if (!payload.rating) { + payload.rating = 5 + } }, }, }, }, }) - const schema = { - types: { - user: { - props: { - isNice: 'boolean', - }, - hooks: { - create(payload) {}, - }, - }, - }, - } as const - - const payload: InferPayload = {} - const isNice: boolean | null | undefined = payload.isNice - // @ts-expect-error - const isWrong: boolean | null | undefined = payload.isWrong - - // const schema: SchemaIn = { - - // } - - // await db.create('user', { - // isNice: true, - // }) + { + const id = await db.create('user', {}) + deepEqual(await db.query2('user', id).get(), { + id, + rating: 5, + }) + } }) From a491c2eac8cd105b0b095080ba318ac6fa571ec8 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sat, 14 Feb 2026 10:46:31 +0100 Subject: [PATCH 270/449] clean up condition parsing --- native/query/filter/filter.zig | 1 - native/query/filter/instruction.zig | 2 +- native/query/filter/select.zig | 30 - native/types.zig | 1 - src/db-query/ast/filter/comparison.ts | 135 ++++ src/db-query/ast/filter/condition.ts | 200 ------ src/db-query/ast/filter/filter.ts | 33 +- src/db-query/ast/filter/operatorToEnum.ts | 62 ++ src/zigTsExports.ts | 744 +++++++++------------- test/query-ast/include.ts | 68 +- test/query/ast.ts | 17 +- test/query/db.ts | 7 +- 12 files changed, 578 insertions(+), 722 deletions(-) delete mode 100644 native/query/filter/select.zig create mode 100644 src/db-query/ast/filter/comparison.ts delete mode 100644 src/db-query/ast/filter/condition.ts create mode 100644 src/db-query/ast/filter/operatorToEnum.ts diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index e5da606844..bf5520c6e0 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -7,7 +7,6 @@ const Fields = @import("../../selva/fields.zig"); const Selva = @import("../../selva/selva.zig"); const t = @import("../../types.zig"); const Compare = @import("compare.zig"); -const Select = @import("select.zig"); const Instruction = @import("instruction.zig"); const COND_ALIGN_BYTES = @alignOf(t.FilterCondition); diff --git a/native/query/filter/instruction.zig b/native/query/filter/instruction.zig index 8bdfe8925c..ddaeebf8a1 100644 --- a/native/query/filter/instruction.zig +++ b/native/query/filter/instruction.zig @@ -27,7 +27,7 @@ pub fn parseOp(comptime op: t.FilterOpCompare) OpMeta { return .{ .func = getFunc(op), .invert = switch (op) { - .neq, .neqBatch, .neqBatchSmall, .nrange => true, + .neq, .neqBatch, .neqBatchSmall, .nrange, .ninc => true, else => false, }, }; diff --git a/native/query/filter/select.zig b/native/query/filter/select.zig deleted file mode 100644 index e1ae595b28..0000000000 --- a/native/query/filter/select.zig +++ /dev/null @@ -1,30 +0,0 @@ -const std = @import("std"); -const Query = @import("../common.zig"); -const utils = @import("../../utils.zig"); -const Node = @import("../../selva/node.zig"); -const Schema = @import("../../selva/schema.zig"); -const Fields = @import("../../selva/fields.zig"); -const t = @import("../../types.zig"); -const Filter = @import("filter.zig"); - -pub fn largeRef(ctx: *Query.QueryCtx, q: []u8, value: []u8, i: *usize) !bool { - const selectReference = utils.readNext(t.FilterSelect, q, i); - const nodeId = utils.read(u32, value, 0); - if (nodeId == 0) { - i.* += selectReference.size; - return false; - } - const refTypeEntry = try Node.getType(ctx.db, selectReference.typeId); - if (Node.getNode(refTypeEntry, nodeId)) |refNode| { - const refPass = try Filter.filter( - refNode, - ctx, - q[i.* .. i.* + selectReference.size], - refTypeEntry, - ); - i.* += selectReference.size; - return refPass; - } - i.* += selectReference.size; - return true; -} diff --git a/native/types.zig b/native/types.zig index 4ddf6f8896..206269dec4 100644 --- a/native/types.zig +++ b/native/types.zig @@ -893,7 +893,6 @@ pub const FilterOpCompare = enum(u8) { selectLargeRefsEdge = 207, nextOrIndex = 253, - andOp = 254, }; pub const FilterOp = packed struct { diff --git a/src/db-query/ast/filter/comparison.ts b/src/db-query/ast/filter/comparison.ts new file mode 100644 index 0000000000..18e9f00e23 --- /dev/null +++ b/src/db-query/ast/filter/comparison.ts @@ -0,0 +1,135 @@ +import native from '../../../native.js' +import { PropDef } from '../../../schema/defs/index.js' +import { ENCODER } from '../../../utils/uint8.js' +import { + FilterConditionByteSize, + FilterConditionAlignOf, + writeFilterCondition, + PropTypeEnum, + FilterOpCompareEnum, + FilterOpCompare, +} from '../../../zigTsExports.js' +import { FilterOpts, Operator } from '../ast.js' +import { operatorToEnum } from './operatorToEnum.js' + +export const conditionByteSize = (propSize: number, size: number) => { + return size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propSize +} + +// nice to get this from zig +const VECTOR_BYTES = 16 + +export const createCondition = ( + prop: { start: number; id: number; size: number; type: PropTypeEnum }, + op: FilterOpCompareEnum, + size: number = prop.size, +) => { + const conditionBuffer = new Uint8Array(conditionByteSize(prop.size, size)) + conditionBuffer[0] = 255 // Means condition header is not aligned + const offset = + writeFilterCondition( + conditionBuffer, + { + op: { + prop: prop.type, + compare: op, + }, + start: prop.start || 0, + prop: prop.id, + fieldSchema: 0, + len: prop.size, + offset: 255, // Means value is not aligned + size: size + prop.size, + }, + FilterConditionAlignOf + 1, + ) + prop.size + return { condition: conditionBuffer, offset } +} + +export const fixedComparison = ( + prop: PropDef, + operator: Operator, + val: any[], + opts?: FilterOpts, +) => { + const op = operatorToEnum(operator, val, prop) + + if (op === FilterOpCompare.eqBatch || op === FilterOpCompare.neqBatch) { + const size = val.length * prop.size + const empty = VECTOR_BYTES - (size % VECTOR_BYTES) + const rest = empty / prop.size + const { condition, offset } = createCondition(prop, op, size + empty) + let i = offset + for (const v of val) { + prop.write(condition, v, i) + i += prop.size + } + for (let j = 0; j < rest; j++) { + prop.write(condition, val[0], i) + i += prop.size + } + return condition + } + + if ( + op === FilterOpCompare.eqBatchSmall || + op === FilterOpCompare.neqBatchSmall + ) { + const vectorLen = VECTOR_BYTES / prop.size + const { condition, offset } = createCondition(prop, op, VECTOR_BYTES) + let i = offset + for (let j = 0; j < vectorLen; j++) { + prop.write(condition, j >= val.length ? val[0] : val[j], i) + i += prop.size + } + return condition + } + + if (op === FilterOpCompare.range || op === FilterOpCompare.nrange) { + const { condition, offset } = createCondition(prop, op, prop.size * 2) + prop.write(condition, val[0], offset) + prop.write(condition, val[1] - val[0], offset + prop.size) + return condition + } + + const { condition, offset } = createCondition(prop, op) + prop.write(condition, val[0], offset) + return condition +} + +export const variableComparison = ( + prop: PropDef, + operator: Operator, + val: any[], + opts?: FilterOpts, +) => { + const op = operatorToEnum(operator, val, prop) + + if (op === FilterOpCompare.inc || op === FilterOpCompare.ninc) { + if (val.length === 1) { + const size = native.stringByteLength(val[0]) + const { condition, offset } = createCondition(prop, op, size) + ENCODER.encodeInto(val[0], condition.subarray(offset)) + return condition + } + } + + throw new Error( + `Filter comparison not supported "${operator}" ${prop.path.join('.')}`, + ) +} + +export const comparison = ( + prop: PropDef, + op: Operator, + val: any, + opts?: FilterOpts, +) => { + if (!Array.isArray(val)) { + val = [val] + } + if (prop.size > 0) { + return fixedComparison(prop, op, val, opts) + } + return variableComparison(prop, op, val, opts) +} diff --git a/src/db-query/ast/filter/condition.ts b/src/db-query/ast/filter/condition.ts deleted file mode 100644 index cd6d68f4d2..0000000000 --- a/src/db-query/ast/filter/condition.ts +++ /dev/null @@ -1,200 +0,0 @@ -import native from '../../../native.js' -import { PropDef } from '../../../schema/defs/index.js' -import { - FilterConditionByteSize, - FilterConditionAlignOf, - FilterOp, - FilterOpCompare, - writeFilterCondition, - ModifyEnum, - LangCodeEnum, - PropType, -} from '../../../zigTsExports.js' -import { FilterOpts, Operator } from '../ast.js' - -export const conditionByteSize = (propSize: number, size: number) => { - return size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propSize -} - -// this will be PUSH -export const conditionBuffer = ( - prop: { start: number; id: number; size: number }, - size: number, - op: FilterOp, -) => { - const condition = new Uint8Array(conditionByteSize(prop.size, size)) - condition[0] = 255 // Means condition header is not aligned - const offset = - writeFilterCondition( - condition, - { - op, - start: prop.start || 0, - prop: prop.id, - fieldSchema: 0, - len: prop.size, - offset: 255, // Means value is not aligned - size: size + prop.size, - }, - FilterConditionAlignOf + 1, - ) + prop.size - - return { condition, offset } -} - -const opMap: Partial> = { - '=': 'eq', - '!=': 'neq', - '>': 'gt', - '<': 'lt', - '>=': 'ge', - '<=': 'le', - includes: 'inc', - '!includes': 'ninc', -} - -// redo this whole thing - -const getFilterOp = ( - prop: PropDef, - operator: Operator, - value: any[], -): { - size: number - op: FilterOp - write: (buf: Uint8Array, val: any, offset: number) => void -} => { - const opName = opMap[operator] - - if (!opName) { - throw new Error(`un supported op ${operator}`) - } - - let write = (buf: Uint8Array, val: any, offset: number) => { - console.log('write', val) - prop.write(buf, val, offset) - } - - let size = prop.size - if (size === 0) { - if (prop.type === PropType.string) { - if (value.length === 1) { - size = native.stringByteLength(value[0]) - - console.log('SIZE', size) - - write = (buf: Uint8Array, val: any, offset: number) => { - const x = new TextEncoder() - x.encodeInto(val, buf.subarray(offset)) - } - // opName has to be eqCrc32 if EQ - } - } - } - - if ((opName === 'eq' || opName === 'neq') && value.length > 1) { - // incBatch, incBatchSmall - const vectorLen = 16 / size - if (value.length > vectorLen) { - return { - op: { - compare: FilterOpCompare[`${opName}Batch`], - prop: prop.type, - }, - size, - write, - } - } else { - return { - op: { - compare: FilterOpCompare[`${opName}BatchSmall`], - prop: prop.type, - }, - size: prop.size, - write, - } - } - } else if (operator === '..' || operator === '!..') { - return { - op: { - compare: FilterOpCompare.range, - prop: prop.type, - }, - size: size * 2, - write: (condition: Uint8Array, v: any, offset: number) => { - // x >= 3 && x <= 11 - // (x -% 3) <= (11 - 3) - prop.write(condition, v[0], offset) - prop.write(condition, v[1] - v[0], offset + prop.size) - return condition - }, - } - } else { - return { - op: { - compare: FilterOpCompare[opName], - prop: prop.type, - }, - size, - write, - } - } -} - -export const createCondition = ( - prop: PropDef, - operator: Operator, - value?: any, - opts?: FilterOpts, -) => { - if (value !== undefined && !(value instanceof Array)) { - value = [value] - } - - const { op, size, write } = getFilterOp(prop, operator, value) - - // this is fixed make fixed and variable in a file - - const vectorLen = 16 / size - - if (value.length == 1 || operator === '..' || operator == '!..') { - const { condition, offset } = conditionBuffer( - { size: prop.size, start: prop.start, id: prop.id }, - size, - op, - ) - if (operator === '..' || operator == '!..') { - write(condition, value, offset) - } else { - write(condition, value[0], offset) - } - return condition - } else if (value.length > vectorLen) { - // only relevant for eq and neq - const { condition, offset } = conditionBuffer(prop, value.length * size, op) - let i = offset - // Actual values - for (const v of value) { - write(condition, v, i) - i += prop.size - } - // Empty padding for SIMD (16 bytes) - for (let j = 0; j < vectorLen; j++) { - write(condition, value[0], i) - i += size - } - return condition - } else if (value.length > 1) { - // Small batch - const { condition, offset } = conditionBuffer(prop, value.length * size, op) - let i = offset - for (let j = 0; j < vectorLen; j++) { - // Allways use a full ARM neon simd vector (16 bytes) - write(condition, j >= value.length ? value[0] : value[j], i) - i += size - } - return condition - } - - throw new Error('Cannot create filter cond') -} diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index 277c16df3c..fc5a53f628 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -4,21 +4,15 @@ import { PropTree, TypeDef, } from '../../../schema/defs/index.js' -import { debugBuffer } from '../../../sdk.js' -import { concatUint8Arr, writeUint64 } from '../../../utils/uint8.js' +import { writeUint64 } from '../../../utils/uint8.js' import { FilterConditionAlignOf, FilterOpCompare, - ID_PROP, PropType, writeFilterConditionProps, } from '../../../zigTsExports.js' import { Ctx, FilterAst, FilterOp } from '../ast.js' -import { - conditionBuffer, - conditionByteSize, - createCondition, -} from './condition.js' +import { comparison, conditionByteSize, createCondition } from './comparison.js' type WalkCtx = { tree: PropTree @@ -33,13 +27,12 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { const prop = tree.get(field) const astProp = ast.props[field] const ops = astProp.ops - - // AND & OR if (isPropDef(prop)) { if (prop.type === PropType.references) { // references(astProp, ctx, prop) } else if (prop.type === PropType.reference) { // this can be added here + // need this again... // reference(astProp, ctx, prop) } else if (ops) { if (prop.id === 0) { @@ -48,7 +41,7 @@ const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { walkCtx.prop = prop.id for (const op of ops) { // can prob just push this directly - const condition = createCondition(prop, op.op, op.val, op.opts) + const condition = comparison(prop, op.op, op.val, op.opts) ctx.query.set(condition, ctx.query.length) } } @@ -116,24 +109,23 @@ export const filter = ( const { main } = walk(ast, ctx, typeDef, walkCtx) for (const { prop, ops } of main) { - // better to do main first scince they are usualy lighter filters... walkCtx.prop = prop.id for (const op of ops) { - const condition = createCondition(prop, op.op, op.val, op.opts) + const condition = comparison(prop, op.op, op.val, op.opts) ctx.query.set(condition, ctx.query.length) } } if (ast.and) { if (ast.or) { - const { offset, condition } = conditionBuffer( + const { offset, condition } = createCondition( { id: PropType.id, size: 8, start: 0, + type: PropType.null, }, - 8, - { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, + FilterOpCompare.nextOrIndex, ) writeUint64( condition, @@ -158,13 +150,11 @@ export const filter = ( const resultSize = ctx.query.length - startIndex const nextOrIndex = resultSize + filterIndex - const { offset, condition } = conditionBuffer( - { id: lastProp, size: 8, start: 0 }, - 8, - { compare: FilterOpCompare.nextOrIndex, prop: PropType.null }, + const { offset, condition } = createCondition( + { id: lastProp, size: 8, start: 0, type: PropType.null }, + FilterOpCompare.nextOrIndex, ) - console.dir(ast.or, { depth: 10 }) writeUint64(condition, nextOrIndex, offset) ctx.query.set(condition, startIndex) @@ -177,6 +167,7 @@ export const filter = ( } if (andOrReplace) { + // REMOVE THIS! let index = indexOf( ctx.query.data, andOrReplace, diff --git a/src/db-query/ast/filter/operatorToEnum.ts b/src/db-query/ast/filter/operatorToEnum.ts new file mode 100644 index 0000000000..2dae54c5d3 --- /dev/null +++ b/src/db-query/ast/filter/operatorToEnum.ts @@ -0,0 +1,62 @@ +import { FilterOpCompareEnum, FilterOpCompare } from '../../../zigTsExports.js' +import { Operator } from '../ast.js' +import { PropDef } from '../../../schema/defs/index.js' + +export const operatorToEnum = ( + op: Operator, + val: any[], + prop: PropDef, +): FilterOpCompareEnum => { + const size = prop.size + const vectorLen = 16 / size + + if (op === '=' && val.length === 1) { + return FilterOpCompare.eq + } + + if (op === '=' && val.length > vectorLen) { + return FilterOpCompare.eqBatch + } + + if (op === '=' && val.length <= vectorLen) { + return FilterOpCompare.eqBatchSmall + } + + if (op === '!=' && val.length === 1) { + return FilterOpCompare.neq + } + + if (op === '!=' && val.length > vectorLen) { + return FilterOpCompare.neqBatch + } + + if (op === '!=' && val.length <= vectorLen) { + return FilterOpCompare.neqBatchSmall + } + + if (op === '>') { + return FilterOpCompare.gt + } + + if (op === '>=') { + return FilterOpCompare.ge + } + + if (op === '<') { + return FilterOpCompare.lt + } + + if (op === '<=') { + return FilterOpCompare.le + } + + if (op === 'includes') { + return FilterOpCompare.inc + } + + if (op === '!includes') { + return FilterOpCompare.ninc + } + + throw new Error(`Unsupported compare operator ${op}`) +} diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 39f2bb705d..08ee41aeb9 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -1,20 +1,12 @@ -import { - writeUint16, - writeInt16, - writeUint32, - writeInt32, - writeUint64, - writeInt64, - writeFloatLE, - writeDoubleLE, - readUint16, - readInt16, - readUint32, - readInt32, - readUint64, - readInt64, - readFloatLE, - readDoubleLE, +import { + writeUint16, writeInt16, + writeUint32, writeInt32, + writeUint64, writeInt64, + writeFloatLE, writeDoubleLE, + readUint16, readInt16, + readUint32, readInt32, + readUint64, readInt64, + readFloatLE, readDoubleLE } from './utils/index.js' import { AutoSizedUint8Array } from './utils/AutoSizedUint8Array.js' @@ -44,8 +36,7 @@ export const BridgeResponseInverse = { flushQuery, flushModify */ -export type BridgeResponseEnum = - (typeof BridgeResponse)[keyof typeof BridgeResponse] +export type BridgeResponseEnum = (typeof BridgeResponse)[keyof typeof BridgeResponse] export const OpType = { id: 0, @@ -275,7 +266,7 @@ export const readModifyHeader = ( ): ModifyHeader => { const value: ModifyHeader = { opId: readUint32(buf, offset), - opType: buf[offset + 4] as OpTypeEnum, + opType: (buf[offset + 4]) as OpTypeEnum, schema: readUint64(buf, offset + 5), count: readUint32(buf, offset + 13), } @@ -283,10 +274,10 @@ export const readModifyHeader = ( } export const readModifyHeaderProps = { - opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - opType: (buf: Uint8Array, offset: number) => buf[offset + 4] as OpTypeEnum, - schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), - count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), + opId: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + opType: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as OpTypeEnum, + schema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 5), + count: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 13), } export const createModifyHeader = (header: ModifyHeader): Uint8Array => { @@ -362,9 +353,9 @@ export const readModifyUpdateHeader = ( offset: number, ): ModifyUpdateHeader => { const value: ModifyUpdateHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, - isTmp: ((buf[offset + 3] >>> 0) & 1) === 1, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, + isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, id: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -372,18 +363,14 @@ export const readModifyUpdateHeader = ( } export const readModifyUpdateHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createModifyUpdateHeader = ( - header: ModifyUpdateHeader, -): Uint8Array => { +export const createModifyUpdateHeader = (header: ModifyUpdateHeader): Uint8Array => { const buffer = new Uint8Array(ModifyUpdateHeaderByteSize) writeModifyUpdateHeader(buffer, header, 0) return buffer @@ -453,26 +440,22 @@ export const readModifyDeleteHeader = ( offset: number, ): ModifyDeleteHeader => { const value: ModifyDeleteHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, - isTmp: ((buf[offset + 3] >>> 0) & 1) === 1, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, + isTmp: (((buf[offset + 3] >>> 0) & 1)) === 1, id: readUint32(buf, offset + 4), } return value } export const readModifyDeleteHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), } -export const createModifyDeleteHeader = ( - header: ModifyDeleteHeader, -): Uint8Array => { +export const createModifyDeleteHeader = (header: ModifyDeleteHeader): Uint8Array => { const buffer = new Uint8Array(ModifyDeleteHeaderByteSize) writeModifyDeleteHeader(buffer, header, 0) return buffer @@ -533,23 +516,20 @@ export const readModifyCreateHeader = ( offset: number, ): ModifyCreateHeader => { const value: ModifyCreateHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, size: readUint32(buf, offset + 3), } return value } export const readModifyCreateHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), } -export const createModifyCreateHeader = ( - header: ModifyCreateHeader, -): Uint8Array => { +export const createModifyCreateHeader = (header: ModifyCreateHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCreateHeaderByteSize) writeModifyCreateHeader(buffer, header, 0) return buffer @@ -613,8 +593,8 @@ export const readModifyCreateRingHeader = ( offset: number, ): ModifyCreateRingHeader => { const value: ModifyCreateRingHeader = { - op: buf[offset] as ModifyEnum, - type: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as ModifyEnum, + type: (readUint16(buf, offset + 1)) as TypeId, maxNodeId: readUint32(buf, offset + 3), size: readUint32(buf, offset + 7), } @@ -622,16 +602,13 @@ export const readModifyCreateRingHeader = ( } export const readModifyCreateRingHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyEnum, - type: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyEnum, + type: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + maxNodeId: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } -export const createModifyCreateRingHeader = ( - header: ModifyCreateRingHeader, -): Uint8Array => { +export const createModifyCreateRingHeader = (header: ModifyCreateRingHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCreateRingHeaderByteSize) writeModifyCreateRingHeader(buffer, header, 0) return buffer @@ -666,8 +643,7 @@ export const ModifyIncrementInverse = { increment, decrement */ -export type ModifyIncrementEnum = - (typeof ModifyIncrement)[keyof typeof ModifyIncrement] +export type ModifyIncrementEnum = (typeof ModifyIncrement)[keyof typeof ModifyIncrement] export type ModifyMainHeader = { id: number @@ -723,8 +699,8 @@ export const readModifyMainHeader = ( ): ModifyMainHeader => { const value: ModifyMainHeader = { id: buf[offset], - type: buf[offset + 1] as PropTypeEnum, - increment: buf[offset + 2] as ModifyIncrementEnum, + type: (buf[offset + 1]) as PropTypeEnum, + increment: (buf[offset + 2]) as ModifyIncrementEnum, size: buf[offset + 3], start: readUint16(buf, offset + 4), } @@ -732,17 +708,14 @@ export const readModifyMainHeader = ( } export const readModifyMainHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, - increment: (buf: Uint8Array, offset: number) => - buf[offset + 2] as ModifyIncrementEnum, - size: (buf: Uint8Array, offset: number) => buf[offset + 3], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + increment: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as ModifyIncrementEnum, + size: (buf: Uint8Array, offset: number) => buf[offset + 3], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), } -export const createModifyMainHeader = ( - header: ModifyMainHeader, -): Uint8Array => { +export const createModifyMainHeader = (header: ModifyMainHeader): Uint8Array => { const buffer = new Uint8Array(ModifyMainHeaderByteSize) writeModifyMainHeader(buffer, header, 0) return buffer @@ -803,21 +776,19 @@ export const readModifyPropHeader = ( ): ModifyPropHeader => { const value: ModifyPropHeader = { id: buf[offset], - type: buf[offset + 1] as PropTypeEnum, + type: (buf[offset + 1]) as PropTypeEnum, size: readUint32(buf, offset + 2), } return value } export const readModifyPropHeaderProps = { - id: (buf: Uint8Array, offset: number) => buf[offset], - type: (buf: Uint8Array, offset: number) => buf[offset + 1] as PropTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + id: (buf: Uint8Array, offset: number) => buf[offset], + type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createModifyPropHeader = ( - header: ModifyPropHeader, -): Uint8Array => { +export const createModifyPropHeader = (header: ModifyPropHeader): Uint8Array => { const buffer = new Uint8Array(ModifyPropHeaderByteSize) writeModifyPropHeader(buffer, header, 0) return buffer @@ -860,8 +831,7 @@ export const ModifyReferencesInverse = { delIds, delTmpIds */ -export type ModifyReferencesEnum = - (typeof ModifyReferences)[keyof typeof ModifyReferences] +export type ModifyReferencesEnum = (typeof ModifyReferences)[keyof typeof ModifyReferences] export type ModifyReferencesHeader = { op: ModifyReferencesEnum @@ -898,20 +868,18 @@ export const readModifyReferencesHeader = ( offset: number, ): ModifyReferencesHeader => { const value: ModifyReferencesHeader = { - op: buf[offset] as ModifyReferencesEnum, + op: (buf[offset]) as ModifyReferencesEnum, size: readUint32(buf, offset + 1), } return value } export const readModifyReferencesHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ModifyReferencesEnum, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ModifyReferencesEnum, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } -export const createModifyReferencesHeader = ( - header: ModifyReferencesHeader, -): Uint8Array => { +export const createModifyReferencesHeader = (header: ModifyReferencesHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesHeaderByteSize) writeModifyReferencesHeader(buffer, header, 0) return buffer @@ -982,8 +950,8 @@ export const readModifyReferencesMetaHeader = ( ): ModifyReferencesMetaHeader => { const value: ModifyReferencesMetaHeader = { id: readUint32(buf, offset), - isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, - withIndex: ((buf[offset + 4] >>> 1) & 1) === 1, + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (((buf[offset + 4] >>> 1) & 1)) === 1, index: readUint32(buf, offset + 5), size: readUint32(buf, offset + 9), } @@ -991,18 +959,14 @@ export const readModifyReferencesMetaHeader = ( } export const readModifyReferencesMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - withIndex: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 1) & 1) === 1, - index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + withIndex: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + index: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 9), } -export const createModifyReferencesMetaHeader = ( - header: ModifyReferencesMetaHeader, -): Uint8Array => { +export const createModifyReferencesMetaHeader = (header: ModifyReferencesMetaHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferencesMetaHeaderByteSize) writeModifyReferencesMetaHeader(buffer, header, 0) return buffer @@ -1067,22 +1031,19 @@ export const readModifyReferenceMetaHeader = ( ): ModifyReferenceMetaHeader => { const value: ModifyReferenceMetaHeader = { id: readUint32(buf, offset), - isTmp: ((buf[offset + 4] >>> 0) & 1) === 1, + isTmp: (((buf[offset + 4] >>> 0) & 1)) === 1, size: readUint32(buf, offset + 5), } return value } export const readModifyReferenceMetaHeaderProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isTmp: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isTmp: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), } -export const createModifyReferenceMetaHeader = ( - header: ModifyReferenceMetaHeader, -): Uint8Array => { +export const createModifyReferenceMetaHeader = (header: ModifyReferenceMetaHeader): Uint8Array => { const buffer = new Uint8Array(ModifyReferenceMetaHeaderByteSize) writeModifyReferenceMetaHeader(buffer, header, 0) return buffer @@ -1110,18 +1071,14 @@ export const ModifyCardinalityHeaderByteSize = 2 export const ModifyCardinalityHeaderAlignOf = 2 -export const packModifyCardinalityHeader = ( - obj: ModifyCardinalityHeader, -): number => { +export const packModifyCardinalityHeader = (obj: ModifyCardinalityHeader): number => { let val = 0 val |= ((obj.sparse ? 1 : 0) & 1) << 0 val |= (Number(obj.precision) & 255) << 8 return val } -export const unpackModifyCardinalityHeader = ( - val: number, -): ModifyCardinalityHeader => { +export const unpackModifyCardinalityHeader = (val: number): ModifyCardinalityHeader => { return { sparse: ((val >>> 0) & 1) === 1, precision: Number((val >>> 8) & 255), @@ -1156,20 +1113,18 @@ export const readModifyCardinalityHeader = ( offset: number, ): ModifyCardinalityHeader => { const value: ModifyCardinalityHeader = { - sparse: ((buf[offset] >>> 0) & 1) === 1, + sparse: (((buf[offset] >>> 0) & 1)) === 1, precision: buf[offset + 1], } return value } export const readModifyCardinalityHeaderProps = { - sparse: (buf: Uint8Array, offset: number) => ((buf[offset] >>> 0) & 1) === 1, - precision: (buf: Uint8Array, offset: number) => buf[offset + 1], + sparse: (buf: Uint8Array, offset: number) => (((buf[offset] >>> 0) & 1)) === 1, + precision: (buf: Uint8Array, offset: number) => buf[offset + 1], } -export const createModifyCardinalityHeader = ( - header: ModifyCardinalityHeader, -): Uint8Array => { +export const createModifyCardinalityHeader = (header: ModifyCardinalityHeader): Uint8Array => { const buffer = new Uint8Array(ModifyCardinalityHeaderByteSize) writeModifyCardinalityHeader(buffer, header, 0) return buffer @@ -1223,19 +1178,17 @@ export const readModifyResultItem = ( ): ModifyResultItem => { const value: ModifyResultItem = { id: readUint32(buf, offset), - err: buf[offset + 4] as ModifyErrorEnum, + err: (buf[offset + 4]) as ModifyErrorEnum, } return value } export const readModifyResultItemProps = { - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - err: (buf: Uint8Array, offset: number) => buf[offset + 4] as ModifyErrorEnum, + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + err: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as ModifyErrorEnum, } -export const createModifyResultItem = ( - header: ModifyResultItem, -): Uint8Array => { +export const createModifyResultItem = (header: ModifyResultItem): Uint8Array => { const buffer = new Uint8Array(ModifyResultItemByteSize) writeModifyResultItem(buffer, header, 0) return buffer @@ -1399,8 +1352,7 @@ export const PropTypeSelvaInverse = { aliases, colVec */ -export type PropTypeSelvaEnum = - (typeof PropTypeSelva)[keyof typeof PropTypeSelva] +export type PropTypeSelvaEnum = (typeof PropTypeSelva)[keyof typeof PropTypeSelva] export const RefOp = { clear: 0, @@ -1475,8 +1427,7 @@ export const ReferencesSelectInverse = { any, all */ -export type ReferencesSelectEnum = - (typeof ReferencesSelect)[keyof typeof ReferencesSelect] +export type ReferencesSelectEnum = (typeof ReferencesSelect)[keyof typeof ReferencesSelect] export const RefEdgeOp = { noEdgeNoIndexRealId: 0, @@ -2209,28 +2160,30 @@ export const writeSortHeaderProps = { }, } -export const readSortHeader = (buf: Uint8Array, offset: number): SortHeader => { +export const readSortHeader = ( + buf: Uint8Array, + offset: number, +): SortHeader => { const value: SortHeader = { - order: buf[offset] as OrderEnum, + order: (buf[offset]) as OrderEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, start: readUint16(buf, offset + 3), len: readUint16(buf, offset + 5), - lang: buf[offset + 7] as LangCodeEnum, + lang: (buf[offset + 7]) as LangCodeEnum, edgeType: readUint16(buf, offset + 8), } return value } export const readSortHeaderProps = { - order: (buf: Uint8Array, offset: number) => buf[offset] as OrderEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - lang: (buf: Uint8Array, offset: number) => buf[offset + 7] as LangCodeEnum, - edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + order: (buf: Uint8Array, offset: number) => (buf[offset]) as OrderEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + lang: (buf: Uint8Array, offset: number) => (buf[offset + 7]) as LangCodeEnum, + edgeType: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } export const createSortHeader = (header: SortHeader): Uint8Array => { @@ -2364,8 +2317,7 @@ export const QueryIteratorTypeInverse = { groupBy, groupByFilter */ -export type QueryIteratorTypeEnum = - (typeof QueryIteratorType)[keyof typeof QueryIteratorType] +export type QueryIteratorTypeEnum = (typeof QueryIteratorType)[keyof typeof QueryIteratorType] export const QueryType = { id: 0, @@ -2510,18 +2462,17 @@ export const readIncludeHeader = ( offset: number, ): IncludeHeader => { const value: IncludeHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, } return value } export const readIncludeHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, } export const createIncludeHeader = (header: IncludeHeader): Uint8Array => { @@ -2598,23 +2549,20 @@ export const readIncludeMetaHeader = ( offset: number, ): IncludeMetaHeader => { const value: IncludeMetaHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, } return value } export const readIncludeMetaHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, } -export const createIncludeMetaHeader = ( - header: IncludeMetaHeader, -): Uint8Array => { +export const createIncludeMetaHeader = (header: IncludeMetaHeader): Uint8Array => { const buffer = new Uint8Array(IncludeMetaHeaderByteSize) writeIncludeMetaHeader(buffer, header, 0) return buffer @@ -2678,25 +2626,22 @@ export const readIncludePartialHeader = ( offset: number, ): IncludePartialHeader => { const value: IncludePartialHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, prop: buf[offset + 1], - propType: buf[offset + 2] as PropTypeEnum, + propType: (buf[offset + 2]) as PropTypeEnum, amount: readUint16(buf, offset + 3), } return value } export const readIncludePartialHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 2] as PropTypeEnum, - amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as PropTypeEnum, + amount: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), } -export const createIncludePartialHeader = ( - header: IncludePartialHeader, -): Uint8Array => { +export const createIncludePartialHeader = (header: IncludePartialHeader): Uint8Array => { const buffer = new Uint8Array(IncludePartialHeaderByteSize) writeIncludePartialHeader(buffer, header, 0) return buffer @@ -2770,13 +2715,11 @@ export const readIncludePartialProp = ( } export const readIncludePartialPropProps = { - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), } -export const createIncludePartialProp = ( - header: IncludePartialProp, -): Uint8Array => { +export const createIncludePartialProp = (header: IncludePartialProp): Uint8Array => { const buffer = new Uint8Array(IncludePartialPropByteSize) writeIncludePartialProp(buffer, header, 0) return buffer @@ -2847,22 +2790,20 @@ export const readIncludeOpts = ( ): IncludeOpts => { const value: IncludeOpts = { end: readUint32(buf, offset), - isChars: ((buf[offset + 4] >>> 0) & 1) === 1, - hasOpts: ((buf[offset + 4] >>> 1) & 1) === 1, + isChars: (((buf[offset + 4] >>> 0) & 1)) === 1, + hasOpts: (((buf[offset + 4] >>> 1) & 1)) === 1, langFallbackSize: buf[offset + 5], - lang: buf[offset + 6] as LangCodeEnum, + lang: (buf[offset + 6]) as LangCodeEnum, } return value } export const readIncludeOptsProps = { - end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - isChars: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 0) & 1) === 1, - hasOpts: (buf: Uint8Array, offset: number) => - ((buf[offset + 4] >>> 1) & 1) === 1, - langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], - lang: (buf: Uint8Array, offset: number) => buf[offset + 6] as LangCodeEnum, + end: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + isChars: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 0) & 1)) === 1, + hasOpts: (buf: Uint8Array, offset: number) => (((buf[offset + 4] >>> 1) & 1)) === 1, + langFallbackSize: (buf: Uint8Array, offset: number) => buf[offset + 5], + lang: (buf: Uint8Array, offset: number) => (buf[offset + 6]) as LangCodeEnum, } export const createIncludeOpts = (header: IncludeOpts): Uint8Array => { @@ -2928,8 +2869,8 @@ export const readIncludeResponse = ( } export const readIncludeResponseProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset], - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), + prop: (buf: Uint8Array, offset: number) => buf[offset], + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 1), } export const createIncludeResponse = (header: IncludeResponse): Uint8Array => { @@ -3009,10 +2950,10 @@ export const readIncludeResponseMeta = ( offset: number, ): IncludeResponseMeta => { const value: IncludeResponseMeta = { - op: buf[offset] as ReadOpEnum, + op: (buf[offset]) as ReadOpEnum, prop: buf[offset + 1], - lang: buf[offset + 2] as LangCodeEnum, - compressed: ((buf[offset + 3] >>> 0) & 1) === 1, + lang: (buf[offset + 2]) as LangCodeEnum, + compressed: (((buf[offset + 3] >>> 0) & 1)) === 1, crc32: readUint32(buf, offset + 4), size: readUint32(buf, offset + 8), } @@ -3020,18 +2961,15 @@ export const readIncludeResponseMeta = ( } export const readIncludeResponseMetaProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as ReadOpEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - lang: (buf: Uint8Array, offset: number) => buf[offset + 2] as LangCodeEnum, - compressed: (buf: Uint8Array, offset: number) => - ((buf[offset + 3] >>> 0) & 1) === 1, - crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as ReadOpEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + lang: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as LangCodeEnum, + compressed: (buf: Uint8Array, offset: number) => (((buf[offset + 3] >>> 0) & 1)) === 1, + crc32: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 8), } -export const createIncludeResponseMeta = ( - header: IncludeResponseMeta, -): Uint8Array => { +export const createIncludeResponseMeta = (header: IncludeResponseMeta): Uint8Array => { const buffer = new Uint8Array(IncludeResponseMetaByteSize) writeIncludeResponseMeta(buffer, header, 0) return buffer @@ -3100,8 +3038,8 @@ export const readSubscriptionHeader = ( offset: number, ): SubscriptionHeader => { const value: SubscriptionHeader = { - op: buf[offset] as OpTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as OpTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, fieldsLen: buf[offset + 3], partialLen: buf[offset + 4], } @@ -3109,16 +3047,13 @@ export const readSubscriptionHeader = ( } export const readSubscriptionHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as OpTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], - partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], + op: (buf: Uint8Array, offset: number) => (buf[offset]) as OpTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + fieldsLen: (buf: Uint8Array, offset: number) => buf[offset + 3], + partialLen: (buf: Uint8Array, offset: number) => buf[offset + 4], } -export const createSubscriptionHeader = ( - header: SubscriptionHeader, -): Uint8Array => { +export const createSubscriptionHeader = (header: SubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(SubscriptionHeaderByteSize) writeSubscriptionHeader(buffer, header, 0) return buffer @@ -3229,11 +3164,7 @@ export const writeQueryHeaderProps = { includeSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 22) }, - iteratorType: ( - buf: Uint8Array, - value: QueryIteratorTypeEnum, - offset: number, - ) => { + iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { buf[offset + 24] = Number(value) }, size: (buf: Uint8Array, value: number, offset: number) => { @@ -3249,10 +3180,10 @@ export const readQueryHeader = ( offset: number, ): QueryHeader => { const value: QueryHeader = { - op: buf[offset] as QueryTypeEnum, + op: (buf[offset]) as QueryTypeEnum, prop: buf[offset + 1], - typeId: readUint16(buf, offset + 2) as TypeId, - edgeTypeId: readUint16(buf, offset + 4) as TypeId, + typeId: (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, offset: readUint32(buf, offset + 6), limit: readUint32(buf, offset + 10), filterSize: readUint16(buf, offset + 14), @@ -3260,34 +3191,28 @@ export const readQueryHeader = ( edgeSize: readUint16(buf, offset + 18), edgeFilterSize: readUint16(buf, offset + 20), includeSize: readUint16(buf, offset + 22), - iteratorType: buf[offset + 24] as QueryIteratorTypeEnum, + iteratorType: (buf[offset + 24]) as QueryIteratorTypeEnum, size: readUint16(buf, offset + 25), - sort: ((buf[offset + 27] >>> 0) & 1) === 1, + sort: (((buf[offset + 27] >>> 0) & 1)) === 1, } return value } export const readQueryHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 4) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), - searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), - edgeFilterSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 20), - includeSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 22), - iteratorType: (buf: Uint8Array, offset: number) => - buf[offset + 24] as QueryIteratorTypeEnum, - size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), - sort: (buf: Uint8Array, offset: number) => - ((buf[offset + 27] >>> 0) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 6), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 10), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + searchSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 18), + edgeFilterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 20), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 22), + iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 24]) as QueryIteratorTypeEnum, + size: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 25), + sort: (buf: Uint8Array, offset: number) => (((buf[offset + 27] >>> 0) & 1)) === 1, } export const createQueryHeader = (header: QueryHeader): Uint8Array => { @@ -3385,8 +3310,8 @@ export const readQueryHeaderSingle = ( offset: number, ): QueryHeaderSingle => { const value: QueryHeaderSingle = { - op: buf[offset] as QueryTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as QueryTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, prop: buf[offset + 3], id: readUint32(buf, offset + 4), filterSize: readUint16(buf, offset + 8), @@ -3397,20 +3322,16 @@ export const readQueryHeaderSingle = ( } export const readQueryHeaderSingleProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - prop: (buf: Uint8Array, offset: number) => buf[offset + 3], - id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - includeSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 10), - aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), -} - -export const createQueryHeaderSingle = ( - header: QueryHeaderSingle, -): Uint8Array => { + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + prop: (buf: Uint8Array, offset: number) => buf[offset + 3], + id: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 4), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), + aliasSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 12), +} + +export const createQueryHeaderSingle = (header: QueryHeaderSingle): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleByteSize) writeQueryHeaderSingle(buffer, header, 0) return buffer @@ -3490,10 +3411,10 @@ export const readQueryHeaderSingleReference = ( offset: number, ): QueryHeaderSingleReference => { const value: QueryHeaderSingleReference = { - op: buf[offset] as QueryTypeEnum, + op: (buf[offset]) as QueryTypeEnum, prop: buf[offset + 1], - typeId: readUint16(buf, offset + 2) as TypeId, - edgeTypeId: readUint16(buf, offset + 4) as TypeId, + typeId: (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (readUint16(buf, offset + 4)) as TypeId, edgeSize: readUint16(buf, offset + 6), includeSize: readUint16(buf, offset + 8), } @@ -3501,19 +3422,15 @@ export const readQueryHeaderSingleReference = ( } export const readQueryHeaderSingleReferenceProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - prop: (buf: Uint8Array, offset: number) => buf[offset + 1], - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - edgeTypeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 4) as TypeId, - edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + prop: (buf: Uint8Array, offset: number) => buf[offset + 1], + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + edgeTypeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 4)) as TypeId, + edgeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + includeSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), } -export const createQueryHeaderSingleReference = ( - header: QueryHeaderSingleReference, -): Uint8Array => { +export const createQueryHeaderSingleReference = (header: QueryHeaderSingleReference): Uint8Array => { const buffer = new Uint8Array(QueryHeaderSingleReferenceByteSize) writeQueryHeaderSingleReference(buffer, header, 0) return buffer @@ -3565,8 +3482,7 @@ export const VectorBaseTypeInverse = { float32, float64 */ -export type VectorBaseTypeEnum = - (typeof VectorBaseType)[keyof typeof VectorBaseType] +export type VectorBaseTypeEnum = (typeof VectorBaseType)[keyof typeof VectorBaseType] export type AggHeader = { op: QueryTypeEnum @@ -3630,11 +3546,7 @@ export const writeAggHeaderProps = { filterSize: (buf: Uint8Array, value: number, offset: number) => { writeUint16(buf, Number(value), offset + 11) }, - iteratorType: ( - buf: Uint8Array, - value: QueryIteratorTypeEnum, - offset: number, - ) => { + iteratorType: (buf: Uint8Array, value: QueryIteratorTypeEnum, offset: number) => { buf[offset + 13] = Number(value) }, resultsSize: (buf: Uint8Array, value: number, offset: number) => { @@ -3651,39 +3563,36 @@ export const writeAggHeaderProps = { }, } -export const readAggHeader = (buf: Uint8Array, offset: number): AggHeader => { +export const readAggHeader = ( + buf: Uint8Array, + offset: number, +): AggHeader => { const value: AggHeader = { - op: buf[offset] as QueryTypeEnum, - typeId: readUint16(buf, offset + 1) as TypeId, + op: (buf[offset]) as QueryTypeEnum, + typeId: (readUint16(buf, offset + 1)) as TypeId, offset: readUint32(buf, offset + 3), limit: readUint32(buf, offset + 7), filterSize: readUint16(buf, offset + 11), - iteratorType: buf[offset + 13] as QueryIteratorTypeEnum, + iteratorType: (buf[offset + 13]) as QueryIteratorTypeEnum, resultsSize: readUint16(buf, offset + 14), accumulatorSize: readUint16(buf, offset + 16), - hasGroupBy: ((buf[offset + 18] >>> 0) & 1) === 1, - isSamplingSet: ((buf[offset + 18] >>> 1) & 1) === 1, + hasGroupBy: (((buf[offset + 18] >>> 0) & 1)) === 1, + isSamplingSet: (((buf[offset + 18] >>> 1) & 1)) === 1, } return value } export const readAggHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as QueryTypeEnum, - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 1) as TypeId, - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), - limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), - iteratorType: (buf: Uint8Array, offset: number) => - buf[offset + 13] as QueryIteratorTypeEnum, - resultsSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 14), - accumulatorSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 16), - hasGroupBy: (buf: Uint8Array, offset: number) => - ((buf[offset + 18] >>> 0) & 1) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => - ((buf[offset + 18] >>> 1) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as QueryTypeEnum, + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 1)) as TypeId, + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 3), + limit: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 11), + iteratorType: (buf: Uint8Array, offset: number) => (buf[offset + 13]) as QueryIteratorTypeEnum, + resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 14), + accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 16), + hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 18] >>> 0) & 1)) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 18] >>> 1) & 1)) === 1, } export const createAggHeader = (header: AggHeader): Uint8Array => { @@ -3784,30 +3693,27 @@ export const readAggRefsHeader = ( offset: number, ): AggRefsHeader => { const value: AggRefsHeader = { - op: buf[offset] as IncludeOpEnum, + op: (buf[offset]) as IncludeOpEnum, targetProp: buf[offset + 1], offset: readUint32(buf, offset + 2), filterSize: readUint16(buf, offset + 6), resultsSize: readUint16(buf, offset + 8), accumulatorSize: readUint16(buf, offset + 10), - hasGroupBy: ((buf[offset + 12] >>> 0) & 1) === 1, - isSamplingSet: ((buf[offset + 12] >>> 1) & 1) === 1, + hasGroupBy: (((buf[offset + 12] >>> 0) & 1)) === 1, + isSamplingSet: (((buf[offset + 12] >>> 1) & 1)) === 1, } return value } export const readAggRefsHeaderProps = { - op: (buf: Uint8Array, offset: number) => buf[offset] as IncludeOpEnum, - targetProp: (buf: Uint8Array, offset: number) => buf[offset + 1], - offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), - resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), - accumulatorSize: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 10), - hasGroupBy: (buf: Uint8Array, offset: number) => - ((buf[offset + 12] >>> 0) & 1) === 1, - isSamplingSet: (buf: Uint8Array, offset: number) => - ((buf[offset + 12] >>> 1) & 1) === 1, + op: (buf: Uint8Array, offset: number) => (buf[offset]) as IncludeOpEnum, + targetProp: (buf: Uint8Array, offset: number) => buf[offset + 1], + offset: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + filterSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 6), + resultsSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 8), + accumulatorSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 10), + hasGroupBy: (buf: Uint8Array, offset: number) => (((buf[offset + 12] >>> 0) & 1)) === 1, + isSamplingSet: (buf: Uint8Array, offset: number) => (((buf[offset + 12] >>> 1) & 1)) === 1, } export const createAggRefsHeader = (header: AggRefsHeader): Uint8Array => { @@ -3842,17 +3748,13 @@ export const addMultiSubscriptionHeaderByteSize = 2 export const addMultiSubscriptionHeaderAlignOf = 2 -export const packaddMultiSubscriptionHeader = ( - obj: addMultiSubscriptionHeader, -): number => { +export const packaddMultiSubscriptionHeader = (obj: addMultiSubscriptionHeader): number => { let val = 0 val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackaddMultiSubscriptionHeader = ( - val: number, -): addMultiSubscriptionHeader => { +export const unpackaddMultiSubscriptionHeader = (val: number): addMultiSubscriptionHeader => { return { typeId: Number((val >>> 0) & 65535), } @@ -3885,12 +3787,10 @@ export const readaddMultiSubscriptionHeader = ( } export const readaddMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createaddMultiSubscriptionHeader = ( - header: addMultiSubscriptionHeader, -): Uint8Array => { +export const createaddMultiSubscriptionHeader = (header: addMultiSubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(addMultiSubscriptionHeaderByteSize) writeaddMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -3913,17 +3813,13 @@ export const removeMultiSubscriptionHeaderByteSize = 2 export const removeMultiSubscriptionHeaderAlignOf = 2 -export const packremoveMultiSubscriptionHeader = ( - obj: removeMultiSubscriptionHeader, -): number => { +export const packremoveMultiSubscriptionHeader = (obj: removeMultiSubscriptionHeader): number => { let val = 0 val |= (Number(obj.typeId) & 65535) << 0 return val } -export const unpackremoveMultiSubscriptionHeader = ( - val: number, -): removeMultiSubscriptionHeader => { +export const unpackremoveMultiSubscriptionHeader = (val: number): removeMultiSubscriptionHeader => { return { typeId: Number((val >>> 0) & 65535), } @@ -3956,12 +3852,10 @@ export const readremoveMultiSubscriptionHeader = ( } export const readremoveMultiSubscriptionHeaderProps = { - typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), + typeId: (buf: Uint8Array, offset: number) => readUint16(buf, offset), } -export const createremoveMultiSubscriptionHeader = ( - header: removeMultiSubscriptionHeader, -): Uint8Array => { +export const createremoveMultiSubscriptionHeader = (header: removeMultiSubscriptionHeader): Uint8Array => { const buffer = new Uint8Array(removeMultiSubscriptionHeaderByteSize) writeremoveMultiSubscriptionHeader(buffer, header, 0) return buffer @@ -4030,12 +3924,15 @@ export const writeAggPropProps = { }, } -export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { +export const readAggProp = ( + buf: Uint8Array, + offset: number, +): AggProp => { const value: AggProp = { propId: buf[offset], - propType: buf[offset + 1] as PropTypeEnum, + propType: (buf[offset + 1]) as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), - aggFunction: buf[offset + 4] as AggFunctionEnum, + aggFunction: (buf[offset + 4]) as AggFunctionEnum, resultPos: readUint16(buf, offset + 5), accumulatorPos: readUint16(buf, offset + 7), } @@ -4043,16 +3940,12 @@ export const readAggProp = (buf: Uint8Array, offset: number): AggProp => { } export const readAggPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 1] as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2), - aggFunction: (buf: Uint8Array, offset: number) => - buf[offset + 4] as AggFunctionEnum, - resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), - accumulatorPos: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 7), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + aggFunction: (buf: Uint8Array, offset: number) => (buf[offset + 4]) as AggFunctionEnum, + resultPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 5), + accumulatorPos: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), } export const createAggProp = (header: AggProp): Uint8Array => { @@ -4135,7 +4028,7 @@ export const readGroupByKeyProp = ( ): GroupByKeyProp => { const value: GroupByKeyProp = { propId: buf[offset], - propType: buf[offset + 1] as PropTypeEnum, + propType: (buf[offset + 1]) as PropTypeEnum, propDefStart: readUint16(buf, offset + 2), stepType: buf[offset + 4], stepRange: readUint32(buf, offset + 5), @@ -4145,14 +4038,12 @@ export const readGroupByKeyProp = ( } export const readGroupByKeyPropProps = { - propId: (buf: Uint8Array, offset: number) => buf[offset], - propType: (buf: Uint8Array, offset: number) => - buf[offset + 1] as PropTypeEnum, - propDefStart: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2), - stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], - stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), - timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), + propId: (buf: Uint8Array, offset: number) => buf[offset], + propType: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, + propDefStart: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 2), + stepType: (buf: Uint8Array, offset: number) => buf[offset + 4], + stepRange: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 5), + timezone: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 9), } export const createGroupByKeyProp = (header: GroupByKeyProp): Uint8Array => { @@ -4190,6 +4081,8 @@ export const FilterOpCompare = { le: 21, inc: 22, ninc: 23, + incBatch: 24, + nincBatch: 25, selectLargeRefs: 203, selectRef: 204, selectSmallRefs: 205, @@ -4212,6 +4105,10 @@ export const FilterOpCompareInverse = { 15: 'lt', 20: 'ge', 21: 'le', + 22: 'inc', + 23: 'ninc', + 24: 'incBatch', + 25: 'nincBatch', 203: 'selectLargeRefs', 204: 'selectRef', 205: 'selectSmallRefs', @@ -4234,6 +4131,10 @@ export const FilterOpCompareInverse = { lt, ge, le, + inc, + ninc, + incBatch, + nincBatch, selectLargeRefs, selectRef, selectSmallRefs, @@ -4242,8 +4143,7 @@ export const FilterOpCompareInverse = { nextOrIndex, andOp */ -export type FilterOpCompareEnum = - (typeof FilterOpCompare)[keyof typeof FilterOpCompare] +export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] export type FilterOp = { prop: PropTypeEnum @@ -4289,18 +4189,20 @@ export const writeFilterOpProps = { }, } -export const readFilterOp = (buf: Uint8Array, offset: number): FilterOp => { +export const readFilterOp = ( + buf: Uint8Array, + offset: number, +): FilterOp => { const value: FilterOp = { - prop: buf[offset] as PropTypeEnum, - compare: buf[offset + 1] as FilterOpCompareEnum, + prop: (buf[offset]) as PropTypeEnum, + compare: (buf[offset + 1]) as FilterOpCompareEnum, } return value } export const readFilterOpProps = { - prop: (buf: Uint8Array, offset: number) => buf[offset] as PropTypeEnum, - compare: (buf: Uint8Array, offset: number) => - buf[offset + 1] as FilterOpCompareEnum, + prop: (buf: Uint8Array, offset: number) => (buf[offset]) as PropTypeEnum, + compare: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as FilterOpCompareEnum, } export const createFilterOp = (header: FilterOp): Uint8Array => { @@ -4396,15 +4298,13 @@ export const readFilterCondition = ( } export const readFilterConditionProps = { - op: (buf: Uint8Array, offset: number) => - unpackFilterOp(readUint16(buf, offset)), - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), - prop: (buf: Uint8Array, offset: number) => buf[offset + 6], - start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), - len: (buf: Uint8Array, offset: number) => buf[offset + 9], - fieldSchema: (buf: Uint8Array, offset: number) => - readUint64(buf, offset + 10), - offset: (buf: Uint8Array, offset: number) => buf[offset + 18], + op: (buf: Uint8Array, offset: number) => unpackFilterOp(readUint16(buf, offset)), + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + prop: (buf: Uint8Array, offset: number) => buf[offset + 6], + start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 7), + len: (buf: Uint8Array, offset: number) => buf[offset + 9], + fieldSchema: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 10), + offset: (buf: Uint8Array, offset: number) => buf[offset + 18], } export const createFilterCondition = (header: FilterCondition): Uint8Array => { @@ -4471,16 +4371,15 @@ export const readFilterSelect = ( const value: FilterSelect = { size: readUint32(buf, offset), typeEntry: readUint64(buf, offset + 4), - typeId: readUint16(buf, offset + 12) as TypeId, + typeId: (readUint16(buf, offset + 12)) as TypeId, } return value } export const readFilterSelectProps = { - size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), - typeId: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 12) as TypeId, + size: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + typeEntry: (buf: Uint8Array, offset: number) => readUint64(buf, offset + 4), + typeId: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 12)) as TypeId, } export const createFilterSelect = (header: FilterSelect): Uint8Array => { @@ -4563,16 +4462,14 @@ export const readSelvaSchemaHeader = ( } export const readSelvaSchemaHeaderProps = { - blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), - nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], - nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], - nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], - sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], + blockCapacity: (buf: Uint8Array, offset: number) => readUint32(buf, offset), + nrFields: (buf: Uint8Array, offset: number) => buf[offset + 4], + nrFixedFields: (buf: Uint8Array, offset: number) => buf[offset + 5], + nrVirtualFields: (buf: Uint8Array, offset: number) => buf[offset + 6], + sdbVersion: (buf: Uint8Array, offset: number) => buf[offset + 7], } -export const createSelvaSchemaHeader = ( - header: SelvaSchemaHeader, -): Uint8Array => { +export const createSelvaSchemaHeader = (header: SelvaSchemaHeader): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaHeaderByteSize) writeSelvaSchemaHeader(buffer, header, 0) return buffer @@ -4601,9 +4498,7 @@ export const SelvaSchemaMicroBufferByteSize = 4 export const SelvaSchemaMicroBufferAlignOf = 4 -export const packSelvaSchemaMicroBuffer = ( - obj: SelvaSchemaMicroBuffer, -): number => { +export const packSelvaSchemaMicroBuffer = (obj: SelvaSchemaMicroBuffer): number => { let val = 0 val |= (Number(obj.type) & 255) << 0 val |= (Number(obj.len) & 65535) << 8 @@ -4611,9 +4506,7 @@ export const packSelvaSchemaMicroBuffer = ( return val } -export const unpackSelvaSchemaMicroBuffer = ( - val: number, -): SelvaSchemaMicroBuffer => { +export const unpackSelvaSchemaMicroBuffer = (val: number): SelvaSchemaMicroBuffer => { return { type: Number((val >>> 0) & 255), len: Number((val >>> 8) & 65535), @@ -4660,14 +4553,12 @@ export const readSelvaSchemaMicroBuffer = ( } export const readSelvaSchemaMicroBufferProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], + type: (buf: Uint8Array, offset: number) => buf[offset], + len: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 3], } -export const createSelvaSchemaMicroBuffer = ( - header: SelvaSchemaMicroBuffer, -): Uint8Array => { +export const createSelvaSchemaMicroBuffer = (header: SelvaSchemaMicroBuffer): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaMicroBufferByteSize) writeSelvaSchemaMicroBuffer(buffer, header, 0) return buffer @@ -4733,14 +4624,12 @@ export const readSelvaSchemaString = ( } export const readSelvaSchemaStringProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], - defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), + type: (buf: Uint8Array, offset: number) => buf[offset], + fixedLenHint: (buf: Uint8Array, offset: number) => buf[offset + 1], + defaultLen: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 2), } -export const createSelvaSchemaString = ( - header: SelvaSchemaString, -): Uint8Array => { +export const createSelvaSchemaString = (header: SelvaSchemaString): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaStringByteSize) writeSelvaSchemaString(buffer, header, 0) return buffer @@ -4813,8 +4702,8 @@ export const readSelvaSchemaText = ( } export const readSelvaSchemaTextProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], + type: (buf: Uint8Array, offset: number) => buf[offset], + nrDefaults: (buf: Uint8Array, offset: number) => buf[offset + 1], } export const createSelvaSchemaText = (header: SelvaSchemaText): Uint8Array => { @@ -4894,23 +4783,21 @@ export const readSelvaSchemaRef = ( const value: SelvaSchemaRef = { type: buf[offset], flags: buf[offset + 1], - dstNodeType: readUint16(buf, offset + 2) as TypeId, + dstNodeType: (readUint16(buf, offset + 2)) as TypeId, inverseField: buf[offset + 4], - edgeNodeType: readUint16(buf, offset + 5) as TypeId, + edgeNodeType: (readUint16(buf, offset + 5)) as TypeId, capped: readUint32(buf, offset + 7), } return value } export const readSelvaSchemaRefProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - flags: (buf: Uint8Array, offset: number) => buf[offset + 1], - dstNodeType: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 2) as TypeId, - inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], - edgeNodeType: (buf: Uint8Array, offset: number) => - readUint16(buf, offset + 5) as TypeId, - capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), + type: (buf: Uint8Array, offset: number) => buf[offset], + flags: (buf: Uint8Array, offset: number) => buf[offset + 1], + dstNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 2)) as TypeId, + inverseField: (buf: Uint8Array, offset: number) => buf[offset + 4], + edgeNodeType: (buf: Uint8Array, offset: number) => (readUint16(buf, offset + 5)) as TypeId, + capped: (buf: Uint8Array, offset: number) => readUint32(buf, offset + 7), } export const createSelvaSchemaRef = (header: SelvaSchemaRef): Uint8Array => { @@ -4989,15 +4876,13 @@ export const readSelvaSchemaColvec = ( } export const readSelvaSchemaColvecProps = { - type: (buf: Uint8Array, offset: number) => buf[offset], - vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), - compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), - hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], + type: (buf: Uint8Array, offset: number) => buf[offset], + vecLen: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 1), + compSize: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 3), + hasDefault: (buf: Uint8Array, offset: number) => buf[offset + 5], } -export const createSelvaSchemaColvec = ( - header: SelvaSchemaColvec, -): Uint8Array => { +export const createSelvaSchemaColvec = (header: SelvaSchemaColvec): Uint8Array => { const buffer = new Uint8Array(SelvaSchemaColvecByteSize) writeSelvaSchemaColvec(buffer, header, 0) return buffer @@ -5014,3 +4899,4 @@ export const pushSelvaSchemaColvec = ( buf.pushUint8(Number(header.hasDefault)) return index } + diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index a32677bc63..598c4bab15 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -71,7 +71,7 @@ await test('include', async (t) => { let d = Date.now() - for (let i = 0; i < 5e6; i++) { + for (let i = 0; i < 10; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -90,41 +90,41 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', filter: { - props: { - name: { - // ADD LOWER CASE - // ops: [{ op: 'includes', val: 'flap', opts: { lowerCase: true } }], - ops: [{ op: 'includes', val: 'x' }], - }, - }, // props: { - // flap: { ops: [{ op: '=', val: 9999 }] }, - // }, - // and: { - // props: { - // y: { ops: [{ op: '=', val: 100 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 3 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 4 }] }, - // }, - // }, - // }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 670 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 15 }] }, - // }, + // name: { + // // ADD LOWER CASE + // // ops: [{ op: 'includes', val: 'flap', opts: { lowerCase: true } }], + // ops: [{ op: 'includes', val: 'x' }], // }, // }, + props: { + flap: { ops: [{ op: '=', val: 9999 }] }, + }, + and: { + props: { + y: { ops: [{ op: '=', val: 100 }] }, + }, + or: { + props: { + y: { ops: [{ op: '=', val: 3 }] }, + }, + or: { + props: { + y: { ops: [{ op: '=', val: 4 }] }, + }, + }, + }, + }, + or: { + props: { + y: { ops: [{ op: '=', val: 670 }] }, + }, + or: { + props: { + y: { ops: [{ op: '=', val: 15 }] }, + }, + }, + }, }, // filter('flap', '=', 9999) @@ -176,7 +176,7 @@ await test('include', async (t) => { queries.push(x) } - await perf( + await perf.skip( async () => { const q: any = [] for (let i = 0; i < 10; i++) { diff --git a/test/query/ast.ts b/test/query/ast.ts index ea0184eb95..9aadf66a59 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -10,6 +10,11 @@ await test('query ast creation', async (t) => { } types: { user: { + friend: { + ref: 'user' + prop: 'friend' + $rating: 'uint32' + } name: 'string' isNice: 'boolean' age: 'number' @@ -57,10 +62,14 @@ await test('query ast creation', async (t) => { { const q = query('user') - .filter('isNice', '=', false) - .and('name', '=', 'youzi') - .or('name', '=', 'james') - .and('isNice', '=', false) + .include('age') + .filter('name', 'includes', 'jim') + .and((f) => f('age', '>', 2)) + + // .filter('isNice', '=', false) + // .and('name', '=', 'youzi') + // .or('name', '=', 'james') + // .and('isNice', '=', false) deepEqual(q.ast, { type: 'user', diff --git a/test/query/db.ts b/test/query/db.ts index 1a688e11de..6e8b9d67d0 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -12,6 +12,10 @@ await test('query db', async (t) => { name: 'string', isNice: 'boolean', age: 'number', + friend: { + ref: 'user', + prop: 'friend', + }, }, }, }) @@ -19,7 +23,7 @@ await test('query db', async (t) => { db.create('user', { name: 'john', isNice: false, - age: 21, + age: 1, }) db.create('user', { @@ -34,6 +38,7 @@ await test('query db', async (t) => { .include('name') .filter('isNice', '=', false) .get() + deepEqual(res, [{ id: 1, name: 'john' }]) } From 11fee38a9c04bf3e1e73d2b5411b498cd5513330 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Sat, 14 Feb 2026 10:48:25 +0100 Subject: [PATCH 271/449] fix --- src/db-query/ast/filter/comparison.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db-query/ast/filter/comparison.ts b/src/db-query/ast/filter/comparison.ts index 18e9f00e23..0d9e6fb5bd 100644 --- a/src/db-query/ast/filter/comparison.ts +++ b/src/db-query/ast/filter/comparison.ts @@ -16,7 +16,8 @@ export const conditionByteSize = (propSize: number, size: number) => { return size + FilterConditionByteSize + FilterConditionAlignOf + 1 + propSize } -// nice to get this from zig +// Make this configurable in the client +// has to be send from the server const VECTOR_BYTES = 16 export const createCondition = ( From 5d5064b569da9bc2ee2f02adb68ca36383a51c49 Mon Sep 17 00:00:00 2001 From: youzi Date: Sat, 14 Feb 2026 10:55:19 +0100 Subject: [PATCH 272/449] fixie --- src/schema/defs/getTypeDefs.ts | 6 ++++-- src/schema/defs/index.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/schema/defs/getTypeDefs.ts b/src/schema/defs/getTypeDefs.ts index 20de34ebac..763abfcdc2 100644 --- a/src/schema/defs/getTypeDefs.ts +++ b/src/schema/defs/getTypeDefs.ts @@ -80,7 +80,7 @@ const getTypeDef = ( separate: [], props: new Map(), main: [], - tree: { props: new Map(), required: [] }, + tree: { path: [], schema, props: new Map(), required: [] }, schema, schemaRoot, propHooks: { @@ -112,7 +112,9 @@ const getTypeDef = ( props: new Map(), required: [], } - if (walk(prop.props, path, def)) required = true + if (walk(prop.props, path, def)) { + required = true + } tree.props.set(key, def) } else { def = addPropDef(prop, path, typeDef) diff --git a/src/schema/defs/index.ts b/src/schema/defs/index.ts index 99608a2a37..ec891909f1 100644 --- a/src/schema/defs/index.ts +++ b/src/schema/defs/index.ts @@ -23,7 +23,7 @@ export type PropTree = { props: Map required: string[] path: string[] - schema: SchemaObject + schema: SchemaObject | SchemaType } export type TypeDef = { From 07b8b3ef914ed5d41290491ebecd19b39d2a1e09 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 16 Feb 2026 09:43:03 +0100 Subject: [PATCH 273/449] Add dependent test case --- test/dependent.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/test/dependent.ts b/test/dependent.ts index b980406367..5eda47149b 100644 --- a/test/dependent.ts +++ b/test/dependent.ts @@ -1,4 +1,4 @@ -import { equal } from './shared/assert.js' +import { deepEqual, equal } from './shared/assert.js' import { BasedDb } from '../src/index.js' import test from './shared/test.js' @@ -8,7 +8,6 @@ await test('dependent', async (t) => { }) await db.start({ clean: true }) - t.after(() => t.backup(db)) const schema = { @@ -85,3 +84,52 @@ await test('dependent', async (t) => { } await createShowTree() }) + +await test('del children', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + + await db.start({ clean: true }) + t.after(() => t.backup(db)) + + const client = await db.setSchema({ + types: { + parent: { + children: { type: 'references', items: { ref: 'child', prop: 'parent' } } + }, + child: { + parent: { type: 'reference', ref: 'parent', prop: 'children', dependent: true }, + }, + } + }) + + for (let n = 1; n <= 5; n++) { + const head = client.create('parent', {}) + const children: ReturnType[] = [] + + for (let i = 0; i < n; i++) { + children.push(client.create('child', { parent: head })) + } + deepEqual(await client.query('parent', head).include('**').get(), { + id: await head, + children: (await Promise.all(children)).map((id: number) => ({id})), + }) + + for (const child of children) { + client.delete('child', child) + } + await client.drain() + deepEqual(await client.query('parent', head).include('**').get(), { + id: await head, + children: [], + }) + + for (let i = 0; i < n; i++) { + children.push(client.create('child', { parent: head })) + } + await client.delete('parent', head) + deepEqual(await client.query('parent').get(), []) + deepEqual(await client.query('child').get(), []) + } +}) From 90bbd8d23622ed53e9a360abb681593872c9de1d Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 16 Feb 2026 14:01:50 +0100 Subject: [PATCH 274/449] exports --- src/zigTsExports.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 08ee41aeb9..58d7b6a3b3 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -4089,7 +4089,6 @@ export const FilterOpCompare = { selectLargeRefEdge: 206, selectLargeRefsEdge: 207, nextOrIndex: 253, - andOp: 254, } as const export const FilterOpCompareInverse = { @@ -4115,7 +4114,6 @@ export const FilterOpCompareInverse = { 206: 'selectLargeRefEdge', 207: 'selectLargeRefsEdge', 253: 'nextOrIndex', - 254: 'andOp', } as const /** @@ -4140,8 +4138,7 @@ export const FilterOpCompareInverse = { selectSmallRefs, selectLargeRefEdge, selectLargeRefsEdge, - nextOrIndex, - andOp + nextOrIndex */ export type FilterOpCompareEnum = (typeof FilterOpCompare)[keyof typeof FilterOpCompare] From 8324a56f50c7ab93331e560d02bebe1efe23f0be Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 16 Feb 2026 14:11:14 +0100 Subject: [PATCH 275/449] edge filter start --- native/query/filter/filter.zig | 11 ++--------- src/db-query/ast/filter/filter.ts | 4 +++- src/db-query/ast/multiple.ts | 2 ++ src/db-query/ast/single.ts | 3 +++ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index bf5520c6e0..b4612df207 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -37,19 +37,14 @@ pub fn prepare( c.offset = utils.alignLeftLen(c.len, q[nextI .. totalSize + i]); const end = totalSize + i; - // if (c.op.compare == t.FilterOpCompare.nextOrIndex) { - // if NEXT END = -1 then its q.len - // std.debug.print("HELLO ITS OR {any} \n", .{utils.readPtr(u64, q, nextI + @alignOf(u64) - c.offset).*}); - // } - switch (c.op.compare) { .selectLargeRefEdge => { - // const select = utils.readPtr(t.FilterSelect, q, i + q[i] + utils.sizeOf(t.FilterCondition) + @alignOf(t.FilterSelect) - condition.offset); + // const select = utils.readPtr(t.FilterSelect, q, i + q[i] + utils.sizeOf(t.FilterCondition) + @alignOf(t.FilterSelect) - c.offset); // const edgeSelect = utils.readPtr(t.FilterSelect, q, i + q[i] + utils.sizeOf(t.FilterCondition) + @alignOf(t.FilterSelect) - condition.offset); // select.typeEntry = try Node.getType(ctx.db, select.typeId); // try prepare(q[end .. end + select.size], ctx, select.typeEntry); // i = end + select.size; - i = end; + // i = end; }, .selectRef => { const select = utils.readPtr(t.FilterSelect, q, nextI + @alignOf(t.FilterSelect) - c.offset); @@ -131,8 +126,6 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { pass = switch (c.op.compare) { .nextOrIndex => blk: { end = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; - // nextEnd = nextOrIndex; - // put second thing PREV OR INDEX here break :blk true; }, .selectRef => blk: { diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index 52554045c3..7bcd75405b 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -20,6 +20,8 @@ type WalkCtx = { main: { prop: PropDef; ops: FilterOp[] }[] } +// Handle EDGES + const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { const { tree, main } = walkCtx @@ -167,7 +169,7 @@ export const filter = ( } if (andOrReplace) { - // REMOVE THIS! + // REMOVE THIS! FIX let index = indexOf( ctx.query.data, andOrReplace, diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index db0be8389e..959d808663 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -23,6 +23,8 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { return } + // ADD SORT + const headerIndex = pushQueryHeader(ctx.query, { op: QueryType.default, prop: ID_PROP, diff --git a/src/db-query/ast/single.ts b/src/db-query/ast/single.ts index b946b1d9cd..3523cc7a23 100644 --- a/src/db-query/ast/single.ts +++ b/src/db-query/ast/single.ts @@ -18,6 +18,9 @@ export const defaultSingle = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { let prop = 0 let aliasProp: PropDef | undefined let aliasValue + + // ADD FILTER AND ALIAS + if (typeof ast.target === 'number') { id = ast.target } else if (typeof ast.target === 'object' && ast.target !== null) { From 0ebd2e6900babed2e8de7fd4ca1cef6d2284aa47 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 16 Feb 2026 11:42:59 -0300 Subject: [PATCH 276/449] very ugly fix to be cleaned --- native/query/aggregates/aggregates.zig | 4 +- native/query/aggregates/group.zig | 25 +- native/query/aggregates/references.zig | 8 +- native/query/multiple.zig | 4 +- test/aggregate/deep.ts | 77 +++--- test/aggregate/dev.ts | 327 ++++++++++++++++++------- 6 files changed, 303 insertions(+), 142 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 6b182e0313..15a4add5e8 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -53,7 +53,7 @@ pub inline fn aggregateProps( while (i < aggDefs.len) { const currentAggDef = utils.readNext(t.AggProp, aggDefs, &i); // utils.debugPrint("currentAggDef: {any}\n", .{currentAggDef}); - utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); + // utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); var value: []u8 = undefined; if (currentAggDef.aggFunction == t.AggFunction.count) { accumulate(currentAggDef, accumulatorProp, value, hadAccumulated, null, null); @@ -112,7 +112,7 @@ pub inline fn accumulate( switch (aggFunction) { .sum => { writeAs(f64, accumulatorProp, read(f64, accumulatorProp, accumulatorPos) + microbufferToF64(propTypeTag, value, start), accumulatorPos); - utils.debugPrint("❤️ v: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); + // utils.debugPrint("❤️ v: {d}\n", .{read(f64, accumulatorProp, accumulatorPos)}); }, .avg => { const val = microbufferToF64(propTypeTag, value, start); diff --git a/native/query/aggregates/group.zig b/native/query/aggregates/group.zig index 18bb7c195f..b67c6425a3 100644 --- a/native/query/aggregates/group.zig +++ b/native/query/aggregates/group.zig @@ -23,9 +23,11 @@ pub fn iterator( filterBuf: []u8, aggDefs: []u8, accumulatorSize: usize, + resultsSize: usize, typeEntry: Node.Type, hllAccumulator: anytype, -) !u32 { + sumOfDistinctKeyLens: *usize, +) usize { var count: u32 = 0; var hadAccumulated: bool = false; @@ -35,11 +37,14 @@ pub fn iterator( continue; } } - try aggregatePropsWithGroupBy(groupByHashMap, node, typeEntry, aggDefs, accumulatorSize, hllAccumulator, &hadAccumulated); + sumOfDistinctKeyLens.* += aggregatePropsWithGroupBy(groupByHashMap, node, typeEntry, aggDefs, accumulatorSize, resultsSize, hllAccumulator, &hadAccumulated) catch { + return 0; + }; count += 1; if (count >= limit) break; } - return count; + // utils.debugPrint("count {d}, resultsSize {d}, sumOfDistinctKeyLens {d}\n", .{ count, resultsSize, sumOfDistinctKeyLens.* }); + return sumOfDistinctKeyLens.*; } inline fn getGrouByKeyValue( @@ -76,12 +81,14 @@ inline fn aggregatePropsWithGroupBy( typeEntry: Node.Type, aggDefs: []u8, accumulatorSize: usize, + resultsSize: usize, hllAccumulator: anytype, hadAccumulated: *bool, -) !void { - if (aggDefs.len == 0) return; +) !usize { + if (aggDefs.len == 0) return 0; // utils.debugPrint("\n\naggDefs: {any}\n", .{aggDefs}); + var sumOfDistinctKeyLens: usize = 0; var i: usize = 0; const currentKeyPropDef = utils.readNext(t.GroupByKeyProp, aggDefs, &i); // utils.debugPrint("currentKeyPropDef: {any}\n", .{currentKeyPropDef}); @@ -91,7 +98,7 @@ inline fn aggregatePropsWithGroupBy( const propSchema = Schema.getFieldSchema(typeEntry, currentKeyPropDef.propId) catch { i += utils.sizeOf(t.GroupByKeyProp); - return; + return 0; }; keyValue = Fields.get( @@ -108,8 +115,13 @@ inline fn aggregatePropsWithGroupBy( try groupByHashMap.getOrInsert(key, accumulatorSize); const accumulatorProp = hash_map_entry.value; hadAccumulated.* = !hash_map_entry.is_new; + if (hash_map_entry.is_new) { + sumOfDistinctKeyLens += 2 + key.len + resultsSize; + } + // utils.debugPrint("is_new?: {any}, key: {s} {d}, sumOfDistinctKeyLens: {d}\n", .{ hash_map_entry.is_new, key, key.len, sumOfDistinctKeyLens }); Aggregates.aggregateProps(node, typeEntry, aggDefs[i..], accumulatorProp, hllAccumulator, hadAccumulated); + return sumOfDistinctKeyLens; } pub inline fn finalizeGroupResults( @@ -145,6 +157,7 @@ pub inline fn finalizeRefsGroupResults( while (it.next()) |entry| { const key = entry.key_ptr.*; const keyLen: u16 = @intCast(key.len); + if (key.len > 0) { try ctx.thread.query.append(keyLen); try ctx.thread.query.append(key); diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index 9c6f603b75..8b25fd41a8 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -20,9 +20,11 @@ pub inline fn aggregateRefsProps( fromType: Selva.Type, i: *usize, ) !void { + var totalSize: usize = 0; // utils.debugPrint("i: {d}\n", .{i.*}); + const header = utils.readNext(t.AggRefsHeader, q, i); - utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); + // utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); const accumulatorProp = try ctx.db.allocator.alloc(u8, header.accumulatorSize); @memset(accumulatorProp, 0); @@ -34,11 +36,11 @@ pub inline fn aggregateRefsProps( if (header.hasGroupBy) { var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - const ct = try GroupBy.iterator(ctx, &groupByHashMap, &it, 1000, false, undefined, q[i.*..], header.accumulatorSize, it.dstType, undefined); // TODO: hllAcc + totalSize += GroupBy.iterator(ctx, &groupByHashMap, &it, 1000, false, undefined, q[i.*..], header.accumulatorSize, header.resultsSize, it.dstType, undefined, &totalSize); // TODO: hllAcc try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); - try ctx.thread.query.append(@as(u32, ct * utils.sizeOf(t.AggProp) + utils.sizeOf(t.AggRefsHeader) + header.filterSize)); + try ctx.thread.query.append(@as(u32, @intCast(totalSize))); try GroupBy.finalizeRefsGroupResults(ctx, &groupByHashMap, header, q[i.*..]); } else { _ = try Aggregates.iterator(ctx, &it, 1000, false, undefined, q[i.*..], accumulatorProp, it.dstType, undefined); // TODO: hllAcc diff --git a/native/query/multiple.zig b/native/query/multiple.zig index a44be1e8ed..17924a3a53 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -330,7 +330,7 @@ pub fn aggregates( .groupBy => { var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - nodeCnt = try GroupBy.iterator(ctx, &groupByHashMap, &it, header.limit, false, undefined, q[i..], header.accumulatorSize, typeEntry, hllAccumulator); + nodeCnt = @intCast(GroupBy.iterator(ctx, &groupByHashMap, &it, header.limit, false, undefined, q[i..], header.accumulatorSize, header.resultsSize, typeEntry, hllAccumulator, undefined)); try GroupBy.finalizeGroupResults(ctx, &groupByHashMap, header, q[i..]); }, .groupByFilter => { @@ -338,7 +338,7 @@ pub fn aggregates( try Filter.prepare(filter, ctx, typeEntry); var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - nodeCnt = try GroupBy.iterator(ctx, &groupByHashMap, &it, header.limit, true, filter, q[i..], header.accumulatorSize, typeEntry, hllAccumulator); + nodeCnt = @intCast(GroupBy.iterator(ctx, &groupByHashMap, &it, header.limit, true, filter, q[i..], header.accumulatorSize, header.resultsSize, typeEntry, hllAccumulator, undefined)); try GroupBy.finalizeGroupResults(ctx, &groupByHashMap, header, q[i..]); }, else => {}, diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index 10056393f0..e226535610 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -313,46 +313,45 @@ await test('agg on references', async (t) => { select('players').groupBy('position').sum('goalsScored', 'gamesPlayed') }) .get() - result.inspect() - // deepEqual( - // result.toObject(), - // [ - // { - // id: 1, - // teamName: 'Grêmio', - // city: 'Porto Alegre', - // players: { - // Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) - // Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) - // }, - // }, - // { - // id: 2, - // teamName: 'Ajax', - // city: 'Amsterdam', - // players: { - // Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) - // Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) - // }, - // }, - // { - // id: 3, - // teamName: 'Boca Juniors', - // city: 'Buenos Aires', - // players: {}, // does anybody wants to play for Boca? - // }, - // { - // id: 4, - // teamName: 'Barcelona', - // city: 'Barcelona', - // players: { - // Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski - // }, - // }, - // ], - // 'Include parent props, with referenced items grouped by their own prop, and aggregations', - // ) + deepEqual( + result.toObject(), + [ + { + id: 1, + teamName: 'Boca Juniors', + city: 'Buenos Aires', + players: {}, // does anybody wants to play for Boca? + }, + { + id: 2, + teamName: 'Barcelona', + city: 'Barcelona', + players: { + Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski + }, + }, + { + id: 3, + teamName: 'Grêmio', + city: 'Porto Alegre', + players: { + Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) + Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) + }, + }, + { + id: 4, + teamName: 'Ajax', + city: 'Amsterdam', + players: { + Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) + Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) + }, + }, + ], + 'Include parent props, with referenced items grouped by their own prop, and aggregations', + ) }) await test('enums', async (t) => { diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index 51cb99f824..b6f3b002d2 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -2,127 +2,274 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' -await test('kev', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) +// await test('kev', async (t) => { +// const db = new BasedDb({ +// path: t.tmp, +// maxModifySize: 1e6, +// }) - await db.start({ clean: true }) - t.after(() => db.stop()) +// await db.start({ clean: true }) +// t.after(() => db.stop()) - await db.setSchema({ - types: { - trip: { - driver: 'string', - distance: 'int32', - rate: 'int8', - }, - }, - }) +// await db.setSchema({ +// types: { +// trip: { +// driver: 'string', +// distance: 'int32', +// rate: 'int8', +// }, +// }, +// }) - db.create('trip', { driver: 'lala', distance: 10, rate: 5 }) - db.create('trip', { driver: 'lala', distance: 20, rate: 10 }) - db.create('trip', { driver: 'lele', distance: 40, rate: 10 }) +// db.create('trip', { driver: 'lala', distance: 10, rate: 5 }) +// db.create('trip', { driver: 'lala', distance: 20, rate: 10 }) +// db.create('trip', { driver: 'lele', distance: 40, rate: 10 }) - // console.log((await db.query('trip').include('distance').get()).debug()) - // console.log( - // ( - // await db.query('trip').hmean('distance').avg('distance').get() - // ).debug(), - // ) +// // console.log((await db.query('trip').include('distance').get()).debug()) +// // console.log( +// // ( +// // await db.query('trip').harmonicMean('distance').avg('distance').get() +// // ).debug(), +// // ) - // console.log((await db.query('trip').sum('distance', 'rate').get()).debug()) - console.log( - (await db.query('trip').filter('distance', '>', 10).get()).debug(), - ) - console.log( - ( - await db.query('trip').sum('distance').filter('distance', '>', 10).get() - ).debug(), - ) - console.log( - ( - await db - .query('trip') - .sum('distance') - .filter('rate', '>', 8) - .groupBy('driver') - .get() - ).debug(), - ) +// // console.log((await db.query('trip').sum('distance', 'rate').get()).debug()) +// console.log( +// (await db.query('trip').filter('distance', '>', 10).get()).debug(), +// ) +// console.log( +// ( +// await db.query('trip').sum('distance').filter('distance', '>', 10).get() +// ).debug(), +// ) +// console.log( +// ( +// await db +// .query('trip') +// .sum('distance') +// .filter('rate', '>', 8) +// .groupBy('driver') +// .get() +// ).debug(), +// ) - await db.stop() -}) +// await db.stop() +// }) + +// await test('references', async (t) => { +// const db = new BasedDb({ +// path: t.tmp, +// }) +// await db.start({ clean: true }) +// t.after(() => db.stop()) + +// await db.setSchema({ +// types: { +// driver: { +// props: { +// name: 'string', +// trips: { +// items: { +// ref: 'trip', +// prop: 'driver', // Defines the inverse relationship +// }, +// }, +// }, +// }, +// trip: { +// props: { +// distance: 'number', +// rate: 'uint8', +// driver: { +// ref: 'driver', +// prop: 'trips', // Points back to the list on driver +// }, +// }, +// }, +// }, +// }) + +// const d1 = db.create('driver', { +// name: 'Luc Ferry', +// }) +// db.drain() +// const t1 = db.create('trip', { +// distance: 523.1, // with uint16 => 523 +// rate: 4, +// driver: d1, +// }) +// const t2 = db.create('trip', { +// distance: 1230, +// rate: 2, +// driver: d1, +// }) + +// // await db.query('trip').include('*', '**').get().inspect(10) + +// // await db +// // .query('driver') +// // .include((t) => t('trips').include('distance')) +// // .get() +// // .inspect(10) + +// const lala = await db +// .query('driver') +// .include((t) => +// t('trips') +// .sum('distance') +// .avg('distance') +// .min('rate') +// .sum('rate') +// .count(), +// ) +// .get() -await test('references', async (t) => { +// // console.log(lala.toObject()) +// lala.inspect(10) +// }) + +await test('yyy', async (t) => { const db = new BasedDb({ path: t.tmp, + maxModifySize: 1e6, }) + await db.start({ clean: true }) - t.after(() => db.stop()) + t.after(() => t.backup(db)) await db.setSchema({ types: { - driver: { + team: { props: { - name: 'string', - trips: { + teamName: { type: 'string' }, + city: { type: 'string' }, + players: { items: { - ref: 'trip', - prop: 'driver', // Defines the inverse relationship + ref: 'player', + prop: 'team', }, }, }, }, - trip: { + player: { props: { - distance: 'number', - rate: 'uint8', - driver: { - ref: 'driver', - prop: 'trips', // Points back to the list on driver + playerName: { type: 'string' }, + position: { type: 'string' }, + goalsScored: 'uint16', + gamesPlayed: 'uint16', + team: { + ref: 'team', + prop: 'players', }, }, }, }, }) - const d1 = db.create('driver', { - name: 'Luc Ferry', + const p1 = db.create('player', { + playerName: 'Martin', + position: 'Forward', + goalsScored: 10, + gamesPlayed: 5, + }) + const p2 = db.create('player', { + playerName: 'Jemerson', + position: 'Defender', + goalsScored: 1, + gamesPlayed: 10, + }) + const p3 = db.create('player', { + playerName: 'Pavon', + position: 'Forward', + goalsScored: 12, + gamesPlayed: 6, + }) + const p4 = db.create('player', { + playerName: 'Wout', + position: 'Forward', + goalsScored: 8, + gamesPlayed: 7, + }) + const p5 = db.create('player', { + playerName: 'Jorrel', + position: 'Defender', + goalsScored: 2, + gamesPlayed: 9, + }) + + const t1 = db.create('team', { + teamName: 'Grêmio', + city: 'Porto Alegre', + players: [p1, p2, p3], + }) + const t2 = db.create('team', { + teamName: 'Ajax', + city: 'Amsterdam', + players: [p4, p5], }) - db.drain() - const t1 = db.create('trip', { - distance: 523.1, // with uint16 => 523 - rate: 4, - driver: d1, + const t3 = db.create('team', { + teamName: 'Boca Juniors', + city: 'Buenos Aires', + players: [], }) - const t2 = db.create('trip', { - distance: 1230, - rate: 2, - driver: d1, + const t4 = db.create('team', { + teamName: 'Barcelona', + city: 'Barcelona', + players: [ + db.create('player', { + playerName: 'Lewandowski', + position: 'Forward', + goalsScored: 5, + gamesPlayed: 5, + }), + ], }) - // await db.query('trip').include('*', '**').get().inspect(10) - - // await db - // .query('driver') - // .include((t) => t('trips').include('distance')) - // .get() - // .inspect(10) - - const lala = await db - .query('driver') - .include((t) => - t('trips') - .sum('distance') - .avg('distance') - .min('rate') - .sum('rate') - .count(), - ) + const result = await db + .query('team') + .include('teamName', 'city', (select) => { + select('players').sum('goalsScored', 'gamesPlayed').groupBy('position') + }) .get() - // console.log(lala.toObject()) - lala.inspect(10) + result.debug() + result.inspect() + + deepEqual( + result.toObject(), + [ + { + id: 1, + teamName: 'Grêmio', + city: 'Porto Alegre', + players: { + Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) + Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) + }, + }, + { + id: 2, + teamName: 'Ajax', + city: 'Amsterdam', + players: { + Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) + Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) + }, + }, + { + id: 3, + teamName: 'Boca Juniors', + city: 'Buenos Aires', + players: {}, // does anybody wants to play for Boca? + }, + { + id: 4, + teamName: 'Barcelona', + city: 'Barcelona', + players: { + Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski + }, + }, + ], + 'Include parent props, with referenced items grouped by their own prop, and aggregations', + ) }) From adb02cc7973005a31251e27c7234aaba2613a067 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 16 Feb 2026 20:16:29 -0300 Subject: [PATCH 277/449] moving parameters to aggContext --- native/query/aggregates/aggregates.zig | 93 ++++++++++++++------------ native/query/aggregates/group.zig | 65 +++++++----------- native/query/aggregates/references.zig | 24 ++++--- native/query/multiple.zig | 28 +++++--- 4 files changed, 110 insertions(+), 100 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 15a4add5e8..403d9eb98e 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -12,40 +12,47 @@ const t = @import("../../types.zig"); const resultHeaderOffset = @import("../../thread/results.zig").resultHeaderOffset; const filter = @import("../filter/filter.zig").filter; +pub const AggCtx = struct { + queryCtx: *Query.QueryCtx, + typeEntry: Node.Type, + limit: u32 = 0, + isSamplingSet: bool = false, + hllAccumulator: ?*Selva.c.struct_selva_string = null, + accumulatorSize: usize = 0, + resultsSize: usize = 0, + hadAccumulated: bool = false, + totalResultsSize: usize = 0, +}; + pub fn iterator( - ctx: *Query.QueryCtx, + aggCtx: *AggCtx, it: anytype, - limit: u32, comptime hasFilter: bool, filterBuf: []u8, aggDefs: []u8, accumulatorProp: []u8, - typeEntry: Node.Type, - hllAccumulator: anytype, ) !u32 { var count: u32 = 0; - var hadAccumulated: bool = false; + aggCtx.hadAccumulated = false; while (it.next()) |node| { if (hasFilter) { - if (!try filter(node, ctx, filterBuf)) { + if (!try filter(node, aggCtx.queryCtx, filterBuf)) { continue; } } - aggregateProps(node, typeEntry, aggDefs, accumulatorProp, hllAccumulator, &hadAccumulated); + aggregateProps(node, aggDefs, accumulatorProp, aggCtx); count += 1; - if (count >= limit) break; + if (count >= aggCtx.limit) break; } return count; } pub inline fn aggregateProps( node: Node.Node, - typeEntry: Node.Type, aggDefs: []u8, accumulatorProp: []u8, - hllAccumulator: anytype, - hadAccumulated: *bool, + aggCtx: *AggCtx, ) void { if (aggDefs.len == 0) return; @@ -56,13 +63,13 @@ pub inline fn aggregateProps( // utils.debugPrint("😸 propId: {d}, node {d}\n", .{ currentAggDef.propId, Node.getNodeId(node) }); var value: []u8 = undefined; if (currentAggDef.aggFunction == t.AggFunction.count) { - accumulate(currentAggDef, accumulatorProp, value, hadAccumulated, null, null); + accumulate(currentAggDef, accumulatorProp, value, aggCtx, null); } else { if (currentAggDef.propId != t.MAIN_PROP and currentAggDef.aggFunction != t.AggFunction.cardinality) { i += utils.sizeOf(t.AggProp); continue; } - const propSchema = Schema.getFieldSchema(typeEntry, currentAggDef.propId) catch { + const propSchema = Schema.getFieldSchema(aggCtx.typeEntry, currentAggDef.propId) catch { i += utils.sizeOf(t.AggProp); continue; }; @@ -72,34 +79,33 @@ pub inline fn aggregateProps( i += utils.sizeOf(t.AggProp); continue; } - if (!hadAccumulated.*) { - _ = Selva.c.selva_string_replace(hllAccumulator, null, Selva.c.HLL_INIT_SIZE); - Selva.c.hll_init_like(hllAccumulator, hllValue); + if (!aggCtx.hadAccumulated) { + _ = Selva.c.selva_string_replace(aggCtx.hllAccumulator, null, Selva.c.HLL_INIT_SIZE); + Selva.c.hll_init_like(aggCtx.hllAccumulator, hllValue); } - accumulate(currentAggDef, accumulatorProp, value, hadAccumulated, hllAccumulator, hllValue); + accumulate(currentAggDef, accumulatorProp, value, aggCtx, hllValue); } else { value = Fields.get( - typeEntry, + aggCtx.typeEntry, node, propSchema, currentAggDef.propType, ); if (value.len > 0) { - accumulate(currentAggDef, accumulatorProp, value, hadAccumulated, null, null); + accumulate(currentAggDef, accumulatorProp, value, aggCtx, null); } } } } - hadAccumulated.* = true; + aggCtx.hadAccumulated = true; } pub inline fn accumulate( currentAggDef: t.AggProp, accumulatorProp: []u8, value: []u8, - hadAccumulated: *bool, - hllAccumulator: anytype, + aggCtx: *AggCtx, hllValue: anytype, ) void { const propType = currentAggDef.propType; @@ -126,14 +132,14 @@ pub inline fn accumulate( writeAs(f64, accumulatorProp, sum, accumulatorPos + 8); }, .min => { - if (!hadAccumulated.*) { + if (!aggCtx.hadAccumulated) { writeAs(f64, accumulatorProp, microbufferToF64(propTypeTag, value, start), accumulatorPos); } else { writeAs(f64, accumulatorProp, @min(read(f64, accumulatorProp, accumulatorPos), microbufferToF64(propTypeTag, value, start)), accumulatorPos); } }, .max => { - if (!hadAccumulated.*) { + if (!aggCtx.hadAccumulated) { writeAs(f64, accumulatorProp, microbufferToF64(propTypeTag, value, start), accumulatorPos); } else { writeAs(f64, accumulatorProp, @max(read(f64, accumulatorProp, accumulatorPos), microbufferToF64(propTypeTag, value, start)), accumulatorPos); @@ -187,8 +193,8 @@ pub inline fn accumulate( writeAs(u32, accumulatorProp, read(u32, accumulatorProp, accumulatorPos) + 1, accumulatorPos); }, .cardinality => { - Selva.c.hll_union(hllAccumulator, hllValue); - writeAs(u32, accumulatorProp, read(u32, Selva.c.hll_count(hllAccumulator)[0..4], 0), accumulatorPos); + Selva.c.hll_union(aggCtx.hllAccumulator, hllValue); + writeAs(u32, accumulatorProp, read(u32, Selva.c.hll_count(aggCtx.hllAccumulator)[0..4], 0), accumulatorPos); }, else => {}, } @@ -197,15 +203,14 @@ pub inline fn accumulate( } pub inline fn finalizeResults( - ctx: *Query.QueryCtx, + aggCtx: *AggCtx, aggDefs: []u8, accumulatorProp: []u8, - isSamplingSet: bool, initialAggDefOffset: usize, ) !void { var i: usize = initialAggDefOffset; - const initialResultOffset = ctx.thread.query.index; + const initialResultOffset = aggCtx.queryCtx.thread.query.index; while (i < aggDefs.len) { const currentAggDef = utils.readNext(t.AggProp, aggDefs, &i); const aggFunction = currentAggDef.aggFunction; @@ -214,28 +219,28 @@ pub inline fn finalizeResults( switch (aggFunction) { .sum => { - ctx.thread.query.reserveAndWrite(read(f64, accumulatorProp, accumulatorPos), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(read(f64, accumulatorProp, accumulatorPos), resultPos); }, .max => { - ctx.thread.query.reserveAndWrite(read(f64, accumulatorProp, accumulatorPos), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(read(f64, accumulatorProp, accumulatorPos), resultPos); }, .min => { - ctx.thread.query.reserveAndWrite(read(f64, accumulatorProp, accumulatorPos), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(read(f64, accumulatorProp, accumulatorPos), resultPos); }, .avg => { const count = read(u64, accumulatorProp, accumulatorPos); const sum = read(f64, accumulatorProp, accumulatorPos + 8); const mean = sum / @as(f64, @floatFromInt(count)); - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(mean)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(mean)), resultPos); }, .hmean => { const count = read(u64, accumulatorProp, accumulatorPos); if (count != 0) { const isum = read(f64, accumulatorProp, accumulatorPos + 8); const mean = @as(f64, @floatFromInt(count)) / isum; - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(mean)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(mean)), resultPos); } else { - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(0.0)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(0.0)), resultPos); } }, .stddev => { @@ -246,14 +251,14 @@ pub inline fn finalizeResults( const mean = sum / @as(f64, @floatFromInt(count)); const numerator = sum_sq - (sum * sum) / @as(f64, @floatFromInt(count)); const denominator = @as(f64, @floatFromInt(count)) - 1.0; - const variance = if (isSamplingSet) + const variance = if (aggCtx.isSamplingSet) numerator / denominator else (sum_sq / @as(f64, @floatFromInt(count))) - (mean * mean); const stddev = if (variance < 0) 0 else @sqrt(variance); - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(stddev)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(stddev)), resultPos); } else { - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(0.0)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(0.0)), resultPos); } }, .variance => { @@ -264,25 +269,25 @@ pub inline fn finalizeResults( const mean = sum / @as(f64, @floatFromInt(count)); const numerator = sum_sq - (sum * sum) / @as(f64, @floatFromInt(count)); const denominator = @as(f64, @floatFromInt(count)) - 1.0; - var variance = if (isSamplingSet) + var variance = if (aggCtx.isSamplingSet) numerator / denominator else (sum_sq / @as(f64, @floatFromInt(count))) - (mean * mean); if (variance < 0) variance = 0; - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(variance)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(variance)), resultPos); } else { - ctx.thread.query.reserveAndWrite(@as(f64, @floatCast(0.0)), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(@as(f64, @floatCast(0.0)), resultPos); } }, .count => { const count = read(u32, accumulatorProp, accumulatorPos); - ctx.thread.query.reserveAndWrite(count, resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(count, resultPos); }, .cardinality => { - ctx.thread.query.reserveAndWrite(read(u32, accumulatorProp, accumulatorPos), resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(read(u32, accumulatorProp, accumulatorPos), resultPos); }, else => { - ctx.thread.query.reserveAndWrite(0.0, resultPos); + aggCtx.queryCtx.thread.query.reserveAndWrite(0.0, resultPos); }, } } diff --git a/native/query/aggregates/group.zig b/native/query/aggregates/group.zig index b67c6425a3..8ec33787d8 100644 --- a/native/query/aggregates/group.zig +++ b/native/query/aggregates/group.zig @@ -15,36 +15,30 @@ const GroupByHashMap = @import("hashMap.zig").GroupByHashMap; const filter = @import("../filter/filter.zig").filter; pub fn iterator( - ctx: *Query.QueryCtx, + aggCtx: *Aggregates.AggCtx, groupByHashMap: *GroupByHashMap, it: anytype, - limit: u32, comptime hasFilter: bool, filterBuf: []u8, aggDefs: []u8, - accumulatorSize: usize, - resultsSize: usize, - typeEntry: Node.Type, - hllAccumulator: anytype, - sumOfDistinctKeyLens: *usize, ) usize { var count: u32 = 0; - var hadAccumulated: bool = false; + aggCtx.hadAccumulated = false; while (it.next()) |node| { if (hasFilter) { - if (!try filter(node, ctx, filterBuf)) { + if (!try filter(node, aggCtx.queryCtx, filterBuf)) { continue; } } - sumOfDistinctKeyLens.* += aggregatePropsWithGroupBy(groupByHashMap, node, typeEntry, aggDefs, accumulatorSize, resultsSize, hllAccumulator, &hadAccumulated) catch { + aggregatePropsWithGroupBy(groupByHashMap, node, aggDefs, aggCtx) catch { return 0; }; count += 1; - if (count >= limit) break; + if (count >= aggCtx.limit) break; } // utils.debugPrint("count {d}, resultsSize {d}, sumOfDistinctKeyLens {d}\n", .{ count, resultsSize, sumOfDistinctKeyLens.* }); - return sumOfDistinctKeyLens.*; + return count; } inline fn getGrouByKeyValue( @@ -78,17 +72,12 @@ inline fn getGrouByKeyValue( inline fn aggregatePropsWithGroupBy( groupByHashMap: *GroupByHashMap, node: Node.Node, - typeEntry: Node.Type, aggDefs: []u8, - accumulatorSize: usize, - resultsSize: usize, - hllAccumulator: anytype, - hadAccumulated: *bool, -) !usize { - if (aggDefs.len == 0) return 0; + aggCtx: *Aggregates.AggCtx, +) !void { + if (aggDefs.len == 0) return; // utils.debugPrint("\n\naggDefs: {any}\n", .{aggDefs}); - var sumOfDistinctKeyLens: usize = 0; var i: usize = 0; const currentKeyPropDef = utils.readNext(t.GroupByKeyProp, aggDefs, &i); // utils.debugPrint("currentKeyPropDef: {any}\n", .{currentKeyPropDef}); @@ -96,13 +85,13 @@ inline fn aggregatePropsWithGroupBy( var keyValue: []u8 = undefined; - const propSchema = Schema.getFieldSchema(typeEntry, currentKeyPropDef.propId) catch { + const propSchema = Schema.getFieldSchema(aggCtx.typeEntry, currentKeyPropDef.propId) catch { i += utils.sizeOf(t.GroupByKeyProp); - return 0; + return; }; keyValue = Fields.get( - typeEntry, + aggCtx.typeEntry, node, propSchema, currentKeyPropDef.propType, @@ -110,24 +99,23 @@ inline fn aggregatePropsWithGroupBy( const key = getGrouByKeyValue(keyValue, currentKeyPropDef); const hash_map_entry = if (currentKeyPropDef.propType == t.PropType.timestamp and currentKeyPropDef.stepRange != 0) - try groupByHashMap.getOrInsertWithRange(key, accumulatorSize, currentKeyPropDef.stepRange) + try groupByHashMap.getOrInsertWithRange(key, aggCtx.accumulatorSize, currentKeyPropDef.stepRange) else - try groupByHashMap.getOrInsert(key, accumulatorSize); + try groupByHashMap.getOrInsert(key, aggCtx.accumulatorSize); + const accumulatorProp = hash_map_entry.value; - hadAccumulated.* = !hash_map_entry.is_new; + aggCtx.hadAccumulated = !hash_map_entry.is_new; if (hash_map_entry.is_new) { - sumOfDistinctKeyLens += 2 + key.len + resultsSize; + aggCtx.totalResultsSize += 2 + key.len + aggCtx.resultsSize; } // utils.debugPrint("is_new?: {any}, key: {s} {d}, sumOfDistinctKeyLens: {d}\n", .{ hash_map_entry.is_new, key, key.len, sumOfDistinctKeyLens }); - Aggregates.aggregateProps(node, typeEntry, aggDefs[i..], accumulatorProp, hllAccumulator, hadAccumulated); - return sumOfDistinctKeyLens; + Aggregates.aggregateProps(node, aggDefs[i..], accumulatorProp, aggCtx); } pub inline fn finalizeGroupResults( - ctx: *Query.QueryCtx, + aggCtx: *Aggregates.AggCtx, groupByHashMap: *GroupByHashMap, - header: t.AggHeader, aggDefs: []u8, ) !void { var it = groupByHashMap.iterator(); @@ -136,20 +124,19 @@ pub inline fn finalizeGroupResults( const key = entry.key_ptr.*; const keyLen: u16 = @intCast(key.len); if (key.len > 0) { - try ctx.thread.query.append(keyLen); - try ctx.thread.query.append(key); + try aggCtx.queryCtx.thread.query.append(keyLen); + try aggCtx.queryCtx.thread.query.append(key); } const accumulatorProp = entry.value_ptr.*; - try Aggregates.finalizeResults(ctx, aggDefs, accumulatorProp, header.isSamplingSet, @bitSizeOf(t.GroupByKeyProp) / 8); + try Aggregates.finalizeResults(aggCtx, aggDefs, accumulatorProp, @bitSizeOf(t.GroupByKeyProp) / 8); } } pub inline fn finalizeRefsGroupResults( - ctx: *Query.QueryCtx, + aggCtx: *Aggregates.AggCtx, groupByHashMap: *GroupByHashMap, - header: t.AggRefsHeader, aggDefs: []u8, ) !void { var it = groupByHashMap.iterator(); @@ -159,12 +146,12 @@ pub inline fn finalizeRefsGroupResults( const keyLen: u16 = @intCast(key.len); if (key.len > 0) { - try ctx.thread.query.append(keyLen); - try ctx.thread.query.append(key); + try aggCtx.queryCtx.thread.query.append(keyLen); + try aggCtx.queryCtx.thread.query.append(key); } const accumulatorProp = entry.value_ptr.*; - try Aggregates.finalizeResults(ctx, aggDefs, accumulatorProp, header.isSamplingSet, @bitSizeOf(t.GroupByKeyProp) / 8); + try Aggregates.finalizeResults(aggCtx, aggDefs, accumulatorProp, @bitSizeOf(t.GroupByKeyProp) / 8); } } diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index 8b25fd41a8..6ea893731d 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -20,9 +20,6 @@ pub inline fn aggregateRefsProps( fromType: Selva.Type, i: *usize, ) !void { - var totalSize: usize = 0; - // utils.debugPrint("i: {d}\n", .{i.*}); - const header = utils.readNext(t.AggRefsHeader, q, i); // utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); @@ -33,21 +30,32 @@ pub inline fn aggregateRefsProps( // filter var it = try References.iterator(false, false, ctx.db, from, header.targetProp, fromType); + + var aggCtx = Aggregates.AggCtx{ + .queryCtx = ctx, + .typeEntry = it.dstType, + .limit = 1000, // MV: check it + .isSamplingSet = header.isSamplingSet, + .accumulatorSize = header.accumulatorSize, + .resultsSize = header.resultsSize, + }; + if (header.hasGroupBy) { var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - totalSize += GroupBy.iterator(ctx, &groupByHashMap, &it, 1000, false, undefined, q[i.*..], header.accumulatorSize, header.resultsSize, it.dstType, undefined, &totalSize); // TODO: hllAcc + + _ = GroupBy.iterator(&aggCtx, &groupByHashMap, &it, false, undefined, q[i.*..]); // TODO: hllAcc try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); - try ctx.thread.query.append(@as(u32, @intCast(totalSize))); - try GroupBy.finalizeRefsGroupResults(ctx, &groupByHashMap, header, q[i.*..]); + try ctx.thread.query.append(@as(u32, @intCast(aggCtx.totalResultsSize))); + try GroupBy.finalizeRefsGroupResults(&aggCtx, &groupByHashMap, q[i.*..]); } else { - _ = try Aggregates.iterator(ctx, &it, 1000, false, undefined, q[i.*..], accumulatorProp, it.dstType, undefined); // TODO: hllAcc + _ = try Aggregates.iterator(&aggCtx, &it, false, undefined, q[i.*..], accumulatorProp); // TODO: hllAcc try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); try ctx.thread.query.append(@as(u32, header.resultsSize)); // MV: recheck - try Aggregates.finalizeResults(ctx, q[i.*..], accumulatorProp, header.isSamplingSet, 0); + try Aggregates.finalizeResults(&aggCtx, q[i.*..], accumulatorProp, 0); } } diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 17924a3a53..5e099cbff8 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -307,7 +307,6 @@ pub fn aggregates( i += utils.sizeOf(t.AggHeader); const typeId = header.typeId; const typeEntry = try Node.getType(ctx.db, typeId); - const isSamplingSet = header.isSamplingSet; const accumulatorProp = try ctx.db.allocator.alloc(u8, header.accumulatorSize); @memset(accumulatorProp, 0); @@ -315,31 +314,42 @@ pub fn aggregates( const hllAccumulator = Selva.c.selva_string_create(null, Selva.c.HLL_INIT_SIZE, Selva.c.SELVA_STRING_MUTABLE); defer Selva.c.selva_string_free(hllAccumulator); + var aggCtx = Aggregates.AggCtx{ + .queryCtx = ctx, + .typeEntry = typeEntry, + .limit = header.limit, + .isSamplingSet = header.isSamplingSet, + .hllAccumulator = hllAccumulator, + .accumulatorSize = header.accumulatorSize, + .resultsSize = header.resultsSize, + .totalResultsSize = 0, + }; + var it = Node.iterator(false, typeEntry); switch (header.iteratorType) { .aggregate => { - nodeCnt = try Aggregates.iterator(ctx, &it, header.limit, false, undefined, q[i..], accumulatorProp, typeEntry, hllAccumulator); - try Aggregates.finalizeResults(ctx, q[i..], accumulatorProp, isSamplingSet, 0); + nodeCnt = try Aggregates.iterator(&aggCtx, &it, false, undefined, q[i..], accumulatorProp); + try Aggregates.finalizeResults(&aggCtx, q[i..], accumulatorProp, 0); }, .aggregateFilter => { const filter = utils.sliceNext(header.filterSize, q, &i); try Filter.prepare(filter, ctx, typeEntry); - nodeCnt = try Aggregates.iterator(ctx, &it, header.limit, true, filter, q[i..], accumulatorProp, typeEntry, hllAccumulator); - try Aggregates.finalizeResults(ctx, q[i..], accumulatorProp, isSamplingSet, 0); + nodeCnt = try Aggregates.iterator(&aggCtx, &it, true, filter, q[i..], accumulatorProp); + try Aggregates.finalizeResults(&aggCtx, q[i..], accumulatorProp, 0); }, .groupBy => { var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - nodeCnt = @intCast(GroupBy.iterator(ctx, &groupByHashMap, &it, header.limit, false, undefined, q[i..], header.accumulatorSize, header.resultsSize, typeEntry, hllAccumulator, undefined)); - try GroupBy.finalizeGroupResults(ctx, &groupByHashMap, header, q[i..]); + nodeCnt = @intCast(GroupBy.iterator(&aggCtx, &groupByHashMap, &it, false, undefined, q[i..])); + try GroupBy.finalizeGroupResults(&aggCtx, &groupByHashMap, q[i..]); }, .groupByFilter => { const filter = utils.sliceNext(header.filterSize, q, &i); try Filter.prepare(filter, ctx, typeEntry); var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - nodeCnt = @intCast(GroupBy.iterator(ctx, &groupByHashMap, &it, header.limit, true, filter, q[i..], header.accumulatorSize, header.resultsSize, typeEntry, hllAccumulator, undefined)); - try GroupBy.finalizeGroupResults(ctx, &groupByHashMap, header, q[i..]); + nodeCnt = @intCast(GroupBy.iterator(&aggCtx, &groupByHashMap, &it, true, filter, q[i..])); + try GroupBy.finalizeGroupResults(&aggCtx, &groupByHashMap, q[i..]); }, else => {}, } From 35b4bd13937f2b9eb0b7efd09d55c3733d237c9f Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 16 Feb 2026 15:50:44 +0100 Subject: [PATCH 278/449] Code cleanup --- clibs/lib/selva/fields.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 5ad22e951a..a0c6e2d8d3 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -511,7 +511,13 @@ static void write_refs( * A helper for remove_reference(). * @returns the original value. */ -static node_id_t del_single_ref(struct SelvaDb *db, struct SelvaNode *src_node, const struct EdgeFieldConstraint *efc, struct SelvaFields *fields, struct SelvaFieldInfo *nfo, bool ignore_dependent) +static node_id_t del_single_ref( + struct SelvaDb *db, + struct SelvaNode *src_node, + const struct EdgeFieldConstraint *efc, + struct SelvaFields *fields, + struct SelvaFieldInfo *nfo, + bool ignore_dependent) { void *vp = nfo2p(fields, nfo); struct SelvaNodeLargeReference ref; @@ -534,7 +540,12 @@ static node_id_t del_single_ref(struct SelvaDb *db, struct SelvaNode *src_node, /** * This is only a helper for remove_reference(). */ -static node_id_t del_multi_ref(struct SelvaDb *db, struct SelvaNode *src_node, const struct EdgeFieldConstraint *efc, struct SelvaNodeReferences *refs, size_t i) +static node_id_t del_multi_ref( + struct SelvaDb *db, + struct SelvaNode *src_node, + const struct EdgeFieldConstraint *efc, + struct SelvaNodeReferences *refs, + size_t i) { node_id_t dst_id; size_t id_set_len = refs->nr_refs; From a988c8547e40c4e189ccd8277daeef41be60b765 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 16 Feb 2026 15:51:52 +0100 Subject: [PATCH 279/449] Test circular dependency --- test/dependent.ts | 117 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 11 deletions(-) diff --git a/test/dependent.ts b/test/dependent.ts index 5eda47149b..5b21f49d2c 100644 --- a/test/dependent.ts +++ b/test/dependent.ts @@ -50,37 +50,37 @@ await test('dependent', async (t) => { }, } as const - await db.setSchema(schema) + const client = await db.setSchema(schema) const createShowTree = async () => { - const showId = await db.create('show', {}) - const editionId = await db.create('edition', { + const showId = await client.create('show', {}) + const editionId = await client.create('edition', { show: showId, }) - const sequenceId = await db.create('sequence', { + const sequenceId = await client.create('sequence', { edition: editionId, }) - const pageId = await db.create('page', { + const pageId = await client.create('page', { sequence: sequenceId, }) - await db.create('item', { + await client.create('item', { page: pageId, }) - await db.drain() + await client.drain() for (const type in schema.types) { - const len = (await db.query(type).get()).length + const len = (await client.query(type).get()).length equal(len, 1) } return showId } const showId = await createShowTree() - await db.delete('show', showId) - await db.drain() + await client.delete('show', showId) + await client.drain() for (const type in schema.types) { - equal((await db.query(type).get()).length, 0) + equal((await client.query(type).get()).length, 0) } await createShowTree() }) @@ -133,3 +133,98 @@ await test('del children', async (t) => { deepEqual(await client.query('child').get(), []) } }) + +await test('circle of friends', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + + await db.start({ clean: true }) + t.after(() => t.backup(db)) + + const client = await db.setSchema({ + types: { + human: { + name: { type: 'string', maxBytes: 8 }, + //friends: { type: 'references', items: { ref: 'human', prop: 'friends' } }, + friends: { type: 'references', items: { ref: 'human', prop: 'friends', dependent: true } }, + }, + }, + }) + + const h1 = client.create('human', { name: 'joe', }) + const h2 = client.create('human', { name: 'john', }) + const h3 = client.create('human', { name: 'jack', }) + + client.update('human', h2, { + friends: [h1, h3] + }) + //client.update('human', h3, { + // friends: [h2, h1], + //}) + client.update('human', h3, { + friends: { add: [ h2, h1 ] }, + }) + + deepEqual(await client.query('human').include('**').get(), [ + { + id: 1, + friends: [{ + id: 2, + name: "john" + }, + { + id: 3, + name: "jack" + }] + + }, + { + id: 2, + friends: [{ + id: 1, + name: "joe" + }, + { + id: 3, + name: "jack" + }] + + }, + { + id: 3, + friends: [{ + id: 2, + name: "john" + }, + { + id: 1, + name: "joe" + }] + + }, + ]) + + client.delete('human', 1) + deepEqual(await client.query('human').include('**').get(), [ + { + id: 2, + friends: [ + { + id: 3, + name: "jack" + }] + }, + { + id: 3, + friends: [ + { + id: 2, + name: "john" + }] + }, + ]) + + client.delete('human', 2) + deepEqual(await client.query('human').include('**').get(), []) +}) From c85ec41af8192895790565e171eafdd4b6923f46 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 08:50:25 +0100 Subject: [PATCH 280/449] db -> client --- test/insertOnly.ts | 10 +++++----- test/mem.ts | 18 +++++++++--------- test/vector.ts | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/insertOnly.ts b/test/insertOnly.ts index 2c4b646fe7..4f9322d8b3 100644 --- a/test/insertOnly.ts +++ b/test/insertOnly.ts @@ -10,7 +10,7 @@ await test('insert only => no delete', async (t) => { await db.start({ clean: true }) t.after(async () => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { audit: { insertOnly: true, @@ -21,10 +21,10 @@ await test('insert only => no delete', async (t) => { }, }) - const a = await db.create('audit', { v: 100 }) - await db.create('audit', { v: 100 }) - await throws(() => db.delete('audit', a)) - deepEqual(await db.query('audit', a).get(), { id: 1, v: 100 }) + const a = await client.create('audit', { v: 100 }) + await client.create('audit', { v: 100 }) + await throws(() => client.delete('audit', a)) + deepEqual(await client.query('audit', a).get(), { id: 1, v: 100 }) }) await test('colvec requires insertOnly', async (t) => { diff --git a/test/mem.ts b/test/mem.ts index f102b1f0b0..d02ad694d5 100644 --- a/test/mem.ts +++ b/test/mem.ts @@ -12,7 +12,7 @@ await test('mem', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { data: { props: { @@ -32,7 +32,7 @@ await test('mem', async (t) => { const rnd = fastPrng() for (let j = 0; j < repeat; j++) { // To keep many different blocks - await db.create('data', { + await client.create('data', { age: 666, name: 'BASIC ' + j, }) @@ -45,7 +45,7 @@ await test('mem', async (t) => { cnt++ } ids.push( - db.create('data', { + client.create('data', { age: i, name: `Mr FLAP ${i}`, a: x ? { id: ids[rnd(0, ids.length - 1)], $derp: i } : null, @@ -53,15 +53,15 @@ await test('mem', async (t) => { ) } - await db.drain() - await db.create('data', { + await client.drain() + await client.create('data', { age: 667, name: 'BASIC2 ' + j, }) equal( ( - await db + await client .query('data') .include('b') .filter('b', 'exists') @@ -72,11 +72,11 @@ await test('mem', async (t) => { ) for (let i = 0; i < amount; i++) { - db.delete('data', ids[i]) + client.delete('data', ids[i]) } - await db.drain() + await client.drain() - equal((await db.query('data').range(0, 10e6).get()).length, (j + 1) * 2) + equal((await client.query('data').range(0, 10e6).get()).length, (j + 1) * 2) } }) diff --git a/test/vector.ts b/test/vector.ts index 5d0fe9c4b7..eabde79768 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -1,4 +1,4 @@ -import { BasedDb } from '../src/index.js' +import { BasedDb, DbClient } from '../src/index.js' import test from './shared/test.js' import { deepEqual, equal } from './shared/assert.js' import { equals } from '../src/utils/index.js' @@ -12,14 +12,14 @@ const data = { car: [81.6, -72.1, 16, -20.2, 102], } -async function initDb(t: Parameters[1]>[0]): Promise { +async function initDb(t: Parameters[1]>[0]): Promise { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { data: { props: { @@ -37,14 +37,14 @@ async function initDb(t: Parameters[1]>[0]): Promise { From 15b3e40dd30c0badb7bd914a2cc0dfcc011e45c5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 08:50:36 +0100 Subject: [PATCH 281/449] Simplify & fix --- test/locales.ts | 48 ++++++++++++------------------------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/test/locales.ts b/test/locales.ts index aaabb96947..bded1b9f6f 100644 --- a/test/locales.ts +++ b/test/locales.ts @@ -1,8 +1,9 @@ -import assert from 'node:assert' import { BasedDb } from '../src/index.js' -import native from '../src/native.js' import test from './shared/test.js' -import { langCodesMap, Schema } from '../src/schema/index.js' +import { LangCode } from '../src/zigTsExports.js' + +const langs = [...Object.keys(LangCode)].filter((val) => val !== 'none') +const locales = Object.fromEntries(langs.map((l: keyof typeof LangCode) => [l, {}])) await test('locales', async (t) => { const db = new BasedDb({ @@ -11,13 +12,7 @@ await test('locales', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - const locales: Schema['locales'] = {} - const langs = [...langCodesMap.keys()].filter((val) => val !== 'none') - for (const key of langs) { - locales[key] = {} - } - - await db.setSchema({ + const client = await db.setSchema({ locales, types: { thing: { @@ -38,29 +33,23 @@ await test('locales', async (t) => { payload.text[key] = key } - db.create('thing', payload) + client.create('thing', payload) } - await db.drain() - - const things = await db.query('thing').get().toObject() + const things = await client.query('thing').get().toObject() for (const thing of things) { - const payload: any = { + const payload: typeof thing = { string: null, - text: {}, - } - - for (const key of langs) { - payload.text[key] = null + text: Object.fromEntries(Object.keys(thing.text).map((l) => [l, null])), } - db.update('thing', thing.id, payload) + client.update('thing', thing.id, payload) } - await db.drain() + await client.drain() - const updatedThings = await db.query('thing').get().toObject() + const updatedThings = await client.query('thing').get().toObject() for (const thing of updatedThings) { if (thing.string !== '') { @@ -73,16 +62,3 @@ await test('locales', async (t) => { } } }) - -await test('locales sanity check', async (t) => { - // prettier-ignore - const missingOnDarwin = new Set([ 'aa', 'ab', 'ak', 'sq', 'an', 'as', 'ae', 'ay', 'az', 'bn', 'bi', 'bs', 'br', 'my', 'km', 'ce', 'cv', 'kw', 'co', 'dv', 'dz', 'fo', 'ff', 'gd', 'gl', 'kl', 'gu', 'ht', 'ha', 'hi', 'ig', 'id', 'ia', 'iu', 'ik', 'ga', 'kn', 'ks', 'rw', 'ku', 'ky', 'lo', 'la', 'lv', 'lb', 'li', 'ln', 'mk', 'mg', 'ms', 'ml', 'mt', 'gv', 'mi', 'mn', 'ne', 'se', 'no', 'nb', 'nn', 'oc', 'or', 'om', 'os', 'pa', 'ps', 'fa', 'qu', 'rm', 'sm', 'sa', 'sc', 'sr', 'sd', 'si', 'so', 'st', 'nr', 'sw', 'ss', 'tl', 'tg', 'ta', 'tt', 'te', 'th', 'bo', 'ti', 'to', 'ts', 'tn', 'tk', 'ug', 'ur', 'uz', 've', 'vi', 'wa', 'cy', 'fy', 'wo', 'xh', 'yi', 'yo', 'zu', 'ka', 'cnr' ]) - const selvaLangs = new Set(native.selvaLangAll().split('\n')) - - langCodesMap.forEach((value, key) => { - if (value === 0) return - if (process.platform === 'darwin' && !missingOnDarwin.has(key)) { - assert(selvaLangs.has(key), `Lang '${key}' is found in selva`) - } - }) -}) From 6822e8d3f9fb71fd28f2c7c557b7e38b31b4805f Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 08:53:49 +0100 Subject: [PATCH 282/449] prettier --- test/dependent.ts | 104 ++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/test/dependent.ts b/test/dependent.ts index 5b21f49d2c..b65c4acf1d 100644 --- a/test/dependent.ts +++ b/test/dependent.ts @@ -96,12 +96,20 @@ await test('del children', async (t) => { const client = await db.setSchema({ types: { parent: { - children: { type: 'references', items: { ref: 'child', prop: 'parent' } } + children: { + type: 'references', + items: { ref: 'child', prop: 'parent' }, + }, }, child: { - parent: { type: 'reference', ref: 'parent', prop: 'children', dependent: true }, + parent: { + type: 'reference', + ref: 'parent', + prop: 'children', + dependent: true, + }, }, - } + }, }) for (let n = 1; n <= 5; n++) { @@ -113,7 +121,7 @@ await test('del children', async (t) => { } deepEqual(await client.query('parent', head).include('**').get(), { id: await head, - children: (await Promise.all(children)).map((id: number) => ({id})), + children: (await Promise.all(children)).map((id: number) => ({ id })), }) for (const child of children) { @@ -147,81 +155,89 @@ await test('circle of friends', async (t) => { human: { name: { type: 'string', maxBytes: 8 }, //friends: { type: 'references', items: { ref: 'human', prop: 'friends' } }, - friends: { type: 'references', items: { ref: 'human', prop: 'friends', dependent: true } }, + friends: { + type: 'references', + items: { ref: 'human', prop: 'friends', dependent: true }, + }, }, }, }) - const h1 = client.create('human', { name: 'joe', }) - const h2 = client.create('human', { name: 'john', }) - const h3 = client.create('human', { name: 'jack', }) + const h1 = client.create('human', { name: 'joe' }) + const h2 = client.create('human', { name: 'john' }) + const h3 = client.create('human', { name: 'jack' }) client.update('human', h2, { - friends: [h1, h3] + friends: [h1, h3], }) //client.update('human', h3, { // friends: [h2, h1], //}) client.update('human', h3, { - friends: { add: [ h2, h1 ] }, + friends: { add: [h2, h1] }, }) deepEqual(await client.query('human').include('**').get(), [ + { + id: 1, + friends: [ { - id: 1, - friends: [{ id: 2, - name: "john" - }, - { + name: 'john', + }, + { id: 3, - name: "jack" - }] - + name: 'jack', + }, + ], }, { - id: 2, - friends: [{ + id: 2, + friends: [ + { id: 1, - name: "joe" - }, - { + name: 'joe', + }, + { id: 3, - name: "jack" - }] - + name: 'jack', + }, + ], }, { - id: 3, - friends: [{ + id: 3, + friends: [ + { id: 2, - name: "john" - }, - { + name: 'john', + }, + { id: 1, - name: "joe" - }] - + name: 'joe', + }, + ], }, ]) client.delete('human', 1) deepEqual(await client.query('human').include('**').get(), [ { - id: 2, - friends: [ - { + id: 2, + friends: [ + { id: 3, - name: "jack" - }] + name: 'jack', + }, + ], }, { - id: 3, - friends: [ - { + id: 3, + friends: [ + { id: 2, - name: "john" - }] + name: 'john', + }, + ], }, ]) From d45268f9b2a1b22e7bf8b1cbf343a3eef71ab834 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 09:05:15 +0100 Subject: [PATCH 283/449] db -> client --- test/capped.ts | 42 +++++++++++++++++++++--------------------- test/enum.ts | 40 ++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/test/capped.ts b/test/capped.ts index ac81ad4d19..b11ea96130 100644 --- a/test/capped.ts +++ b/test/capped.ts @@ -9,7 +9,7 @@ await test('capped type', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { meas: { capped: 3, @@ -22,80 +22,80 @@ await test('capped type', async (t) => { }, }) - db.create('meas', { + client.create('meas', { temperature: 0, humidity: 99, }) - db.create('meas', { + client.create('meas', { temperature: 1, humidity: 98, }) - db.create('meas', { + client.create('meas', { temperature: 2, humidity: 97, wind: 50, }) - deepEqual(await db.query('meas').get(), [ + deepEqual(await client.query('meas').get(), [ { id: 1, temperature: 0, humidity: 99, wind: 10 }, { id: 2, temperature: 1, humidity: 98, wind: 10 }, { id: 3, temperature: 2, humidity: 97, wind: 50 }, ]) - db.create('meas', { + client.create('meas', { temperature: -100, humidity: 1, }) - deepEqual(await db.query('meas').get(), [ + deepEqual(await client.query('meas').get(), [ { id: 1, temperature: -100, humidity: 1, wind: 10 }, { id: 2, temperature: 1, humidity: 98, wind: 10 }, { id: 3, temperature: 2, humidity: 97, wind: 50 }, ]) - db.create('meas', { + client.create('meas', { temperature: -50, humidity: 1, wind: 5, }) - db.create('meas', { + client.create('meas', { temperature: -40, humidity: 1, }) - deepEqual(await db.query('meas').get(), [ + deepEqual(await client.query('meas').get(), [ { id: 1, temperature: -100, humidity: 1, wind: 10 }, { id: 2, temperature: -50, humidity: 1, wind: 5 }, { id: 3, temperature: -40, humidity: 1, wind: 10 }, ]) - db.create('meas', { + client.create('meas', { temperature: -50, humidity: 1, }) - db.create('meas', { + client.create('meas', { temperature: -40, humidity: 1, }) - db.create('meas', { + client.create('meas', { temperature: -50, humidity: 1, wind: 5, }) - db.create('meas', { + client.create('meas', { temperature: -40, humidity: 1, }) - db.create('meas', { + client.create('meas', { temperature: -50, humidity: 1, }) - db.create('meas', { + client.create('meas', { temperature: -40, humidity: 1, }) - deepEqual(await db.query('meas').get(), [ + deepEqual(await client.query('meas').get(), [ { id: 1, temperature: -40, humidity: 1, wind: 10 }, { id: 2, temperature: -50, humidity: 1, wind: 10 }, { id: 3, temperature: -40, humidity: 1, wind: 10 }, @@ -109,7 +109,7 @@ await test('capped references', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -135,12 +135,12 @@ await test('capped references', async (t) => { }, }) - const user = await db.create('user', {}) + const user = await client.create('user', {}) for (let i = 0; i < 10; i++) { - db.create('article', { latest: user }) + client.create('article', { latest: user }) } - deepEqual(await db.query('user', user).include('**').get(), { + deepEqual(await client.query('user', user).include('**').get(), { id: 1, latestArticles: [{ id: 6 }, { id: 7 }, { id: 8 }, { id: 9 }, { id: 10 }], }) diff --git a/test/enum.ts b/test/enum.ts index 9cd21b6edc..35c80159da 100644 --- a/test/enum.ts +++ b/test/enum.ts @@ -9,7 +9,7 @@ await test('enum', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -23,21 +23,21 @@ await test('enum', async (t) => { }, }) - const user1 = await db.create('user', { + const user1 = await client.create('user', { fancyness: 'mid', }) - const user2 = db.create('user', { + const user2 = client.create('user', { fancyness: 'fire', }) - db.create('user', { + client.create('user', { fancyness: 'beta', }) - db.create('user', {}) + client.create('user', {}) - deepEqual(await db.query('user').include('fancyness').get(), [ + deepEqual(await client.query('user').include('fancyness').get(), [ { id: 1, fancyness: 'mid' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -45,7 +45,7 @@ await test('enum', async (t) => { ]) deepEqual( - await db + await client .query('user') .include('fancyness') .filter('fancyness', '=', 'fire') @@ -56,33 +56,33 @@ await test('enum', async (t) => { ], ) - db.update('user', user1, { + client.update('user', user1, { fancyness: 'beta', }) - deepEqual(await db.query('user').include('fancyness').get(), [ + deepEqual(await client.query('user').include('fancyness').get(), [ { id: 1, fancyness: 'beta' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, { id: 4, fancyness: 'fire' }, ]) - await db.update('user', user1, { + await client.update('user', user1, { fancyness: null, }) throws(() => - db.update('user', user2, { + client.update('user', user2, { fancyness: 3, }), ) throws(() => - db.update('user', user2, { + client.update('user', user2, { fancyness: 'fond', }), ) - deepEqual(await db.query('user').include('fancyness').get(), [ + deepEqual(await client.query('user').include('fancyness').get(), [ { id: 1, fancyness: 'fire' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -97,7 +97,7 @@ await test('emoji enum', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { review: { props: { @@ -111,10 +111,10 @@ await test('emoji enum', async (t) => { }, }) - db.create('review', {}) - db.create('review', { score: '🙂' }) + client.create('review', {}) + client.create('review', { score: '🙂' }) - deepEqual(await db.query('review').include('score').get(), [ + deepEqual(await client.query('review').include('score').get(), [ { id: 1, score: '😐', @@ -125,10 +125,10 @@ await test('emoji enum', async (t) => { }, ]) - db.create('review', { score: '☹️' }) - db.create('review', { score: '😐' }) + client.create('review', { score: '☹️' }) + client.create('review', { score: '😐' }) deepEqual( - await db.query('review').include('score').sort('score', 'desc').get(), + await client.query('review').include('score').sort('score', 'desc').get(), [ { id: 2, score: '🙂' }, { id: 1, score: '😐' }, From 6b02243576730ad205dfd9a84e114b22d9c41277 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 10:03:41 +0100 Subject: [PATCH 284/449] Minimum maxModifySize is now 256 --- src/index.ts | 1 + test/flush.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6f4ecaa4e0..4d3fcfe130 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,7 @@ export const COMMON_SDB_FILE = 'common.sdb' export type BasedDbOpts = { path: string + /** Minimum: 256 */ maxModifySize?: number debug?: boolean | 'server' | 'client' saveIntervalInSeconds?: number diff --git a/test/flush.ts b/test/flush.ts index 6069b197a4..a1ee58722b 100644 --- a/test/flush.ts +++ b/test/flush.ts @@ -4,12 +4,12 @@ import test from './shared/test.js' await test('too large payload should throw, correct size should not', async (t) => { const db = new BasedDb({ path: t.tmp, - maxModifySize: 80, + maxModifySize: 256, }) await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -19,10 +19,10 @@ await test('too large payload should throw, correct size should not', async (t) }, }) - let error + let error: Error | null = null try { - db.create('user', { - name: 'cool string but too long for the max size unfortunately wow what the hell', + client.create('user', { + name: 'cool string but too long for the max size unfortunately wow what the hell'.repeat(4), }) } catch (e) { error = e @@ -36,10 +36,10 @@ await test('too large payload should throw, correct size should not', async (t) let i = 10 while (i--) { - db.create('user', { + client.create('user', { name: 'user' + i, }) } - await db.drain() + await client.drain() }) From c8915845ebea24092dddf7412666db18da8f0685 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 10:11:35 +0100 Subject: [PATCH 285/449] Add some jsdoc --- src/schema/def/types.ts | 10 +++++++--- src/schema/schema/reference.ts | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/schema/def/types.ts b/src/schema/def/types.ts index 9157953318..f0d6d12d97 100644 --- a/src/schema/def/types.ts +++ b/src/schema/def/types.ts @@ -15,14 +15,18 @@ import type { SchemaLocales } from '../schema/locales.js' export type PropDef = { __isPropDef: true schema: SchemaProp - prop: number // (0-250) + /** 0-250 */ + prop: number typeIndex: PropTypeEnum separate: boolean path: string[] start: number - len: number // bytes or count - compression?: 0 | 1 // 0 == none , 1 == standard deflate + /** bytes or count */ + len: number + /** 0 == none , 1 == standard deflate */ + compression?: 0 | 1 enum?: any[] + /** The node is deleted if this reference(s) prop becomes empty. */ dependent?: boolean // default here? validation: Validation diff --git a/src/schema/schema/reference.ts b/src/schema/schema/reference.ts index 3f48af3613..46c6f552de 100644 --- a/src/schema/schema/reference.ts +++ b/src/schema/schema/reference.ts @@ -16,6 +16,7 @@ type ReferenceProps = nested extends true ? { prop?: never; dependent?: never; [edge: `$${string}`]: never } : { prop: string + /** The node is deleted if this reference(s) prop becomes empty. */ dependent?: boolean [edge: `$${string}`]: | Exclude< From 95623c0e716ab72c227f99e39e1d97c62ad5ad8e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 11:35:09 +0100 Subject: [PATCH 286/449] selva.c shouldn't typically be used directly --- native/modify/modify.zig | 2 +- native/selva/references.zig | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 46acae1fcb..6795aaaa33 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -177,7 +177,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 const refTypeEntry = try Node.getType(db, refTypeId); const count = utils.read(u32, refs, 0); var x: usize = 4; - _ = selva.c.selva_fields_prealloc_refs(db.selva, node, propSchema, count); + References.preallocReferences2(db, node, propSchema, count); while (x < refs.len) { const meta = utils.readNext(t.ModifyReferencesMetaHeader, refs, &x); var refId = meta.id; diff --git a/native/selva/references.zig b/native/selva/references.zig index df9222de9b..8a44faf915 100644 --- a/native/selva/references.zig +++ b/native/selva/references.zig @@ -16,6 +16,10 @@ pub inline fn preallocReferences(ctx: *Modify.ModifyCtx, len: u64) void { _ = selva.c.selva_fields_prealloc_refs(ctx.db.selva.?, ctx.node.?, ctx.fieldSchema.?, len); } +pub inline fn preallocReferences2(db: *DbCtx, node: Node.Node, fieldSchema: Schema.FieldSchema, len: u64) void { + _ = selva.c.selva_fields_prealloc_refs(db.selva.?, node, fieldSchema, len); +} + pub inline fn getReference(node: Node.Node, fieldSchema: Schema.FieldSchema) ?ReferenceLarge { return selva.c.selva_fields_get_reference(node, fieldSchema); } From 812e2d21358408f12ed65f93606651354e04ea5e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 12:40:02 +0100 Subject: [PATCH 287/449] Don't trigger dependent check when refs is cleared - fields_del() acts on dependent flag on a reference(s) field - selva_fields_clear_references() ignores dependent flag on a reference(s) field --- clibs/lib/selva/fields.c | 17 +++++++++-------- test/dependent.ts | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index a0c6e2d8d3..0a89f43718 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -545,7 +545,8 @@ static node_id_t del_multi_ref( struct SelvaNode *src_node, const struct EdgeFieldConstraint *efc, struct SelvaNodeReferences *refs, - size_t i) + size_t i, + bool ignore_src_dependent) { node_id_t dst_id; size_t id_set_len = refs->nr_refs; @@ -641,7 +642,7 @@ static node_id_t del_multi_ref( assert(id_set_len == refs->nr_refs); assume(id_set_len == refs->nr_refs); - if ((efc->flags & EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT) && refs->nr_refs == 0) { + if (!ignore_src_dependent && (efc->flags & EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT) && refs->nr_refs == 0) { selva_expire_node(db, src_node->type, src_node->node_id, 0, SELVA_EXPIRE_NODE_STRATEGY_CANCEL_OLD); } @@ -698,7 +699,7 @@ static void clear_ref_dst(struct SelvaDb *db, const struct SelvaFieldSchema *fs_ ssize_t i = refs_find_node_i(refs, src_node_id); assert(i >= 0); - (void)del_multi_ref(db, dst, &fs_dst->edge_constraint, refs, i); + (void)del_multi_ref(db, dst, &fs_dst->edge_constraint, refs, i, false); } selva_mark_dirty(selva_get_type_by_index(db, fs_dst->edge_constraint.dst_node_type), src_node_id); @@ -782,7 +783,7 @@ static node_id_t remove_reference(struct SelvaDb *db, struct SelvaNode *src, con ssize_t i = (idx >= 0) ? idx : refs_find_node_i(refs, orig_dst); if (i >= 0 && i < refs->nr_refs) { - dst_node_id = del_multi_ref(db, src, &fs_src->edge_constraint, refs, i); + dst_node_id = del_multi_ref(db, src, &fs_src->edge_constraint, refs, i, ignore_src_dependent); } } } @@ -824,7 +825,7 @@ static size_t remove_references_tail( return removed; } -static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs) +static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, bool ignore_src_dependent) { struct SelvaTypeEntry *te = selva_get_type_by_index(db, node->type); struct SelvaFields *fields = &node->fields; @@ -864,7 +865,7 @@ static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct S goto out; } - removed_dst = remove_reference(db, node, fs, dst_node_id, i, false); + removed_dst = remove_reference(db, node, fs, dst_node_id, i, ignore_src_dependent); if (removed_dst != 0) { assert(removed_dst == dst_node_id); } @@ -880,7 +881,7 @@ static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct S __attribute__((nonnull(1, 2, 3))) static void remove_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaNodeReferences *refs = clear_references(db, node, fs); + struct SelvaNodeReferences *refs = clear_references(db, node, fs, false); if (refs) { switch (refs->size) { case SELVA_NODE_REFERENCE_SMALL: @@ -2011,7 +2012,7 @@ int selva_fields_del_ref(struct SelvaDb *db, struct SelvaNode *node, const struc void selva_fields_clear_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs) { assert(fs->type == SELVA_FIELD_TYPE_REFERENCES); - (void)clear_references(db, node, fs); + (void)clear_references(db, node, fs, true); } static void selva_fields_init_defaults(struct SelvaTypeEntry *te, struct SelvaFields *fields, const struct SelvaFieldsSchema *schema) diff --git a/test/dependent.ts b/test/dependent.ts index b65c4acf1d..6e9b6878c0 100644 --- a/test/dependent.ts +++ b/test/dependent.ts @@ -170,12 +170,12 @@ await test('circle of friends', async (t) => { client.update('human', h2, { friends: [h1, h3], }) - //client.update('human', h3, { - // friends: [h2, h1], - //}) client.update('human', h3, { - friends: { add: [h2, h1] }, + friends: [h2, h1], }) + //client.update('human', h3, { + // friends: { add: [h2, h1] }, + //}) deepEqual(await client.query('human').include('**').get(), [ { From de319504f5816a3530cd3712ede83dfaf5dfb979 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 17 Feb 2026 12:59:57 +0100 Subject: [PATCH 288/449] remove old modify files --- native/modify/edges.zig | 123 --------------------- native/modify/modify.zig | 3 + native/modify/reference.zig | 62 ----------- native/modify/references.zig | 202 ----------------------------------- package-lock.json | 4 + 5 files changed, 7 insertions(+), 387 deletions(-) delete mode 100644 native/modify/edges.zig delete mode 100644 native/modify/reference.zig delete mode 100644 native/modify/references.zig diff --git a/native/modify/edges.zig b/native/modify/edges.zig deleted file mode 100644 index 5466d87ed1..0000000000 --- a/native/modify/edges.zig +++ /dev/null @@ -1,123 +0,0 @@ -const Modify = @import("common.zig"); -const selva = @import("../selva/selva.zig").c; -const Schema = @import("../selva/schema.zig"); -const Node = @import("../selva/node.zig"); -const Fields = @import("../selva/fields.zig"); -const References = @import("../selva/references.zig"); -const utils = @import("../utils.zig"); -const errors = @import("../errors.zig"); -const update = @import("update.zig"); -const std = @import("std"); -const t = @import("../types.zig"); - -const read = utils.read; -const copy = utils.copy; -const ModifyCtx = Modify.ModifyCtx; - -fn isMainEmpty(val: []u8) bool { - var b = false; - for (val) |byte| { - b = b or byte != 0; - } - return b == false; -} - -pub fn writeEdges( - ctx: *ModifyCtx, - ref: References.ReferenceLarge, - data: []u8, -) !void { - var i: usize = 0; - const edgeConstraint = Schema.getEdgeFieldConstraint(ctx.fieldSchema.?); - const edgeNode = Node.getEdgeNode(ctx.db, edgeConstraint, ref); - const edgeId = ref.*.edge; - const edgeTypeId = edgeConstraint.*.edge_node_type; - if (edgeId > ctx.db.ids[edgeTypeId - 1]) { - ctx.db.ids[edgeTypeId - 1] = edgeId; - } - - while (i < data.len) { - const op: t.ModOp = @enumFromInt(data[i]); - const prop = data[i + 1]; - const propType: t.PropType = @enumFromInt(data[i + 2]); - i += 3; - - const edgeFieldSchema = Schema.getEdgeFieldSchema(ctx.db, edgeConstraint, prop) catch { - std.log.err("Edge field schema not found\n", .{}); - return; - }; - - var len: u32 = undefined; - var offset: u32 = 0; - - if (op == t.ModOp.updatePartial) { - len = read(u32, data, i); - const totalMainBufferLen = read(u16, data, i + 4); - offset = 6; - const mainBufferOffset = len - totalMainBufferLen; - const val = Fields.get(null, edgeNode, edgeFieldSchema, propType); - if (!isMainEmpty(val)) { - const edgeData = data[i + offset + mainBufferOffset .. i + len + offset]; - var j: usize = offset + i; - while (j < mainBufferOffset + offset + i) : (j += 6) { - const start = read(u16, data, j); - const l = read(u16, data, j + 2); - const fieldOp: t.ModOp = @enumFromInt(data[j + 4]); - if (fieldOp == t.ModOp.increment or fieldOp == t.ModOp.decrement) { - _ = update.incrementBuffer(op, @enumFromInt(data[j + 5]), val, edgeData); - } else { - copy(u8, val, edgeData[start .. start + l], start); - } - } - } else { - const edgeData = data[i + offset + mainBufferOffset .. i + len + offset]; - try Fields.set(edgeNode, edgeFieldSchema, edgeData); - } - } else switch (propType) { - t.PropType.reference => { - len = 4; - offset = 0; - const dstId = read(u32, data, i + offset); - if (Node.getNode(try Node.getRefDstType(ctx.db, edgeFieldSchema), dstId)) |dstNode| { - _ = try References.writeReference(ctx.db.selva, edgeNode, edgeFieldSchema, dstNode); - } else { - return errors.SelvaError.SELVA_ENOENT; - } - }, - t.PropType.references => { - len = read(u32, data, i); - offset = 4; - const edgeData = data[i + offset .. i + offset + len]; - // fix start - const address = @intFromPtr(edgeData.ptr); - const delta: u8 = @truncate(address % 4); - const d = if (delta == 0) 0 else 4 - delta; - const aligned = edgeData[d .. edgeData.len - 3 + d]; - if (d != 0) { - utils.move(aligned, edgeData[0 .. edgeData.len - 3]); - } - const u32ids = read([]u32, aligned, 0); - try References.putReferences(ctx, edgeNode, edgeFieldSchema, u32ids); - }, - t.PropType.cardinality => { - len = read(u32, data, i); - offset = 4; - const hll = try Fields.ensureEdgePropTypeString(ctx, ctx.node.?, edgeConstraint, ref, edgeFieldSchema); - selva.hll_init(hll, 8, true); // TBD: to get optionals from buffer - var it: usize = i + offset; - while (it < len) { - const hash = read(u64, data, it); - selva.hll_add(hll, hash); - it += 8; - } - }, - else => { - len = read(u32, data, i); - offset = 4; - const edgeData = data[i + offset .. i + offset + len]; - try Fields.set(edgeNode, edgeFieldSchema, edgeData); - }, - } - i += offset + len; - } -} diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 6795aaaa33..00f44cfcb5 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -385,6 +385,9 @@ pub fn modify( j += resItemSize; } + // 1. expire will just be checked on query + // 2. subscription will handle timers + // 3. keep Node.expire(db) for internal cleanup Node.expire(db); utils.write(result, j, 0); if (j < size) @memset(result[j..size], 0); diff --git a/native/modify/reference.zig b/native/modify/reference.zig deleted file mode 100644 index 92392e9f03..0000000000 --- a/native/modify/reference.zig +++ /dev/null @@ -1,62 +0,0 @@ -const Schema = @import("../selva/schema.zig"); -const Node = @import("../selva/node.zig"); -const References = @import("../selva/references.zig"); -const read = @import("../utils.zig").read; -const Modify = @import("common.zig"); -const errors = @import("../errors.zig"); -const std = @import("std"); -const ModifyCtx = Modify.ModifyCtx; -const edge = @import("edges.zig"); -const RefEdgeOp = @import("../types.zig").RefEdgeOp; - -pub fn updateReference(ctx: *ModifyCtx, data: []u8) !usize { - const op: RefEdgeOp = @enumFromInt(data[0]); - const hasEdges = RefEdgeOp.hasEdges(op); - const isTmpId = RefEdgeOp.isTmpId(op); - const refTypeId = Schema.getRefTypeIdFromFieldSchema(ctx.fieldSchema.?); - const refTypeEntry = try Node.getType(ctx.db, refTypeId); - var id = read(u32, data, 1); - - if (isTmpId) { - id = Modify.resolveTmpId(ctx, id); - } - - if (ctx.id == id and ctx.typeId == refTypeId) { - // don't ref yourself - return 5; - } - - var ref: ?References.ReferenceLarge = null; - - const oldRefDst = References.getReference(ctx.node.?, ctx.fieldSchema.?); - const dstType = try Node.getRefDstType(ctx.db, ctx.fieldSchema.?); - const dstNode = Node.getNodeFromReference(dstType, oldRefDst); - - if (dstNode) |d| { - if (Node.getNodeId(d) == id) { - ref = oldRefDst; - } - } - - if (ref == null) { - if (Node.getNode(refTypeEntry, id)) |dst| { - ref = try References.writeReference(ctx.db.selva, ctx.node.?, ctx.fieldSchema.?, dst); - } else { - return 5; //TODO WARN errors.SelvaError.SELVA_ENOENT - } - } - - if (hasEdges) { - const totalEdgesLen = read(u32, data, 5); - const len = 5 + totalEdgesLen; - if (ref) |r| { - const edges = data[9..len]; - try edge.writeEdges(ctx, r, edges); - } else { - std.log.err("EDGE MODIFY / Cannot find select ref to {d} \n", .{id}); - } - return len; - } - - return 5; -} diff --git a/native/modify/references.zig b/native/modify/references.zig deleted file mode 100644 index 682f01a1e6..0000000000 --- a/native/modify/references.zig +++ /dev/null @@ -1,202 +0,0 @@ -const assert = std.debug.assert; -const selva = @import("../selva/selva.zig").c; -const Schema = @import("../selva/schema.zig"); -const Node = @import("../selva/node.zig"); -const References = @import("../selva/references.zig"); -const read = @import("../utils.zig").read; -const Modify = @import("common.zig"); -const errors = @import("../errors.zig"); -const std = @import("std"); -const edge = @import("edges.zig"); -const t = @import("../types.zig"); -const RefEdgeOp = t.RefEdgeOp; -const move = @import("../utils.zig").move; -const modify = @import("./modify.zig"); -const ModifyCtx = Modify.ModifyCtx; - -pub fn writeReferences(ctx: *ModifyCtx, buf: []u8) anyerror!usize { - var i: usize = 0; - while (i < buf.len) { - const op: t.RefOp = @enumFromInt(buf[i]); - const data: []u8 = buf[i + 1 ..]; - i += 1; - switch (op) { - t.RefOp.set => { - const len: u32 = read(u32, data, 0); - const size: u32 = len * 4; - const u8IdsUnaligned = data[4 .. 4 + size]; - const address = @intFromPtr(u8IdsUnaligned.ptr); - const offset: u8 = @truncate(address % 4); - const u8IdsAligned = data[4 - offset .. 4 + size - offset]; - if (offset != 0) move(u8IdsAligned, u8IdsUnaligned); - const u32Ids = read([]u32, u8IdsAligned, 0); - try References.putReferences( - ctx, - ctx.node.?, - ctx.fieldSchema.?, - u32Ids, - ); - i += 4 + size; - }, - t.RefOp.setEdge => { - const len: u32 = read(u32, data, 0); - var j: u32 = 0; - - // modify.switchEdgeId(ctx, ctx.id, ); - - var localCtx = ctx.*; - const srcId = ctx.id; - const refField = ctx.field; - const refTypeId = Schema.getRefTypeIdFromFieldSchema(ctx.fieldSchema.?); - const refTypeEntry = try Node.getType(ctx.db, refTypeId); - - while (j < len) : (j += 1) { - const id = read(u32, data, 4); - if (Node.getNode(refTypeEntry, id)) |dstNode| { - _ = try References.insertReference(ctx, ctx.node.?, ctx.fieldSchema.?, dstNode, 0, false); - } - _ = try modify.switchEdgeId(&localCtx, srcId, id, refField); - i += 4; - i += try modify.writeData(&localCtx, data[i..]); - } - }, - // t.RefOp.setTmp => { - - // }, - // t.RefOp.setEdge => { - - // }, - // t.RefOp.setIndexTmp => { - - // }, - // t.RefOp.setEdgeIndex => { - - // }, - // t.RefOp.setEdgeIndexTmp => { - - // }, - // t.RefOp.setEdgeTmp => { - - // }, - t.RefOp.clear => { - References.clearReferences(ctx, ctx.node.?, ctx.fieldSchema.?); - }, - t.RefOp.del => {}, - t.RefOp.end => break, - } - } - - return i; -} - -pub fn updateReferences(ctx: *ModifyCtx, data: []u8) !usize { - const len: usize = read(u32, data, 0); - if (ctx.node == null) { - std.log.err("References update id: {d} node does not exist \n", .{ctx.id}); - return len; - } - - const refTypeId = Schema.getRefTypeIdFromFieldSchema(ctx.fieldSchema.?); - const refTypeEntry = try Node.getType(ctx.db, refTypeId); - const refsLen: usize = read(u32, data, 5); - var i: usize = 9; - - References.preallocReferences(ctx, refsLen); - - while (i < len) : (i += 5) { - const op: RefEdgeOp = @enumFromInt(data[i]); - const hasEdgeData = RefEdgeOp.hasEdges(op); - const hasIndex = RefEdgeOp.hasIndex(op); - const isTmpId = RefEdgeOp.isTmpId(op); - var id = read(u32, data, i + 1); - if (isTmpId) { - id = Modify.resolveTmpId(ctx, id); - } - - if (ctx.id == id and ctx.typeId == refTypeId) { - // don't ref yourself - if (hasEdgeData) { - const sizepos = if (hasIndex) i + 9 else i + 5; - const edgelen = read(u32, data, sizepos); - const edgepos = sizepos + 4; - const edges = data[edgepos .. edgepos + edgelen]; - i += edges.len + 4; - } - i += 4; - continue; - } - - const index: i32 = if (hasIndex) read(i32, data, i + 5) else -1; - var ref: References.ReferenceAny = undefined; - if (Node.getNode(refTypeEntry, id)) |dstNode| { - ref = try References.insertReference(ctx, ctx.node.?, ctx.fieldSchema.?, dstNode, index, hasIndex); - } else { - if (hasEdgeData) { - const sizepos = if (hasIndex) i + 9 else i + 5; - const edgelen = read(u32, data, sizepos); - i += edgelen; - } - i += 4; - // TODO WARN errors.SelvaError.SELVA_ENOENT - continue; - } - - if (hasEdgeData) { - const sizepos = if (hasIndex) i + 9 else i + 5; - const edgelen = read(u32, data, sizepos); - const edgepos = sizepos + 4; - const edges = data[edgepos .. edgepos + edgelen]; - if (ref.type == selva.SELVA_NODE_REFERENCE_LARGE) { - try edge.writeEdges(ctx, ref.p.large, edges); - } - i += edgelen + 4; - } - if (hasIndex) { - i += 4; - } - } - - return len; -} - -pub fn deleteReferences(ctx: *ModifyCtx, data: []u8) !usize { - const len: usize = read(u32, data, 0); - if (ctx.node == null) { - std.log.err("References delete id: {d} node does not exist \n", .{ctx.id}); - return len; - } - var i: usize = 1; - while (i < len) : (i += 4) { - const id = read(u32, data, i + 4); - try References.deleteReference( - ctx, - ctx.node.?, - ctx.fieldSchema.?, - id, - ); - } - return len; -} - -pub fn putReferences(ctx: *ModifyCtx, data: []u8) !usize { - const len: usize = read(u32, data, 0); - if (ctx.node == null) { - std.log.err("References delete id: {d} node does not exist \n", .{ctx.id}); - return len; - } - const idsUnAligned = data[5 .. len + 4]; - const address = @intFromPtr(idsUnAligned.ptr); - const offset: u8 = @truncate(address % 4); - const aligned = data[5 - offset .. len - offset + 4]; - if (offset != 0) { - move(aligned, idsUnAligned); - } - const u32ids = read([]u32, aligned, 0); - try References.putReferences( - ctx, - ctx.node.?, - ctx.fieldSchema.?, - u32ids, - ); - return len; -} diff --git a/package-lock.json b/package-lock.json index 71baa502af..20b4d7c1f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,6 +174,10 @@ "@based/locale-x86-64-gnu": "*" } }, + "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { + "dev": true, + "optional": true + }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", From e93ff885d8b9222463279ea432fb935cc1ce1db8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 15:25:30 +0100 Subject: [PATCH 289/449] Remove commented out code --- test/dependent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/dependent.ts b/test/dependent.ts index 6e9b6878c0..20341b0238 100644 --- a/test/dependent.ts +++ b/test/dependent.ts @@ -154,7 +154,6 @@ await test('circle of friends', async (t) => { types: { human: { name: { type: 'string', maxBytes: 8 }, - //friends: { type: 'references', items: { ref: 'human', prop: 'friends' } }, friends: { type: 'references', items: { ref: 'human', prop: 'friends', dependent: true }, From d6043abeef764b92e58f54e01d528f4bf44ab57b Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 15:46:17 +0100 Subject: [PATCH 290/449] Add hook to dirty marking --- clibs/include/selva/db.h | 2 ++ clibs/lib/selva/db.c | 13 +++++++++++++ clibs/lib/selva/include/db.h | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index fb4cca276a..cdb7cd53d9 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -65,6 +65,8 @@ void selva_db_destroy(struct SelvaDb *db) __attribute__((nonnull)); SELVA_EXPORT int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname_len) __attribute__((nonnull)); +void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook); + /** * Save the common/shared data of the database. */ diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 177854992b..9db368f59a 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -248,6 +248,10 @@ static uint32_t te_size(void) return slab_size; } +static void noop_dirty_hook(void *, node_type_t, node_id_t) +{ +} + struct SelvaDb *selva_db_create(size_t len, uint8_t schema[len]) { const size_t nr_types = schema_count_types(len, schema); @@ -262,6 +266,7 @@ struct SelvaDb *selva_db_create(size_t len, uint8_t schema[len]) db->expiring.expire_cb = expire_cb; db->expiring.cancel_cb = cancel_cb; db->dirfd = AT_FDCWD; + db->dirty_hook = noop_dirty_hook; selva_expire_init(&db->expiring); for (size_t i = 0; i < len;) { @@ -311,6 +316,11 @@ int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname return 0; } +void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook) +{ + db->dirty_hook = dirty_hook ?: noop_dirty_hook; +} + /** * Delete all nodes of a block. * Pretty safe as long as block_i is within the range. @@ -548,7 +558,10 @@ void selva_flush_node(struct SelvaDb *db, struct SelvaTypeEntry *type, struct Se void selva_mark_dirty(struct SelvaTypeEntry *te, node_id_t node_id) { if (node_id > 0) { + struct SelvaDb *db = containerof(te, typeof(*db), types[te->type - 1]); + selva_block_status_set(te, selva_node_id2block_i2(te, node_id), SELVA_TYPE_BLOCK_STATUS_DIRTY); + db->dirty_hook(nullptr, te->type, node_id); } } diff --git a/clibs/lib/selva/include/db.h b/clibs/lib/selva/include/db.h index dba3284501..b9b984fab0 100644 --- a/clibs/lib/selva/include/db.h +++ b/clibs/lib/selva/include/db.h @@ -134,6 +134,8 @@ struct SelvaDbExpireToken { node_type_t type; }; +typedef void (*selva_db_dirty_hook_t)(void *ctx, node_type_t type, node_id_t node_id); + /** * Database instance. */ @@ -148,6 +150,8 @@ struct SelvaDb { */ int dirfd; + selva_db_dirty_hook_t dirty_hook; + uint32_t sdb_version; /*!< Current SDB version. Set on common load and save. 0 if not saved/loaded. */ size_t nr_types; struct SelvaTypeEntry types[] __counted_by(nr_types); From a5084285e4023bc53e33fe0d5f88ac4b2fea3e81 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 15:47:41 +0100 Subject: [PATCH 291/449] doc --- clibs/include/selva/db.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index cdb7cd53d9..b4218811a5 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -65,6 +65,11 @@ void selva_db_destroy(struct SelvaDb *db) __attribute__((nonnull)); SELVA_EXPORT int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname_len) __attribute__((nonnull)); +/** + * Set a hook to the dirty marking system. + * The hook function will be called every time a node is marked dirty. + */ +SELVA_EXPORT void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook); /** From 4b680afa70195e709b8b647d759b3436fbc4e9e9 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 17 Feb 2026 15:58:13 +0100 Subject: [PATCH 292/449] dirty hook needs ctx --- clibs/include/selva/db.h | 2 +- clibs/lib/selva/db.c | 9 +++++---- clibs/lib/selva/include/db.h | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index b4218811a5..d9ac3378a0 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -70,7 +70,7 @@ int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname * The hook function will be called every time a node is marked dirty. */ SELVA_EXPORT -void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook); +void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook, void *ctx); /** * Save the common/shared data of the database. diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 9db368f59a..6d05be3ad5 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -266,7 +266,7 @@ struct SelvaDb *selva_db_create(size_t len, uint8_t schema[len]) db->expiring.expire_cb = expire_cb; db->expiring.cancel_cb = cancel_cb; db->dirfd = AT_FDCWD; - db->dirty_hook = noop_dirty_hook; + db->dirty_hook_fun = noop_dirty_hook; selva_expire_init(&db->expiring); for (size_t i = 0; i < len;) { @@ -316,9 +316,10 @@ int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname return 0; } -void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook) +void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hook, void *ctx) { - db->dirty_hook = dirty_hook ?: noop_dirty_hook; + db->dirty_hook_fun = dirty_hook ?: noop_dirty_hook; + db->dirty_hook_ctx = ctx; } /** @@ -561,7 +562,7 @@ void selva_mark_dirty(struct SelvaTypeEntry *te, node_id_t node_id) struct SelvaDb *db = containerof(te, typeof(*db), types[te->type - 1]); selva_block_status_set(te, selva_node_id2block_i2(te, node_id), SELVA_TYPE_BLOCK_STATUS_DIRTY); - db->dirty_hook(nullptr, te->type, node_id); + db->dirty_hook_fun(db->dirty_hook_ctx, te->type, node_id); } } diff --git a/clibs/lib/selva/include/db.h b/clibs/lib/selva/include/db.h index b9b984fab0..778e47dbcd 100644 --- a/clibs/lib/selva/include/db.h +++ b/clibs/lib/selva/include/db.h @@ -150,7 +150,8 @@ struct SelvaDb { */ int dirfd; - selva_db_dirty_hook_t dirty_hook; + selva_db_dirty_hook_t dirty_hook_fun; + void *dirty_hook_ctx; uint32_t sdb_version; /*!< Current SDB version. Set on common load and save. 0 if not saved/loaded. */ size_t nr_types; From e17987c0a5fc8cf37a795f5027235df7596d196b Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 17 Feb 2026 16:47:43 +0100 Subject: [PATCH 293/449] add expire --- native/modify/modify.zig | 33 +++++++++++-------- native/selva/node.zig | 9 +++--- native/thread/worker/worker.zig | 2 ++ native/types.zig | 12 +++---- src/db-client/modify/props.ts | 9 ++---- src/db-server/index.ts | 6 +++- src/schema/defs/props/fixed.ts | 2 +- src/schema/schema/timestamp.ts | 9 +++++- src/zigTsExports.ts | 56 ++++++++++++++++++--------------- test/query/expire.ts | 35 +++++++++++++++++++++ 10 files changed, 114 insertions(+), 59 deletions(-) create mode 100644 test/query/expire.ts diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 00f44cfcb5..2502ca12e7 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -13,15 +13,16 @@ const subs = @import("subscription.zig"); pub const subscription = subs.suscription; const resItemSize = utils.sizeOf(t.ModifyResultItem); -inline fn applyInc(comptime T: type, current: []u8, value: []u8, start: u16, op: t.ModifyIncrement) void { +inline fn applyInc(comptime T: type, current: []u8, value: []u8, start: u16, incrementPositive: bool) void { const curr = utils.read(T, current, start); const inc = utils.read(T, value, 0); - const res = switch (op) { - .increment => if (@typeInfo(T) == .float) curr + inc else curr +% inc, - .decrement => if (@typeInfo(T) == .float) curr - inc else curr -% inc, - else => return, - }; - utils.write(value, res, 0); + if (incrementPositive) { + const res = if (@typeInfo(T) == .float) curr + inc else curr +% inc; + utils.write(value, res, 0); + } else { + const res = if (@typeInfo(T) == .float) curr - inc else curr -% inc; + utils.write(value, res, 0); + } } inline fn writeResult(res: *t.ModifyResultItem, id: u32, err: t.ModifyError) void { @@ -57,16 +58,22 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 const main = utils.readNext(t.ModifyMainHeader, data, &j); const current = Fields.get(typeEntry, node, propSchema, t.PropType.microBuffer); const value = data[j .. j + main.size]; - if (main.increment != .none) { + + if (main.increment) { switch (main.type) { - .number => applyInc(f64, current, value, main.start, main.increment), - .timestamp => applyInc(i64, current, value, main.start, main.increment), - .int8, .uint8 => applyInc(u8, current, value, main.start, main.increment), - .int16, .uint16 => applyInc(u16, current, value, main.start, main.increment), - .int32, .uint32 => applyInc(u32, current, value, main.start, main.increment), + .number => applyInc(f64, current, value, main.start, main.incrementPositive), + .timestamp => applyInc(i64, current, value, main.start, main.incrementPositive), + .int8, .uint8 => applyInc(u8, current, value, main.start, main.incrementPositive), + .int16, .uint16 => applyInc(u16, current, value, main.start, main.incrementPositive), + .int32, .uint32 => applyInc(u32, current, value, main.start, main.incrementPositive), else => {}, } } + if (main.expire and main.size == 8) { + const typeId = Node.getNodeTypeId(node); + const id = Node.getNodeId(node); + Node.expireNode(db, typeId, id, @divTrunc(utils.read(i64, value, 0), 1000)); + } utils.copy(u8, current, value, main.start); j += main.size; } else { diff --git a/native/selva/node.zig b/native/selva/node.zig index 3143841b79..39c0771935 100644 --- a/native/selva/node.zig +++ b/native/selva/node.zig @@ -20,7 +20,8 @@ pub inline fn getType(ctx: *DbCtx, v: anytype) !Type { v, ); } else if (comptime @TypeOf(v) == selva.Node or - @TypeOf(v) == ?selva.Node) { + @TypeOf(v) == ?selva.Node) + { if (comptime @TypeOf(v) == ?selva.Node) { if (v == null) { return errors.SelvaError.SELVA_ENOENT; @@ -186,9 +187,9 @@ pub inline fn flushNode(db: *DbCtx, typeEntry: Type, node: Node) void { selva.c.selva_flush_node(db.selva, typeEntry, node); } -pub inline fn expireNode(ctx: *Modify.ModifyCtx, typeId: t.TypeId, nodeId: u32, ts: i64) void { - selva.c.selva_expire_node(ctx.db.selva, typeId, nodeId, ts, selva.c.SELVA_EXPIRE_NODE_STRATEGY_CANCEL_OLD); - selva.markDirty(ctx, typeId, nodeId); +pub inline fn expireNode(db: *DbCtx, typeId: t.TypeId, nodeId: u32, ts: i64) void { + selva.c.selva_expire_node(db.selva, typeId, nodeId, ts, selva.c.SELVA_EXPIRE_NODE_STRATEGY_CANCEL_OLD); + selva.markDirty(db, typeId, nodeId); } pub inline fn expire(db: *DbCtx) void { diff --git a/native/thread/worker/worker.zig b/native/thread/worker/worker.zig index 3d55094ce3..a97dbf82b1 100644 --- a/native/thread/worker/worker.zig +++ b/native/thread/worker/worker.zig @@ -10,6 +10,7 @@ const utils = @import("../../utils.zig"); const selva = @import("../../selva/selva.zig").c; const jemalloc = @import("../../jemalloc.zig"); const common = @import("../common.zig"); +const Node = @import("../../selva/node.zig"); const modifyNotPending = @import("modifyNotPending.zig").modifyNotPending; pub fn worker(threads: *Thread.Threads, thread: *common.Thread) !void { @@ -109,6 +110,7 @@ pub fn worker(threads: *Thread.Threads, thread: *common.Thread) !void { // does nothing but does trigger flush marked subs and maybe more in the future }, .modify => try Modify.modify(thread, m, threads.ctx), + .expire => Node.expire(threads.ctx), .loadBlock => try dump.loadBlock(thread, threads.ctx, m, op), .unloadBlock => try dump.unloadBlock(thread, threads.ctx, m, op), .loadCommon => try dump.loadCommon(thread, threads.ctx, m, op), diff --git a/native/types.zig b/native/types.zig index 206269dec4..943a3bc74a 100644 --- a/native/types.zig +++ b/native/types.zig @@ -39,6 +39,7 @@ pub const OpType = enum(u8) { unloadBlock = 129, loadCommon = 130, emptyMod = 133, + expire = 134, // -------------------- noOp = 255, @@ -121,16 +122,13 @@ pub const ModifyCreateRingHeader = packed struct { size: u32, }; -pub const ModifyIncrement = enum(u8) { - none = 0, - increment = 1, - decrement = 2, -}; - pub const ModifyMainHeader = packed struct { id: u8, type: PropType, - increment: ModifyIncrement, + increment: bool, + incrementPositive: bool, + expire: bool, + _padding: u5, size: u8, start: u16, }; diff --git a/src/db-client/modify/props.ts b/src/db-client/modify/props.ts index 97b34ae2a8..674d897681 100644 --- a/src/db-client/modify/props.ts +++ b/src/db-client/modify/props.ts @@ -6,7 +6,6 @@ import { import type { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import { Modify, - ModifyIncrement, pushModifyMainHeader, pushModifyPropHeader, writeModifyPropHeaderProps, @@ -47,11 +46,9 @@ export const serializeProps = ( start: prop.start, type: prop.type, size: prop.size, - increment: increment - ? increment < 0 - ? ModifyIncrement.decrement - : ModifyIncrement.increment - : ModifyIncrement.none, + increment: !!increment, + incrementPositive: increment > 0, + expire: ('expire' in prop.schema && prop.schema.expire) || false, }) if (val === null) { buf.fill(0, buf.length, buf.length + prop.size) diff --git a/src/db-server/index.ts b/src/db-server/index.ts index 1def58296f..8fc0cafed4 100644 --- a/src/db-server/index.ts +++ b/src/db-server/index.ts @@ -4,7 +4,7 @@ import { realStart, start, StartOpts } from './start.js' import { migrate } from './migrate/index.js' import { debugServer } from '../utils/debug.js' import { DbShared } from '../shared/DbBase.js' -import { writeSchemaFile, } from './schema.js' +import { writeSchemaFile } from './schema.js' import { save, SaveOpts } from './blocks.js' import { OpType, OpTypeEnum } from '../zigTsExports.js' @@ -15,6 +15,8 @@ import { } from '../schema/index.js' import { readUint32, writeUint32 } from '../utils/uint8.js' +const EXPIRE_BUF = new Uint8Array([0, 0, 0, 0, OpType.expire]) + export class DbServer extends DbShared { dbCtxExternal: any // pointer to zig dbCtx @@ -148,6 +150,7 @@ export class DbServer extends DbShared { if (onceListeners) { console.log('💤 Query already staged dont exec again', id) } else { + native.modify(EXPIRE_BUF, this.dbCtxExternal) native.query(buf, this.dbCtxExternal) } }) @@ -169,6 +172,7 @@ export class DbServer extends DbShared { if (qIdListeners) { console.log('💤 Subscription already staged dont exec again', id) } else { + native.modify(EXPIRE_BUF, this.dbCtxExternal) native.query(query, this.dbCtxExternal) } diff --git a/src/schema/defs/props/fixed.ts b/src/schema/defs/props/fixed.ts index 12ada4d095..63e58c5a0e 100644 --- a/src/schema/defs/props/fixed.ts +++ b/src/schema/defs/props/fixed.ts @@ -56,7 +56,7 @@ export const number = class Number extends BasePropDef { } export const timestamp = class Timestamp extends number { - override type = PropType.timestamp + override type: PropTypeEnum = PropType.timestamp override pushValue( buf: AutoSizedUint8Array, value: unknown, diff --git a/src/schema/schema/timestamp.ts b/src/schema/schema/timestamp.ts index da2f7ac857..72d090204e 100644 --- a/src/schema/schema/timestamp.ts +++ b/src/schema/schema/timestamp.ts @@ -1,4 +1,4 @@ -import { assert, isNumber, isRecord, isString } from './shared.js' +import { assert, isBoolean, isNumber, isRecord, isString } from './shared.js' import { parseBase, type Base } from './base.js' import { convertToTimestamp } from '../../utils/index.js' @@ -11,6 +11,7 @@ export type SchemaTimestamp = Base & { max?: strict extends true ? number : Timestamp default?: strict extends true ? number : Timestamp step?: strict extends true ? number : number | string + expire?: boolean } export const isTimestamp = (v: unknown): v is Timestamp => @@ -37,9 +38,15 @@ export const parseTimestamp = ( 'Invalid default timestamp', ) + assert( + def.expire === undefined || isBoolean(def.expire), + 'Invalid expire timestamp', + ) + return parseBase>(def, { type: 'timestamp', on: def.on, + expire: def.expire, min: convertToTsIfDefined(def.min), max: convertToTsIfDefined(def.max), step: convertToTsIfDefined(def.step), diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 58d7b6a3b3..15ec06137f 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -60,6 +60,7 @@ export const OpType = { unloadBlock: 129, loadCommon: 130, emptyMod: 133, + expire: 134, noOp: 255, } as const @@ -85,6 +86,7 @@ export const OpTypeInverse = { 129: 'unloadBlock', 130: 'loadCommon', 133: 'emptyMod', + 134: 'expire', 255: 'noOp', } as const @@ -110,6 +112,7 @@ export const OpTypeInverse = { unloadBlock, loadCommon, emptyMod, + expire, noOp */ export type OpTypeEnum = (typeof OpType)[keyof typeof OpType] @@ -626,29 +629,12 @@ export const pushModifyCreateRingHeader = ( return index } -export const ModifyIncrement = { - none: 0, - increment: 1, - decrement: 2, -} as const - -export const ModifyIncrementInverse = { - 0: 'none', - 1: 'increment', - 2: 'decrement', -} as const - -/** - none, - increment, - decrement - */ -export type ModifyIncrementEnum = (typeof ModifyIncrement)[keyof typeof ModifyIncrement] - export type ModifyMainHeader = { id: number type: PropTypeEnum - increment: ModifyIncrementEnum + increment: boolean + incrementPositive: boolean + expire: boolean size: number start: number } @@ -666,7 +652,11 @@ export const writeModifyMainHeader = ( offset += 1 buf[offset] = Number(header.type) offset += 1 - buf[offset] = Number(header.increment) + buf[offset] = 0 + buf[offset] |= (((header.increment ? 1 : 0) >>> 0) & 1) << 0 + buf[offset] |= (((header.incrementPositive ? 1 : 0) >>> 0) & 1) << 1 + buf[offset] |= (((header.expire ? 1 : 0) >>> 0) & 1) << 2 + buf[offset] |= ((0 >>> 0) & 31) << 3 offset += 1 buf[offset] = Number(header.size) offset += 1 @@ -682,8 +672,14 @@ export const writeModifyMainHeaderProps = { type: (buf: Uint8Array, value: PropTypeEnum, offset: number) => { buf[offset + 1] = Number(value) }, - increment: (buf: Uint8Array, value: ModifyIncrementEnum, offset: number) => { - buf[offset + 2] = Number(value) + increment: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 0 + }, + incrementPositive: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 1 + }, + expire: (buf: Uint8Array, value: boolean, offset: number) => { + buf[offset + 2] |= (((value ? 1 : 0) >>> 0) & 1) << 2 }, size: (buf: Uint8Array, value: number, offset: number) => { buf[offset + 3] = Number(value) @@ -700,7 +696,9 @@ export const readModifyMainHeader = ( const value: ModifyMainHeader = { id: buf[offset], type: (buf[offset + 1]) as PropTypeEnum, - increment: (buf[offset + 2]) as ModifyIncrementEnum, + increment: (((buf[offset + 2] >>> 0) & 1)) === 1, + incrementPositive: (((buf[offset + 2] >>> 1) & 1)) === 1, + expire: (((buf[offset + 2] >>> 2) & 1)) === 1, size: buf[offset + 3], start: readUint16(buf, offset + 4), } @@ -710,7 +708,9 @@ export const readModifyMainHeader = ( export const readModifyMainHeaderProps = { id: (buf: Uint8Array, offset: number) => buf[offset], type: (buf: Uint8Array, offset: number) => (buf[offset + 1]) as PropTypeEnum, - increment: (buf: Uint8Array, offset: number) => (buf[offset + 2]) as ModifyIncrementEnum, + increment: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 0) & 1)) === 1, + incrementPositive: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 1) & 1)) === 1, + expire: (buf: Uint8Array, offset: number) => (((buf[offset + 2] >>> 2) & 1)) === 1, size: (buf: Uint8Array, offset: number) => buf[offset + 3], start: (buf: Uint8Array, offset: number) => readUint16(buf, offset + 4), } @@ -728,7 +728,11 @@ export const pushModifyMainHeader = ( const index = buf.length buf.pushUint8(Number(header.id)) buf.pushUint8(Number(header.type)) - buf.pushUint8(Number(header.increment)) + buf.pushUint8(0) + buf.view[buf.length - 1] |= (((header.increment ? 1 : 0) >>> 0) & 1) << 0 + buf.view[buf.length - 1] |= (((header.incrementPositive ? 1 : 0) >>> 0) & 1) << 1 + buf.view[buf.length - 1] |= (((header.expire ? 1 : 0) >>> 0) & 1) << 2 + buf.view[buf.length - 1] |= ((0 >>> 0) & 31) << 3 buf.pushUint8(Number(header.size)) buf.pushUint16(Number(header.start)) return index diff --git a/test/query/expire.ts b/test/query/expire.ts new file mode 100644 index 0000000000..af827a3831 --- /dev/null +++ b/test/query/expire.ts @@ -0,0 +1,35 @@ +import wait from '../../src/utils/wait.js' +import { deepEqual, testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('query db', async (t) => { + const db = await testDb(t, { + locales: { + en: true, + nl: true, + }, + types: { + user: { + name: 'string', + expiresAt: { + type: 'timestamp', + expire: true, + }, + }, + }, + }) + + const expiresAt = Date.now() + 1e3 + const john = await db.create('user', { + name: 'john', + expiresAt, + }) + + deepEqual(await db.query2('user', john).get(), { + id: 1, + name: 'john', + expiresAt, + }) + await wait(1e3) + deepEqual(await db.query2('user', john).get(), null) +}) From a03137d8f6878dbd323954d461b4ec707670c89f Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 17 Feb 2026 18:36:01 -0300 Subject: [PATCH 294/449] basic test is back --- test/aggregate/basic.ts | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index b803ac860e..7c1f61816a 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -461,13 +461,21 @@ await test('numeric types', async (t) => { PL: -50, FI: -50.999, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) // create with an array not implemented yet + const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) // create with an array not implemented yet db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) - db.create('sequence', { votes: au2 }) - db.create('sequence', { votes: br1 }) + // db.create('sequence', { votes: nl1 }) + // db.create('sequence', { votes: nl2 }) + // db.create('sequence', { votes: au1 }) + // db.create('sequence', { votes: au2 }) + // db.create('sequence', { votes: br1 }) + + // await db + // .query('vote') + // .sum('NL', 'FI') + // // + // .groupBy('region') + // .get() + // .inspect() deepEqual( await db.query('vote').groupBy('region').get().toObject(), @@ -497,6 +505,7 @@ await test('numeric types', async (t) => { }, 'sum, main, group by', ) + deepEqual( await db.query('vote').count().groupBy('region').get().toObject(), { @@ -512,6 +521,7 @@ await test('numeric types', async (t) => { }, 'count, main, group by', ) + deepEqual( await db .query('vote') @@ -538,6 +548,7 @@ await test('numeric types', async (t) => { }, 'avg, main, group by', ) + deepEqual( await db .query('vote') @@ -564,6 +575,7 @@ await test('numeric types', async (t) => { }, 'hmean, main, group by', ) + deepEqual( await db .query('vote') @@ -587,6 +599,7 @@ await test('numeric types', async (t) => { }, 'stddev, main, group by, pop', ) + deepEqual( await db .query('vote') @@ -610,6 +623,7 @@ await test('numeric types', async (t) => { }, 'stddev, main, group by, default=sample', ) + deepEqual( await db .query('vote') @@ -647,6 +661,7 @@ await test('numeric types', async (t) => { }, 'variance, main, group by, sample', ) + deepEqual( await db.query('vote').var('NL', 'PL').groupBy('region').get().toObject(), { @@ -656,6 +671,7 @@ await test('numeric types', async (t) => { }, 'variance, main, group by, default (sample)', ) + deepEqual( await db .query('vote') @@ -685,6 +701,7 @@ await test('numeric types', async (t) => { }, 'max, main, group by', ) + deepEqual( await db .query('vote') @@ -764,6 +781,7 @@ await test('numeric types', async (t) => { ], 'hmean, references, not grouped', ) + deepEqual( await db .query('sequence') @@ -807,6 +825,7 @@ await test('numeric types', async (t) => { ], 'count, references, group by', ) + deepEqual( await db .query('sequence') From 7b5bf80a3006d61ad29eafa5e205da699100e907 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 17 Feb 2026 19:29:37 -0300 Subject: [PATCH 295/449] fixing/uncommenting mostly agg tests --- test/aggregate/basic.ts | 41 ++--------- test/aggregate/deep.ts | 8 +-- test/aggregate/dev.ts | 4 +- test/aggregate/groupBY.ts | 139 +++++++++++++++---------------------- test/aggregate/multiple.ts | 54 ++++---------- test/aggregate/temporal.ts | 1 + 6 files changed, 82 insertions(+), 165 deletions(-) diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 7c1f61816a..27b38e4459 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -56,12 +56,8 @@ await test('sum top level', async (t) => { country: 'aa', AU: 15, }) - db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1] }) // create with an array not implemented yet + const s = db.create('sequence', { votes: [nl1, nl2, au1] }) // top level ---------------------------------- deepEqual( @@ -275,13 +271,7 @@ await test('two phase accumulation', async (t) => { country: 'Brazil', NL: 50, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) // create multiple with an array is not implemented yet - db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) - db.create('sequence', { votes: au2 }) - db.create('sequence', { votes: br1 }) + const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) deepEqual( await db.query('vote').stddev('NL', { mode: 'sample' }).get().toObject(), @@ -461,21 +451,7 @@ await test('numeric types', async (t) => { PL: -50, FI: -50.999, }) - const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) // create with an array not implemented yet - db.drain() - // db.create('sequence', { votes: nl1 }) - // db.create('sequence', { votes: nl2 }) - // db.create('sequence', { votes: au1 }) - // db.create('sequence', { votes: au2 }) - // db.create('sequence', { votes: br1 }) - - // await db - // .query('vote') - // .sum('NL', 'FI') - // // - // .groupBy('region') - // .get() - // .inspect() + const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) deepEqual( await db.query('vote').groupBy('region').get().toObject(), @@ -1098,28 +1074,25 @@ await test('range', async (t) => { }, }) - const rnd = fastPrng() + const rnd = fastPrng(new Date().getTime()) for (let i = 0; i < 10; i++) { - const d = new Date('11/11/2024 11:00-3') + const d = new Date('11/12/2024 00:00-3') db.create('job', { - day: new Date(d.getTime() + Math.random() * 1e7), + day: new Date(d.getTime() + 3600 * 1000 * rnd(2, 3)), tip: Math.random() * 20, }) const s = db.create('state', { name: `statelala ${rnd(0, 2)}`, }) - db.drain() const t = db.create('territory', { name: ter[rnd(0, ter.length - 1)], flap: Math.random() * 100, state: s, }) - db.drain() db.create('employee', { name: `emplala ${rnd(0, 10)}`, area: t, }) - db.drain() } deepEqual( @@ -1128,7 +1101,7 @@ await test('range', async (t) => { .query('job') .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) .avg('tip') - .range(0, 2) + // .range(0, 2) .get() .toObject(), ).length, diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index e226535610..d8fd60a8ad 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -60,8 +60,7 @@ await test('sum branched includes', async (t) => { country: 'aa', AU: 15, }) - db.drain() - const s = db.create('sequence', { votes: [nl1, nl2, au1] }) // create multiple with an array is not implemented yet + const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( await db @@ -160,7 +159,6 @@ await test('count branched includes', async (t) => { country: 'aa', AU: 15, }) - db.drain() const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( @@ -681,17 +679,15 @@ await test('group by reference ids', async (t) => { model: 'VW Beatle', year: 1989, }) - db.drain() const t1 = db.create('trip', { distance: 523.1, vehicle: v2, }) - db.drain() const d1 = db.create('driver', { name: 'Luc Ferry', rank: 5, vehicle: v2, - trips: t1, + trips: [t1], }) deepEqual( diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index b6f3b002d2..0b45a925e2 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -231,8 +231,8 @@ await test('yyy', async (t) => { }) .get() - result.debug() - result.inspect() + // result.debug() + // result.inspect() deepEqual( result.toObject(), diff --git a/test/aggregate/groupBY.ts b/test/aggregate/groupBY.ts index 31478c0b1a..6361f17869 100644 --- a/test/aggregate/groupBY.ts +++ b/test/aggregate/groupBY.ts @@ -56,11 +56,7 @@ await test('sum group by', async (t) => { country: 'aa', AU: 15, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1] }) - db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) + const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( await db.query('vote').sum('NL', 'AU').groupBy('country').get().toObject(), @@ -80,7 +76,7 @@ await test('sum group by', async (t) => { // deepEqual( // await db // .query('vote') - // .filter('country', '=', 'bb') + // .filter('country', '=', 'bb') // filter string not implemented yet // .groupBy('country') // .sum('NL', 'AU') // .get() @@ -142,11 +138,7 @@ await test('count group by', async (t) => { country: 'aa', AU: 15, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1] }) - db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) + const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( await db.query('vote').count().groupBy('country').get().toObject(), @@ -164,7 +156,7 @@ await test('count group by', async (t) => { // deepEqual( // await db // .query('vote') - // .filter('country', '=', 'bb') + // .filter('country', '=', 'bb') // filter string not implemented yet // .groupBy('country') // .count() // .get() @@ -241,57 +233,36 @@ await test('variable key sum', async (t) => { flap: 80, }) - // const strudelArticle = db.create('article', { - // name: 'The wonders of Strudel', - // contributors: [mrSnurp, flippie, derpie, dinkelDoink], - // }) - db.drain() - db.create('article', { + const strudelArticle = db.create('article', { name: 'The wonders of Strudel', - contributors: mrSnurp, - }) - db.create('article', { - name: 'The wonders of Strudel', - contributors: flippie, - }) - db.create('article', { - name: 'The wonders of Strudel', - contributors: derpie, - }) - db.create('article', { - name: 'The wonders of Strudel', - contributors: dinkelDoink, + contributors: [mrSnurp, flippie, derpie, dinkelDoink], }) - // const stupidity = db.create('article', { - // name: 'Les lois fondamentales de la stupidité humaine', - // contributors: [cipolla], - // }) - db.create('article', { + const stupidity = db.create('article', { name: 'Les lois fondamentales de la stupidité humaine', - contributors: cipolla, + contributors: [cipolla], }) - // deepEqual( - // await db - // .query('article') - // .include((q) => q('contributors').sum('flap'), 'name') - // .get() - // .toObject(), - // [ - // { - // id: 1, - // name: 'The wonders of Strudel', - // contributors: { flap: { sum: 100 } }, - // }, - // { - // id: 2, - // name: 'Les lois fondamentales de la stupidité humaine', - // contributors: { flap: { sum: 80 } }, - // }, - // ], - // 'sum, branched query, var len string', - // ) + deepEqual( + await db + .query('article') + .include((q) => q('contributors').sum('flap'), 'name') + .get() + .toObject(), + [ + { + id: 1, + name: 'The wonders of Strudel', + contributors: { flap: { sum: 100 } }, + }, + { + id: 2, + name: 'Les lois fondamentales de la stupidité humaine', + contributors: { flap: { sum: 80 } }, + }, + ], + 'sum, branched query, var len string', + ) deepEqual( await db.query('user').groupBy('name').sum('flap').get().toObject(), @@ -316,33 +287,33 @@ await test('variable key sum', async (t) => { 'sum, groupBy, main, $undefined groupBy key', ) - // deepEqual( - // await db - // .query('article') - // .include((select) => { - // select('contributors').groupBy('name').sum('flap') - // }) - // .get() - // .toObject(), - // [ - // { - // id: 1, - // contributors: { - // Flippie: { flap: { sum: 20 } }, - // 'Mr snurp': { flap: { sum: 10 } }, - // Derpie: { flap: { sum: 30 } }, - // 'Dinkel Doink': { flap: { sum: 40 } }, - // }, - // }, - // { - // id: 2, - // contributors: { - // 'Carlo Cipolla': { flap: { sum: 80 } }, - // }, - // }, - // ], - // 'sum, branched query, groupBy, references', - // ) + deepEqual( + await db + .query('article') + .include((select) => { + select('contributors').groupBy('name').sum('flap') + }) + .get() + .toObject(), + [ + { + id: 1, + contributors: { + Flippie: { flap: { sum: 20 } }, + 'Mr snurp': { flap: { sum: 10 } }, + Derpie: { flap: { sum: 30 } }, + 'Dinkel Doink': { flap: { sum: 40 } }, + }, + }, + { + id: 2, + contributors: { + 'Carlo Cipolla': { flap: { sum: 80 } }, + }, + }, + ], + 'sum, branched query, groupBy, references', + ) }) await test('group by unique numbers', async (t) => { diff --git a/test/aggregate/multiple.ts b/test/aggregate/multiple.ts index ba6a9dadc0..117e95c8b4 100644 --- a/test/aggregate/multiple.ts +++ b/test/aggregate/multiple.ts @@ -79,13 +79,7 @@ await test('multiple functions', async (t) => { PL: -50, FI: -50.999, }) - // const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) - db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) - db.create('sequence', { votes: au2 }) - db.create('sequence', { votes: br1 }) + const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) deepEqual( await db.query('vote').sum('NL').sum('NO').max('NL').min('NL').get(), @@ -110,23 +104,9 @@ await test('multiple functions', async (t) => { 'multiple func main with groupBy', ) - // const j = db.create('vote', { - // region: 'Great', - // judges: ['lala', 'lele', 'lili'], - // }) - - db.drain() - db.create('vote', { - region: 'Great', - judges: 'lala', - }) - db.create('vote', { - region: 'Great', - judges: 'lele', - }) - db.create('vote', { + const j = db.create('vote', { region: 'Great', - judges: 'lili', + judges: ['lala', 'lele', 'lili'], }) const multi = await db @@ -152,8 +132,7 @@ await test('multiple functions', async (t) => { max: 50, }, NO: { - // avg: -29.333333333333332, // originally one node because of multiref - avg: -22, // 3 nodes temporarely + avg: -29.333333333333332, sum: -176, }, judges: { @@ -220,8 +199,7 @@ await test('multiple functions', async (t) => { max: 50, }, NO: { - // avg: -25, // also one node only originally - avg: -12.5, + avg: -25, sum: -50, }, judges: { @@ -241,12 +219,12 @@ await test('multiple functions', async (t) => { PT: { sum: 186, }, - // NO: { - // stddev: 21.518983866964227, // also one node only originally - // }, - // count: 6, // also one node only originally - NO: { stddev: 22.696758736499294 }, // std([-10,-23,-43,-50,-50,0,0,0]) ans = 22.697 - count: 8, + NO: { + stddev: 21.518983866964227, // also one node only originally + }, + count: 6, // also one node only originally + // NO: { stddev: 22.696758736499294 }, // std([-10,-23,-43,-50,-50,0,0,0]) ans = 22.697 + // count: 8, }, 'multiple main + count no groupBy', ) @@ -291,12 +269,10 @@ await test('multiple functions', async (t) => { PT: { sum: 50, }, - // NO: { - // stddev: 35.35533905932738, - // }, - // count: 2, - NO: { stddev: 25 }, - count: 4, + NO: { + stddev: 35.35533905932738, + }, + count: 2, }, }, 'multiple main + count groupBy', diff --git a/test/aggregate/temporal.ts b/test/aggregate/temporal.ts index 87892a3cac..cbbaadd167 100644 --- a/test/aggregate/temporal.ts +++ b/test/aggregate/temporal.ts @@ -327,6 +327,7 @@ await test('cardinality with dates', async (t) => { // console.log('Total Eaters by Month: ', eatersByMonth) deepEqual( eatersByMonth, + //@ts-ignore [{ Jun: { cardinality: 5 } }, { Jul: { cardinality: 11 } }], 'Total Eaters by Month', ) From cf39fc3658fae1e38ccd353031633614158d61e0 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 08:05:28 +0100 Subject: [PATCH 296/449] Move type --- clibs/include/selva/types.h | 2 ++ clibs/lib/selva/include/db.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clibs/include/selva/types.h b/clibs/include/selva/types.h index ad6e8d0539..80adc12167 100644 --- a/clibs/include/selva/types.h +++ b/clibs/include/selva/types.h @@ -135,6 +135,8 @@ struct SelvaNodeRes { enum SelvaTypeBlockStatus block_status; } __designated_init; +typedef void (*selva_db_dirty_hook_t)(void *ctx, node_type_t type, node_id_t node_id); + SELVA_EXPORT bool selva_is_valid_field_type(enum SelvaFieldType ftype); diff --git a/clibs/lib/selva/include/db.h b/clibs/lib/selva/include/db.h index 778e47dbcd..a40c627f69 100644 --- a/clibs/lib/selva/include/db.h +++ b/clibs/lib/selva/include/db.h @@ -134,8 +134,6 @@ struct SelvaDbExpireToken { node_type_t type; }; -typedef void (*selva_db_dirty_hook_t)(void *ctx, node_type_t type, node_id_t node_id); - /** * Database instance. */ From c8b2355f2e7e9263cdaa6ebaed75a49da7133c91 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 08:29:28 +0100 Subject: [PATCH 297/449] No dirty ranges in zig --- native/modify/common.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/native/modify/common.zig b/native/modify/common.zig index 462798277e..10ed065764 100644 --- a/native/modify/common.zig +++ b/native/modify/common.zig @@ -22,7 +22,6 @@ pub const ModifyCtx = struct { node: ?Node.Node, fieldType: t.PropType, db: *DbCtx, - // dirtyRanges: std.AutoArrayHashMap(u64, f64), subTypes: ?*Subscription.TypeSubscriptionCtx, // prob want to add subs here idSubs: ?[]*Subscription.Sub, batch: []u8, From 05d1c1d88eedeea63b9171a77f2273dde73fa251 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 08:39:34 +0100 Subject: [PATCH 298/449] dead code --- native/modify/create.zig | 106 --------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 native/modify/create.zig diff --git a/native/modify/create.zig b/native/modify/create.zig deleted file mode 100644 index e1af71d574..0000000000 --- a/native/modify/create.zig +++ /dev/null @@ -1,106 +0,0 @@ -const Modify = @import("common.zig"); -const selva = @import("../selva/selva.zig"); -const Node = @import("../selva/node.zig"); -const Fields = @import("../selva/fields.zig"); -const utils = @import("../utils.zig"); -const sort = @import("../sort/sort.zig"); -const errors = @import("../errors.zig"); -const references = @import("references.zig"); -const reference = @import("reference.zig"); -const std = @import("std"); -const lib = @import("../lib.zig"); -const subs = @import("subscription.zig"); -const t = @import("../types.zig"); - -const read = utils.read; - -const ModifyCtx = Modify.ModifyCtx; -const getOrCreateShard = Modify.getOrCreateShard; -const getSortIndex = Modify.getSortIndex; - -pub fn createField(ctx: *ModifyCtx, data: []u8) !usize { - // subs.stage(ctx, subs.Op.create); - - switch (ctx.fieldType) { - t.PropType.references => { - return references.writeReferences(ctx, data); - }, - t.PropType.reference => { - return reference.updateReference(ctx, data); - }, - t.PropType.vector => { - const len = read(u32, data, 0); - const padding = data[4]; - const slice = data[8 - padding .. len + 4]; - try Fields.setMicroBuffer(ctx.node.?, ctx.fieldSchema.?, slice); - return len + 4; - }, - t.PropType.colVec => { - const len = read(u32, data, 0); - const padding = data[4]; - const slice = data[8 - padding .. len + 4]; - Fields.setColvec(ctx.typeEntry.?, ctx.id, ctx.fieldSchema.?, slice); - return len + 4; - }, - t.PropType.cardinality => { - const hllMode = if (data[0] == 0) true else false; - const hllPrecision = data[1]; - const offset = 2; - const len = read(u32, data, offset); - const hll = try Fields.ensurePropTypeString(ctx, ctx.fieldSchema.?); - selva.c.hll_init(hll, hllPrecision, hllMode); - var i: usize = 4 + offset; - while (i < (len * 8) + offset) { - const hash = read(u64, data, i); - selva.c.hll_add(hll, hash); - i += 8; - } - const newCount = selva.c.hll_count(hll); - addSortIndexOnCreation(ctx, newCount[0..4]) catch null; - return len * 8 + 6; - }, - else => { - const len = read(u32, data, 0); - const slice = data[4 .. len + 4]; - addSortIndexOnCreation(ctx, slice) catch null; - if (ctx.fieldType == t.PropType.alias) { - if (slice.len > 0) { - const old = try Fields.setAlias(ctx.typeEntry.?, ctx.id, ctx.field, slice); - if (old > 0) { - if (ctx.currentSortIndex != null) { - sort.remove(ctx.thread.decompressor, ctx.currentSortIndex.?, slice, Node.getNode(ctx.typeEntry.?, old).?); - } - selva.markDirty(ctx, ctx.typeId, old); - } - } - } else { - try Fields.set(ctx.node.?, ctx.fieldSchema.?, slice); - } - return len + 4; - }, - } -} - -pub fn addSortIndexOnCreation(ctx: *ModifyCtx, slice: []u8) !void { - if (ctx.field == 0) { - if (ctx.typeSortIndex != null) { - var it = ctx.typeSortIndex.?.main.iterator(); - while (it.next()) |entry| { - const sI = entry.value_ptr.*; - sort.insert(ctx.thread.decompressor, sI, slice, ctx.node.?); - } - } - } else if (ctx.currentSortIndex != null) { - sort.insert(ctx.thread.decompressor, ctx.currentSortIndex.?, slice, ctx.node.?); - } else if (ctx.typeSortIndex != null and ctx.fieldType == t.PropType.text) { - const sIndex = sort.getSortIndex( - ctx.db.sortIndexes.get(ctx.typeId), - ctx.field, - 0, - @enumFromInt(slice[0]), - ); - if (sIndex) |s| { - sort.insert(ctx.thread.decompressor, s, slice, ctx.node.?); - } - } -} From e9add24cf7fe4177c5a2942e31425d66748df6bd Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 08:39:54 +0100 Subject: [PATCH 299/449] Remove unnecessary selva.markDirty() calls --- native/modify/delete.zig | 8 -------- native/modify/modify.zig | 8 ++------ native/selva/selva.zig | 12 ++++++++++-- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/native/modify/delete.zig b/native/modify/delete.zig index 5cb8c4de8e..9afe3d7e82 100644 --- a/native/modify/delete.zig +++ b/native/modify/delete.zig @@ -85,14 +85,6 @@ pub fn deleteField(ctx: *ModifyCtx) !usize { if (e != error.SELVA_ENOENT) return e; }; } else { - if (ctx.fieldType == t.PropType.reference) { - const fs = ctx.fieldSchema.?; - const dstType = try Node.getRefDstType(ctx.db, fs); - const oldRefDst = Node.getNodeFromReference(dstType, References.getReference(ctx.node.?, fs)); - if (oldRefDst) |dstNode| { - Selva.markDirty(ctx, Node.getNodeTypeId(dstNode), Node.getNodeId(dstNode)); - } - } try Fields.deleteField(ctx, ctx.node.?, ctx.fieldSchema.?); } return 0; diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 00f44cfcb5..ebfbc82b5e 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -47,6 +47,8 @@ fn modifyInternalThread(env: napi.Env, info: napi.Info) !void { } pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8, items: []u8) !void { + selva.markDirty(db, typeEntry, Node.getNodeId(node)); + var j: usize = 0; while (j < data.len) { const propId = data[j]; @@ -100,8 +102,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 // if (ctx.currentSortIndex != null) { // sort.remove(ctx.thread.decompressor, ctx.currentSortIndex.?, slice, Node.getNode(ctx.typeEntry.?, prev).?); // } - const typeId = Node.getNodeTypeId(node); - selva.markDirty(db, typeId, prev); } }, .cardinality => { @@ -315,7 +315,6 @@ pub fn modify( }; utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); - selva.markDirty(db, update.type, id); } else { utils.write(result, id, j); utils.write(result, t.ModifyError.nx, j + 4); @@ -341,7 +340,6 @@ pub fn modify( const id = Node.getNodeId(upsertRes.node); utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); - selva.markDirty(db, upsert.type, id); i += dataSize; }, .insert => { @@ -360,7 +358,6 @@ pub fn modify( modifyProps(db, typeEntry, upsertRes.node, data, items) catch { // handle errors }; - selva.markDirty(db, insert.type, id); } utils.write(result, id, j); utils.write(result, t.ModifyError.null, j + 4); @@ -378,7 +375,6 @@ pub fn modify( Node.deleteNode(db, typeEntry, node) catch { // handle errors }; - selva.markDirty(db, delete.type, id); } }, } diff --git a/native/selva/selva.zig b/native/selva/selva.zig index 6088d1a00c..fa011e26eb 100644 --- a/native/selva/selva.zig +++ b/native/selva/selva.zig @@ -30,6 +30,7 @@ pub const c = @cImport({ @cInclude("selva/membar.h"); @cInclude("selva/mblen.h"); }); +const t = @import("../types.zig"); const std = @import("std"); const Modify = @import("../modify/common.zig"); @@ -55,8 +56,15 @@ pub fn selvaStringDestroy(str: ?c.selva_string) void { } // TODO Accept also Type as an arg -pub inline fn markDirty(db: *DbCtx, typeId: u16, nodeId: u32) void { - c.selva_mark_dirty(c.selva_get_type_by_index(db.selva, typeId), nodeId); +pub inline fn markDirty(db: *DbCtx, nodeType: anytype, nodeId: u32) void { + if (comptime @TypeOf(nodeType) == t.TypeId) { + c.selva_mark_dirty(c.selva_get_type_by_index(db.selva, nodeType), nodeId); + } else if (comptime @TypeOf(nodeType) == Type) { + c.selva_mark_dirty(nodeType, nodeId); + } else { + @compileLog("Invalid type: ", @TypeOf(nodeType)); + @compileError("Invalid type"); + } } pub fn markReferencesDirty(ctx: *Modify.ModifyCtx, dstTypeId: u16, refs: []u32) void { From e6570ce125ef7a8a8f4d29a41a22dd7358f0a479 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 10:53:44 +0100 Subject: [PATCH 300/449] Add sort --- native/query/multiple.zig | 91 +++++++++++++++++++++++++------- src/db-client/query2/index.ts | 9 ++-- src/db-query/ast/ast.ts | 3 +- src/db-query/ast/iteratorType.ts | 79 ++++++++++++++------------- src/db-query/ast/multiple.ts | 26 ++++++--- src/db-query/ast/sort.ts | 21 ++++++++ test/query-ast/include.ts | 39 ++++---------- 7 files changed, 166 insertions(+), 102 deletions(-) create mode 100644 src/db-query/ast/sort.ts diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 17924a3a53..6652a38377 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -163,29 +163,56 @@ pub fn default( const sizeIndex = try ctx.thread.query.reserve(4); const typeEntry = try Node.getType(ctx.db, header.typeId); var nodeCnt: u32 = 0; + + std.debug.print("DERP? {any} \n", .{header}); + switch (header.iteratorType) { .default => { var it = Node.iterator(false, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); }, - .filter => { - var it = Node.iterator(false, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); - }, .desc => { var it = Node.iterator(true, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); }, + .sort => { const sortHeader = utils.readNext(t.SortHeader, q, &i); + + std.debug.print("DERP {any} \n", .{sortHeader}); + var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); }, .descSort => { const sortHeader = utils.readNext(t.SortHeader, q, &i); + + std.debug.print("DOINK {any} \n", .{sortHeader}); + var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); }, + + .filter => { + var it = Node.iterator(false, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + .descFilter => { + var it = Node.iterator(true, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + + .filterSort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + .descFilterSort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + else => { // not handled }, @@ -226,18 +253,6 @@ pub fn references( var nodeCnt: u32 = 0; switch (header.iteratorType) { - .edgeInclude => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); - }, - .edgeIncludeDesc => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); - }, - .edge => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - }, .default => { var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); @@ -246,20 +261,34 @@ pub fn references( var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); }, + .sort => { var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - .edgeDesc => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - }, .descSort => { var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + + .edge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); + }, + .edgeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); + }, + .edgeInclude => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + }, .edgeSort => { var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); @@ -280,6 +309,28 @@ pub fn references( nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + + .filter => { + var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .descFilter => { + var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .filterSort => { + var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .descFilterSort => { + var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + + // filter + else => { // not handled }, diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 510751724b..c5f7870357 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -320,7 +320,8 @@ class Query< Aggregate, GroupedKey > { - this.ast.sort = { prop, order: order || 'asc' } + this.ast.order = order || 'asc' + this.ast.sort = { prop } return this as any } @@ -337,11 +338,7 @@ class Query< Aggregate, GroupedKey > { - if (this.ast.sort) { - this.ast.sort.order = order - } else { - this.ast.sort = { prop: 'id', order } - } + this.ast.order = order || 'asc' return this as any } diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 7e1d528aae..1420532e98 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -64,7 +64,8 @@ export type QueryAst = { type?: string target?: number | number[] | Record filter?: FilterAst - sort?: { prop: string; order: 'asc' | 'desc' } + order?: 'asc' | 'desc' + sort?: { prop: string } props?: Record edges?: QueryAst // aggregate options diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index 99bf786964..4c99d50469 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -1,5 +1,4 @@ import { - ID_PROP, QUERY_ITERATOR_DEFAULT, QUERY_ITERATOR_EDGE, QUERY_ITERATOR_EDGE_INCLUDE, @@ -7,22 +6,22 @@ import { QUERY_ITERATOR_SEARCH_VEC, QueryIteratorTypeEnum, QueryHeader, + QueryIteratorType, + QueryIteratorTypeInverse, } from '../../zigTsExports.js' -import { QueryDef, QueryDefType } from '../../db-client/query/types.js' +import { QueryAst } from './ast.js' -export const getIteratorType = (header: QueryHeader): QueryIteratorTypeEnum => { +export const getIteratorType = ( + header: QueryHeader, + ast: QueryAst, +): QueryIteratorTypeEnum => { const hasFilter: boolean = header.filterSize != 0 const edge: boolean = header.edgeTypeId != 0 const edgeInclude: boolean = header.edgeSize != 0 - - // const hasFilter = def.filter.size > 0 - const hasSort = false // - const isDesc = false // def.order === Order.desc - const hasSearch = false //def.search?.size && def.search.size > 0 - const isVector = false // hasSearch && def.search!.isVector - // def.sort && - // (def.sort.prop !== ID_PROP || def.type === QueryDefType.References) - // def.type === QueryDefType.References && + const hasSort = header.sort + const isDesc = ast.order === 'desc' + const hasSearch = false + const isVector = false let base = QUERY_ITERATOR_DEFAULT @@ -34,43 +33,43 @@ export const getIteratorType = (header: QueryHeader): QueryIteratorTypeEnum => { base = QUERY_ITERATOR_EDGE_INCLUDE } - if (hasSearch && !isVector) { - base = QUERY_ITERATOR_SEARCH - } + // if (hasSearch && !isVector) { + // base = QUERY_ITERATOR_SEARCH + // } - if (hasSearch && isVector) { - base = QUERY_ITERATOR_SEARCH_VEC - } + // if (hasSearch && isVector) { + // base = QUERY_ITERATOR_SEARCH_VEC + // } - if (hasSearch) { + // if (hasSearch) { + // if (hasFilter) { + // base += 1 + // } + // } + + if (hasSort) { if (hasFilter) { + if (isDesc) { + base += 7 + } else { + base += 3 + } + } else if (isDesc) { + base += 5 + } else { base += 1 } } else { - if (hasSort) { - if (hasFilter) { - if (isDesc) { - base += 7 - } else { - base += 3 - } - } else if (isDesc) { - base += 5 + if (hasFilter) { + if (isDesc) { + base += 6 } else { - base += 1 + base += 2 } + } else if (isDesc) { + base += 4 } else { - if (hasFilter) { - if (isDesc) { - base += 6 - } else { - base += 2 - } - } else if (isDesc) { - base += 4 - } else { - base += 0 - } + base += 0 } } diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 959d808663..ae6b60818e 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -7,6 +7,7 @@ import { writeQueryHeaderProps as props, QueryIteratorType, readQueryHeader, + pushSortHeader, } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' import { filter } from './filter/filter.js' @@ -14,6 +15,7 @@ import { include } from './include.js' import { getIteratorType } from './iteratorType.js' import { readPropDef, readSchema } from './readSchema.js' import { isAggregateAst, pushAggregatesQuery } from './aggregates.js' +import { sort } from './sort.js' export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { const rangeStart = ast.range?.start || 0 @@ -23,8 +25,6 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { return } - // ADD SORT - const headerIndex = pushQueryHeader(ctx.query, { op: QueryType.default, prop: ID_PROP, @@ -32,9 +32,8 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { typeId: typeDef.id, offset: rangeStart, limit: (ast.range?.end || 1000) + rangeStart, - sort: false, + sort: !!ast.sort, filterSize: 0, - // Lets remove all this from the header and make specific ones searchSize: 0, iteratorType: QueryIteratorType.default, @@ -44,6 +43,10 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { size: 0, }) + if (ast.sort) { + pushSortHeader(ctx.query, sort(ast, ctx, typeDef)) + } + if (ast.filter) { const filterSize = filter(ast.filter, ctx, typeDef) props.filterSize(ctx.query.data, filterSize, headerIndex) @@ -53,7 +56,7 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { props.iteratorType( ctx.query.data, - getIteratorType(readQueryHeader(ctx.query.data, headerIndex)), + getIteratorType(readQueryHeader(ctx.query.data, headerIndex), ast), headerIndex, ) } @@ -67,7 +70,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { typeId: prop.ref!.id, offset: rangeStart, limit: (ast.range?.end || 100) + rangeStart, - sort: false, + sort: !!ast.sort, filterSize: 0, searchSize: 0, iteratorType: QueryIteratorType.default, @@ -83,6 +86,15 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { prop: readPropDef(prop, ctx.locales, ast.include), } + if (ast.sort) { + sort(ast, ctx, prop.ref!) + } + + if (ast.filter) { + const filterSize = filter(ast.filter, ctx, prop.ref!) + props.filterSize(ctx.query.data, filterSize, headerIndex) + } + const size = include( ast, { @@ -115,7 +127,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { props.iteratorType( ctx.query.data, - getIteratorType(readQueryHeader(ctx.query.data, headerIndex)), + getIteratorType(readQueryHeader(ctx.query.data, headerIndex), ast), headerIndex, ) } diff --git a/src/db-query/ast/sort.ts b/src/db-query/ast/sort.ts new file mode 100644 index 0000000000..03a97b6bff --- /dev/null +++ b/src/db-query/ast/sort.ts @@ -0,0 +1,21 @@ +import { TypeDef } from '../../schema/defs/index.js' +import { LangCode, Order } from '../../zigTsExports.js' +import { Ctx, QueryAst } from './ast.js' + +export const sort = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { + const prop = typeDef.props.get(ast.sort!.prop) + + if (!prop) { + throw new Error(`Cannot find prop in sort ${ast.sort!.prop}`) + } + + return { + prop: prop.id, + propType: prop?.type, + start: prop.start, + len: prop.size, + edgeType: 0, // do in a bit + order: ast.order === 'asc' ? Order.asc : Order.desc, + lang: LangCode.none, + } +} diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 598c4bab15..beb4e29ea6 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -87,16 +87,19 @@ await test('include', async (t) => { console.log(Date.now() - d, 'ms') + // filter: RE-ADD REFERENCE + // filter: REFERENCES + + // GET REFERENCEs + // SORT REFERENCES + // FILTER REFENRRENS + const ast: QueryAst = { type: 'user', + order: 'desc', + sort: { prop: 'y' }, + filter: { - // props: { - // name: { - // // ADD LOWER CASE - // // ops: [{ op: 'includes', val: 'flap', opts: { lowerCase: true } }], - // ops: [{ op: 'includes', val: 'x' }], - // }, - // }, props: { flap: { ops: [{ op: '=', val: 9999 }] }, }, @@ -127,17 +130,6 @@ await test('include', async (t) => { }, }, - // filter('flap', '=', 9999) - // .and(q => { - // q.filter('y', '=', 100) - // .or('y', '=', 3) - // .or('y', '=', 4) - // }) - // .or('y','=', 670) - // .or('y', '=', 15) - - // (y == 0 && (y == 10 || y == 3 || y == 4)) || y == 67 - props: { y: { include: {} }, name: { include: {} }, @@ -154,12 +146,6 @@ await test('include', async (t) => { }, } - // (1: y == 0 && ( 2: y == 10 || 4: y == 3)) || 3: y == 67 - - // so the thing is we need to keep track of the NEXT or vs query.len - - // ->:3 :1 ->:4 :2 ->:3 :4 - const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) debugBuffer(ctx.query) @@ -189,11 +175,8 @@ await test('include', async (t) => { repeat: 10, }, ) - // quite large - - // deflate it? - const readSchemaBuf = serializeReaderSchema(ctx.readSchema) + // const readSchemaBuf = serializeReaderSchema(ctx.readSchema) const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) From f21238f87db6776499e1f69cdc88d27d10964f4f Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 11:57:32 +0100 Subject: [PATCH 301/449] add sort --- native/query/multiple.zig | 8 -------- native/sort/iterator.zig | 5 ++++- src/db-query/ast/multiple.ts | 2 +- test/query-ast/include.ts | 20 +++++++++++++++----- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 232dae9573..1cac9577cb 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -164,8 +164,6 @@ pub fn default( const typeEntry = try Node.getType(ctx.db, header.typeId); var nodeCnt: u32 = 0; - std.debug.print("DERP? {any} \n", .{header}); - switch (header.iteratorType) { .default => { var it = Node.iterator(false, typeEntry); @@ -178,17 +176,11 @@ pub fn default( .sort => { const sortHeader = utils.readNext(t.SortHeader, q, &i); - - std.debug.print("DERP {any} \n", .{sortHeader}); - var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); }, .descSort => { const sortHeader = utils.readNext(t.SortHeader, q, &i); - - std.debug.print("DOINK {any} \n", .{sortHeader}); - var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); }, diff --git a/native/sort/iterator.zig b/native/sort/iterator.zig index dfbd58bf58..6e58057dd5 100644 --- a/native/sort/iterator.zig +++ b/native/sort/iterator.zig @@ -65,6 +65,8 @@ pub fn SortIterator( it: selva.SelvaSortIterator, pub fn deinit(self: *SortIterator(desc, false)) void { + std.debug.print("HELLO {any} \n", .{self.index}); + selva.selva_sort_clear(self.index.?.index); } @@ -93,6 +95,7 @@ inline fn createIterator( } else { selva.selva_sort_foreach_begin(sortIndex.index, &it); } + std.debug.print("FLAP {any} \n", .{sortIndex}); return SortIterator(desc, isEdge){ .it = it, .index = sortIndex, @@ -208,7 +211,7 @@ fn getSortIndex( } } -pub fn fromIterator( +pub inline fn fromIterator( comptime desc: bool, comptime isEdge: bool, dbCtx: *DbCtx, diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index ae6b60818e..c4422abc3c 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -87,7 +87,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { } if (ast.sort) { - sort(ast, ctx, prop.ref!) + pushSortHeader(ctx.query, sort(ast, ctx, prop.ref!)) } if (ast.filter) { diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index beb4e29ea6..1f85d4a9c9 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -67,6 +67,7 @@ await test('include', async (t) => { cook: { cookie: 1234, }, + mrFriend: { id: a, $level: 67 }, }) let d = Date.now() @@ -80,6 +81,7 @@ await test('include', async (t) => { cook: { cookie: 1234, }, + friends: [a, b], }) } @@ -133,15 +135,23 @@ await test('include', async (t) => { props: { y: { include: {} }, name: { include: {} }, + friends: { + order: 'desc', + sort: { prop: 'y' }, + props: { + name: { include: {} }, + y: { include: {} }, + }, + }, mrFriend: { props: { y: { include: {} }, }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, + edges: { + props: { + $level: { include: {} }, + }, + }, }, }, } From 4f3a42a6fcbe69818f190be15492da9853438bc2 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 12:01:28 +0100 Subject: [PATCH 302/449] remove logs --- native/sort/iterator.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/native/sort/iterator.zig b/native/sort/iterator.zig index 6e58057dd5..0cfedb67b8 100644 --- a/native/sort/iterator.zig +++ b/native/sort/iterator.zig @@ -65,8 +65,6 @@ pub fn SortIterator( it: selva.SelvaSortIterator, pub fn deinit(self: *SortIterator(desc, false)) void { - std.debug.print("HELLO {any} \n", .{self.index}); - selva.selva_sort_clear(self.index.?.index); } @@ -95,7 +93,6 @@ inline fn createIterator( } else { selva.selva_sort_foreach_begin(sortIndex.index, &it); } - std.debug.print("FLAP {any} \n", .{sortIndex}); return SortIterator(desc, isEdge){ .it = it, .index = sortIndex, From 0d1ae073bfc61409ddfee952c0cf32068c1711bd Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 12:30:31 +0100 Subject: [PATCH 303/449] nested filter in refs --- native/query/filter/filter.zig | 2 +- native/query/multiple.zig | 17 +++++---- test/query-ast/include.ts | 63 +++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index b4612df207..61857e8d57 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -57,7 +57,7 @@ pub fn prepare( }, } } else { - c = utils.readPtr(t.FilterCondition, q, q[i] + i + 1); + c = utils.readPtr(t.FilterCondition, q, q[i] + i); // + 1 const totalSize = headerSize + c.size; const end = totalSize + i; i = end; diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 1cac9577cb..551b8bd079 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -33,7 +33,6 @@ fn iterator( filter = utils.sliceNext(header.filterSize, q, i); try Filter.prepare(filter, ctx, typeEntry); } - // utils.debugPrint("i.* .. i.* + header.includeSize: {d} .. {d} = {d}\n", .{ i.*, i.* + header.includeSize, header.includeSize }); const nestedQuery = q[i.* .. i.* + header.includeSize]; while (offset > 0) { const node = it.next() orelse return 0; @@ -273,14 +272,6 @@ pub fn references( var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); }, - .edgeInclude => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); - }, - .edgeIncludeDesc => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); - }, .edgeSort => { var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); @@ -291,6 +282,14 @@ pub fn references( nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + .edgeInclude => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + }, .edgeIncludeSort => { var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 1f85d4a9c9..a82dac65c1 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -50,7 +50,7 @@ await test('include', async (t) => { }) const a = client.create('user', { - name: 'mr snurf a', + name: 'mr jim', y: 4, x: true, flap: 9999, @@ -105,43 +105,50 @@ await test('include', async (t) => { props: { flap: { ops: [{ op: '=', val: 9999 }] }, }, - and: { - props: { - y: { ops: [{ op: '=', val: 100 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 3 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 4 }] }, - }, - }, - }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 670 }] }, - }, - or: { - props: { - y: { ops: [{ op: '=', val: 15 }] }, - }, - }, - }, + // and: { + // props: { + // y: { ops: [{ op: '=', val: 100 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 3 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 4 }] }, + // }, + // }, + // }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 670 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 15 }] }, + // }, + // }, + // }, }, props: { y: { include: {} }, name: { include: {} }, friends: { - order: 'desc', - sort: { prop: 'y' }, + // order: 'desc', + // sort: { prop: 'y' }, // can just be the prop? props: { name: { include: {} }, y: { include: {} }, }, + filter: { + props: { + y: { + ops: [{ op: '>', val: 10 }], + }, + }, + }, }, mrFriend: { props: { From 8dc6a8485bd036ac53375a8ffd7b17113b4a8c61 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 18 Feb 2026 12:37:28 +0100 Subject: [PATCH 304/449] typed DbClient --- src/db-client/index.ts | 20 ++++++++++++++----- test/clientServer/index.ts | 40 ++++++++++++++++++++++++++++++++++++++ test/shared/index.ts | 7 +------ 3 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 test/clientServer/index.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index ac1def1d81..bc55a6c1a8 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -41,7 +41,9 @@ export type ModifyOpts = { locale?: keyof typeof LangCode } -export class DbClient extends DbShared { +export class DbClientClass< + S extends { types: any } = SchemaOut, +> extends DbShared { constructor({ hooks, maxModifySize = 100 * 1e3 * 1e3, @@ -79,7 +81,7 @@ export class DbClient extends DbShared { async setSchema( schema: StrictSchema, transformFns?: SchemaMigrateFns, - ): Promise>> { + ): Promise>> { const strictSchema = parse(schema as any).schema await this.drain() const schemaChecksum = await this.hooks.setSchema( @@ -87,13 +89,13 @@ export class DbClient extends DbShared { transformFns, ) if (this.stopped) { - return this as DbClient> + return this as unknown as DbClientClass> } if (schemaChecksum !== this.schema?.hash) { await this.once('schema') - return this as DbClient> + return this as unknown as DbClientClass> } - return this as DbClient> + return this as unknown as DbClientClass> } query2( @@ -249,4 +251,12 @@ export class DbClient extends DbShared { } } +export type DbClient = DbClientClass + +export const DbClient = DbClientClass as { + new ( + opts: DbClientOpts, + ): DbClient> +} + function noop() {} diff --git a/test/clientServer/index.ts b/test/clientServer/index.ts new file mode 100644 index 0000000000..06f8c3fe9f --- /dev/null +++ b/test/clientServer/index.ts @@ -0,0 +1,40 @@ +import { + BasedDb, + DbClient, + DbServer, + getDefaultHooks, +} from '../../src/index.js' +import { testDb } from '../shared/index.js' +import test from '../shared/test.js' + +await test('delete', async (t) => { + const server = new DbServer({ + path: t.tmp, + }) + + const schema = { + types: { + user: { + name: 'string', + }, + }, + } as const + + const client = new DbClient({ + hooks: getDefaultHooks(server), + }) + + const client2 = await testDb(t, schema) + + client.create('user', { + name2: 1, + }) + + client.query('smurl') + + client2.create('user', { + name2: 1, + }) + + client2.query('smurl') +}) diff --git a/test/shared/index.ts b/test/shared/index.ts index f65e1a8a80..21f24ae143 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,11 +1,6 @@ import { createHash } from 'node:crypto' import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' -import type { - ResolveSchema, - SchemaIn, - ValidateSchema, - StrictSchema, -} from '../../src/schema.js' +import type { ResolveSchema, SchemaIn, StrictSchema } from '../../src/schema.js' import { BasedDb, DbServer, type DbClient } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' From 42d899c856c92268e744e9a1c64dd25ec3ea9fec Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 18 Feb 2026 13:26:33 +0100 Subject: [PATCH 305/449] update tests --- test/aggregate/basic.ts | 396 ++++++++----------------------------- test/clientServer/index.ts | 14 +- test/query/ast.ts | 24 +-- test/query/db.ts | 22 ++- 4 files changed, 116 insertions(+), 340 deletions(-) diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 27b38e4459..44ca5a7123 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -3,17 +3,10 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { throws, deepEqual } from '../shared/assert.js' import { fastPrng } from '../../src/utils/index.js' +import { testDb } from '../shared/index.js' await test('sum top level', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -43,6 +36,7 @@ await test('sum top level', async (t) => { }, }, }) + const nl1 = db.create('vote', { country: 'bb', flap: { hello: 100 }, @@ -57,64 +51,33 @@ await test('sum top level', async (t) => { AU: 15, }) - const s = db.create('sequence', { votes: [nl1, nl2, au1] }) + db.create('sequence', { votes: [nl1, nl2, au1] }) - // top level ---------------------------------- deepEqual( - await db.query('vote').sum('NL').get().toObject(), + await db.query2('vote').sum('NL').get(), { NL: { sum: 30 } }, 'sum, top level, single prop', ) - // deepEqual( - // await db - // .query('vote') - // .filter('country', '=', 'aa') // string filter not implemented yet - // .sum('NL') - // .get() - // .toObject(), - // { NL: { sum: 20 } }, - // 'sum with filter', - // ) - deepEqual( - await db.query('vote').sum('NL', 'AU').get().toObject(), + await db.query2('vote').sum('NL', 'AU').get(), { NL: { sum: 30 }, AU: { sum: 15 } }, 'sum, top level, multiple props', ) throws(async () => { - await db.query('vote').sum().get().toObject() + await db.query2('vote').sum().get() }, 'sum() returning nothing') - // deepEqual( - // await db - // .query('vote') - // .filter('country', '=', 'zz') // string filter not implemented yet - // .sum('NL') - // .get() - // .toObject(), - // { NL: { sum: 0 } }, - // 'sum with empty result set', - // ) - deepEqual( - await db.query('vote').sum('flap.hello').get().toObject(), + await db.query2('vote').sum('flap.hello').get(), { flap: { hello: { sum: 100 } } }, 'nested object notation', ) }) - +/* await test('top level count', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -158,69 +121,61 @@ await test('top level count', async (t) => { const s = db.create('sequence', { votes: [nl1, nl2, au1] }) db.drain() - db.create('sequence', { votes: nl1 }) - db.create('sequence', { votes: nl2 }) - db.create('sequence', { votes: au1 }) + db.create('sequence', { votes: [nl1] }) + db.create('sequence', { votes: [nl2] }) + db.create('sequence', { votes: [au1] }) // // top level ---------------------------------- deepEqual( - await db.query('vote').count().get().toObject(), + await db.query2('vote').count().get(), { count: 3 }, 'count, top level, prop', ) // deepEqual( // await db - // .query('vote') + // .query2('vote') // .filter('country', '=', 'aa') // string filter not implemented yet // .count() // .get() - // .toObject(), + // , // { count: 2 }, // 'count, top level, with filter', // ) deepEqual( - await db.query('vote').include('IT').count().get().toObject(), + await db.query2('vote').include('IT').count().get(), { count: 3 }, 'count, top level, ignoring include', ) // deepEqual( // await db - // .query('vote') + // .query2('vote') // .filter('country', '=', 'zz') // string filter not implemented yet // .count() // .get() - // .toObject(), + // , // { count: 0 }, // 'count, with no match filtering, string value', // ) deepEqual( - await db.query('vote').filter('NL', '=', 20).count().get().toObject(), + await db.query2('vote').filter('NL', '=', 20).count().get(), { count: 1 }, 'count, with filtering an int value', ) deepEqual( - await db.query('vote').filter('NL', '>', 1e6).count().get().toObject(), + await db.query2('vote').filter('NL', '>', 1e6).count().get(), { count: 0 }, 'count, with no match filtering, int value', ) }) await test('two phase accumulation', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -274,7 +229,7 @@ await test('two phase accumulation', async (t) => { const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) deepEqual( - await db.query('vote').stddev('NL', { mode: 'sample' }).get().toObject(), + await db.query2('vote').stddev('NL', { mode: 'sample' }).get(), { NL: { stddev: 15.56598856481656 }, }, @@ -282,7 +237,7 @@ await test('two phase accumulation', async (t) => { ) deepEqual( - await db.query('vote').stddev('NL', { mode: 'sample' }).get().toObject(), + await db.query2('vote').stddev('NL', { mode: 'sample' }).get(), { NL: { stddev: 15.56598856481656 }, }, @@ -290,11 +245,7 @@ await test('two phase accumulation', async (t) => { ) deepEqual( - await db - .query('vote') - .stddev('NL', { mode: 'population' }) - .get() - .toObject(), + await db.query2('vote').stddev('NL', { mode: 'population' }).get(), { NL: { stddev: 13.922643427165687 }, }, @@ -302,7 +253,7 @@ await test('two phase accumulation', async (t) => { ) deepEqual( - await db.query('vote').sum('NL').get().toObject(), + await db.query2('vote').sum('NL').get(), { NL: { sum: 118 }, }, @@ -311,11 +262,10 @@ await test('two phase accumulation', async (t) => { deepEqual( await db - .query('vote') + .query2('vote') .stddev('NL', { mode: 'population' }) .groupBy('country') - .get() - .toObject(), + .get(), { Brazil: { NL: { stddev: 0 }, @@ -332,10 +282,9 @@ await test('two phase accumulation', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').stddev('NL', { mode: 'population' })) - .get() - .toObject(), + .get(), [ { id: 1, @@ -349,12 +298,11 @@ await test('two phase accumulation', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), ) - .get() - .toObject(), + .get(), [ { id: 1, @@ -376,15 +324,7 @@ await test('two phase accumulation', async (t) => { }) await test('numeric types', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -454,7 +394,7 @@ await test('numeric types', async (t) => { const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) deepEqual( - await db.query('vote').groupBy('region').get().toObject(), + await db.query2('vote').groupBy('region').get(), { bb: {}, aa: {}, @@ -464,7 +404,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db.query('vote').sum('NL', 'FI').groupBy('region').get().toObject(), + await db.query2('vote').sum('NL', 'FI').groupBy('region').get(), { bb: { NL: { sum: 33 }, @@ -483,7 +423,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db.query('vote').count().groupBy('region').get().toObject(), + await db.query2('vote').count().groupBy('region').get(), { bb: { count: 2, @@ -499,12 +439,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db - .query('vote') - .avg('NL', 'PT', 'FI') - .groupBy('region') - .get() - .toObject(), + await db.query2('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), { bb: { NL: { avg: 16.5 }, @@ -526,12 +461,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db - .query('vote') - .hmean('NL', 'PT', 'FI') - .groupBy('region') - .get() - .toObject(), + await db.query2('vote').hmean('NL', 'PT', 'FI').groupBy('region').get(), { bb: { NL: { hmean: 13.93939393939394 }, @@ -554,11 +484,10 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('vote') + .query2('vote') .stddev('NL', 'PL', { mode: 'population' }) .groupBy('region') - .get() - .toObject(), + .get(), { bb: { NL: { stddev: 6.5 }, @@ -577,12 +506,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db - .query('vote') - .stddev('NL', 'PL') - .groupBy('region') - .get() - .toObject(), + await db.query2('vote').stddev('NL', 'PL').groupBy('region').get(), { bb: { NL: { stddev: 9.192388155425117 }, @@ -602,11 +526,10 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('vote') + .query2('vote') .var('NL', 'PL', { mode: 'population' }) .groupBy('region') - .get() - .toObject(), + .get(), { bb: { NL: { variance: 42.25 }, @@ -625,11 +548,10 @@ await test('numeric types', async (t) => { ) deepEqual( await db - .query('vote') + .query2('vote') .var('NL', 'PL', { mode: 'sample' }) .groupBy('region') - .get() - .toObject(), + .get(), { bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, @@ -639,7 +561,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db.query('vote').var('NL', 'PL').groupBy('region').get().toObject(), + await db.query2('vote').var('NL', 'PL').groupBy('region').get(), { bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, @@ -649,12 +571,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db - .query('vote') - .max('NL', 'NO', 'PT', 'FI') - .groupBy('region') - .get() - .toObject(), + await db.query2('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), { bb: { NL: { max: 23 }, @@ -679,12 +596,7 @@ await test('numeric types', async (t) => { ) deepEqual( - await db - .query('vote') - .min('NL', 'NO', 'PT', 'FI') - .groupBy('region') - .get() - .toObject(), + await db.query2('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), { bb: { NL: { min: 10 }, @@ -710,10 +622,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').sum('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -726,10 +637,9 @@ await test('numeric types', async (t) => { ) deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').avg('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -743,10 +653,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').hmean('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -760,10 +669,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').sum('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -785,10 +693,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').count()) - .get() - .toObject(), + .get(), [ { id: 1, @@ -804,12 +711,11 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').stddev('NL', { mode: 'population' }), ) - .get() - .toObject(), + .get(), [ { id: 1, @@ -831,10 +737,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').stddev('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -850,12 +755,11 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').var('NL', { mode: 'population' }), ) - .get() - .toObject(), + .get(), [ { id: 1, @@ -877,12 +781,11 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').var('NL', { mode: 'sample' }), ) - .get() - .toObject(), + .get(), [ { id: 1, @@ -898,10 +801,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').var('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -917,10 +819,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').avg('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -942,10 +843,9 @@ await test('numeric types', async (t) => { deepEqual( await db - .query('sequence') + .query2('sequence') .include((q) => q('votes').groupBy('region').hmean('NL')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -967,13 +867,7 @@ await test('numeric types', async (t) => { }) await test('fixed length strings', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { product: { name: { type: 'string', maxBytes: 10 }, @@ -1008,12 +902,11 @@ await test('fixed length strings', async (t) => { Number( Object.keys( await db - .query('product') + .query2('product') .include('*') .avg('flap') .groupBy('name') - .get() - .toObject(), + .get(), )[0].substring(4, 6), ) < 100, true, @@ -1024,10 +917,9 @@ await test('fixed length strings', async (t) => { Number( Object.keys( await db - .query('shelve') + .query2('shelve') .include((q) => q('products').avg('flap').groupBy('name')) - .get() - .toObject(), + .get(), )[0].substring(4, 6), ) < 100, true, @@ -1036,15 +928,7 @@ await test('fixed length strings', async (t) => { }) await test('range', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - const ter = ['lala', 'lele', 'lili'] - - await db.setSchema({ + const db = await testDb(t, { types: { job: { day: 'timestamp', @@ -1098,12 +982,11 @@ await test('range', async (t) => { deepEqual( Object.keys( await db - .query('job') + .query2('job') .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) .avg('tip') // .range(0, 2) - .get() - .toObject(), + .get(), ).length, 2, 'range group by main', @@ -1112,128 +995,13 @@ await test('range', async (t) => { deepEqual( Object.keys( await db - .query('employee') + .query2('employee') .include((q) => q('area').groupBy('name').sum('flap'), '*') .range(0, 2) - .get() - .toObject(), + .get(), ).length, 2, 'range group by references', ) }) - -// await test('count props', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// maxModifySize: 1e6, -// }) - -// await db.start({ clean: true }) -// t.after(() => db.stop()) - -// await db.setSchema({ -// types: { -// vehicle: { -// props: { -// plate: 'string', -// year: 'unint16', -// }, -// }, -// trip: { -// props: { -// distance: 'number', -// vehicle: { -// ref: 'vehicle', -// prop: 'vehicle', -// }, -// }, -// }, -// }, -// }) - -// const v1 = db.create('vehicle', { -// plate: 'KKK1234', -// year: 2023, -// }) -// db.create('trip', { -// distance: 813.1, -// vehicle: v1, -// }) -// db.create('trip', { -// distance: 1023.1, -// }) -// const v1 = db.create('vehicle', { -// plate: 'LAL0001', -// }) -// await db.query('trip').count().get().inspect() // should count nodes --> count = 2 -// await db.query('trip').count('vehicle').get().inspect() // shoudl count refs --> count = 1 -// await db.query('vehicle').count('year').get().inspect() // should ignore undefined props --> count = 1 -// }) - -// await test('groupBy multiple props', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// maxModifySize: 1e6, -// }) - -// await db.start({ clean: true }) -// t.after(() => db.stop()) - -// await db.setSchema({ -// types: { -// vehicle: { -// props: { -// plate: 'string', -// decade: 'uint16', -// brand: 'string', -// }, -// }, -// }, -// }) - -// const v1 = db.create('vehicle', { -// plate: 'KKK1234', -// year: 2020, -// brand: 'Volkswagen', -// }) -// const v2 = db.create('vehicle', { -// plate: 'LAL0001', -// year: 1990, -// brand: 'Volkswagen', -// }) -// const v3 = db.create('vehicle', { -// plate: 'BYD8001', -// year: 2020, -// brand: 'BYD', -// }) - -// await db.query('vehicle').count().groupBy('brand').get().inspect() -/* - { - BYD: { - count: 1 count - }, - Volkswagen: { - count: 2 count - } - } - */ - -// await db.query('vehicle').count().groupBy('decade','brand').get().inspect() -/* -{ - [BYD, 2020]: { - count: 1 count - }, - [BYD, 1990]: { - count: 0 count - }, - [Volkswagen, 1990]: { - count: 2, - }, - [Volkswagen, 2020]: { - count: 0, - } */ -// }) diff --git a/test/clientServer/index.ts b/test/clientServer/index.ts index 06f8c3fe9f..27903fa927 100644 --- a/test/clientServer/index.ts +++ b/test/clientServer/index.ts @@ -7,7 +7,7 @@ import { import { testDb } from '../shared/index.js' import test from '../shared/test.js' -await test('delete', async (t) => { +await test('clientServer', async (t) => { const server = new DbServer({ path: t.tmp, }) @@ -25,16 +25,4 @@ await test('delete', async (t) => { }) const client2 = await testDb(t, schema) - - client.create('user', { - name2: 1, - }) - - client.query('smurl') - - client2.create('user', { - name2: 1, - }) - - client2.query('smurl') }) diff --git a/test/query/ast.ts b/test/query/ast.ts index bec8211f54..6db5673fdc 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -62,14 +62,10 @@ await test('query ast creation', async (t) => { { const q = query('user') - .include('age') - .filter('name', 'includes', 'jim') - .and((f) => f('age', '>', 2)) - - // .filter('isNice', '=', false) - // .and('name', '=', 'youzi') - // .or('name', '=', 'james') - // .and('isNice', '=', false) + .filter('isNice', '=', false) + .and('name', '=', 'youzi') + .or('name', '=', 'james') + .and('isNice', '=', false) deepEqual(q.ast, { type: 'user', @@ -212,25 +208,29 @@ await test('query ast creation', async (t) => { const q1 = query('user').sort('age') deepEqual(q1.ast, { type: 'user', - sort: { prop: 'age', order: 'asc' }, + order: 'asc', + sort: { prop: 'age' }, }) const q2 = query('user').sort('age', 'desc') deepEqual(q2.ast, { type: 'user', - sort: { prop: 'age', order: 'desc' }, + order: 'desc', + sort: { prop: 'age' }, }) const q3 = query('user').order('desc') deepEqual(q3.ast, { type: 'user', - sort: { prop: 'id', order: 'desc' }, + order: 'desc', + sort: { prop: 'id' }, }) const q4 = query('user').sort('age').order('desc') deepEqual(q4.ast, { type: 'user', - sort: { prop: 'age', order: 'desc' }, + order: 'desc', + sort: { prop: 'age' }, }) } }) diff --git a/test/query/db.ts b/test/query/db.ts index 8939f55623..f50df8e498 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -12,6 +12,11 @@ await test('query db', async (t) => { name: 'string', isNice: 'boolean', age: 'number', + address: { + props: { + street: 'string', + }, + }, friend: { ref: 'user', prop: 'friend', @@ -23,15 +28,30 @@ await test('query db', async (t) => { db.create('user', { name: 'john', isNice: false, - age: 1, + age: 21, + address: { + street: 'Cool street', + }, }) db.create('user', { name: 'billy', isNice: true, age: 49, + address: { + street: 'Mega street', + }, }) + { + const res = await db.query2('user').include('address.street').get() + + deepEqual(res, [ + { id: 1, address: { street: 'Cool street' } }, + { id: 2, address: { street: 'Mega street' } }, + ]) + } + { const res = await db .query2('user') From f975b98a88cbfb27ef4c7607c7fda2a5edfd4e7e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 14:24:03 +0100 Subject: [PATCH 306/449] Cleanup save test --- test/save/save.ts | 160 ++++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 71 deletions(-) diff --git a/test/save/save.ts b/test/save/save.ts index 5718c43c46..9816026781 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -1,4 +1,4 @@ -import { BasedDb } from '../../src/index.js' +import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' import { deepEqual, equal } from '../shared/assert.js' import test from '../shared/test.js' import { setTimeout } from 'node:timers/promises' @@ -10,7 +10,7 @@ await test('simple', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - await db.setSchema({ + const schema = { locales: { en: {}, fr: {}, @@ -63,21 +63,22 @@ await test('simple', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) - db.create('user', { + client.create('user', { name: 'youzi', email: 'youzi@yazi.yo', alias: 'best', }) - db.create('user', { + client.create('user', { name: 'youri', email: 'youri@yari.yo', alias: 'alsobest', }) - db.create('typeTest', {}) + client.create('typeTest', {}) - await db.drain() + await client.drain() await db.save() const db2 = new BasedDb({ @@ -85,24 +86,25 @@ await test('simple', async (t) => { }) await db2.start() t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - const a = await db.query('user').get().toObject() - const b = await db2.query('user').get().toObject() + const a = await client.query('user').get().toObject() + const b = await client2.query('user').get().toObject() deepEqual(b, a) - const c = await db.create('user', { name: 'jerp' }) - const d = await db2.create('user', { name: 'jerp' }) + const c = await client.create('user', { name: 'jerp' }) + const d = await client2.create('user', { name: 'jerp' }) equal(c, 3) equal(d, 3) await db2.save() - const user1 = await db2.create('user', { name: 'jerp' }) - + await client2.create('user', { name: 'jerp' }) await db2.save() - const user2 = await db2.create('user', { name: 'jerp' }) - + await client2.create('user', { name: 'jerp' }) await db2.save() }) @@ -416,7 +418,7 @@ await test('upsert', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - await db.setSchema({ + const schema = { types: { person: { props: { @@ -426,15 +428,17 @@ await test('upsert', async (t) => { }, }, }, - }) - const joe = db.create('person', { + } as const + const client = await db.setSchema(schema) + + client.create('person', { name: 'Joe', alias: 'boss', }) - await db.drain() + await client.drain() await db.save() - await db.upsert('person', { alias: 'boss' }, { age: 42 }) - await db.drain() + await client.upsert('person', { alias: 'boss' }, { age: 42 }) + await client.drain() await db.save() // load the same db into a new instance @@ -443,11 +447,14 @@ await test('upsert', async (t) => { }) await db2.start() t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - deepEqual(await db.query('person').get(), [ + deepEqual(await client.query('person').get(), [ { id: 1, name: 'Joe', age: 42, alias: 'boss' }, ]) - deepEqual(await db2.query('person').get(), [ + deepEqual(await client2.query('person').get(), [ { id: 1, name: 'Joe', age: 42, alias: 'boss' }, ]) }) @@ -459,7 +466,7 @@ await test('alias blocks', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - await db.setSchema({ + const schema = { types: { person: { props: { @@ -468,26 +475,27 @@ await test('alias blocks', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) for (let i = 0; i < 100_000; i++) { - db.create('person', { + client.create('person', { name: 'Joe', }) } - await db.drain() + await client.drain() await db.save() - const john = await db.create('person', { + const john = await client.create('person', { name: 'John', alias: 'bf', }) - await db.drain() + await client.drain() await db.save() - db.update('person', 1, { alias: 'bf' }) + client.update('person', 1, { alias: 'bf' }) for (let id = 2; id < john; id++) { - db.delete('person', id) + client.delete('person', id) } - await db.drain() + await client.drain() await db.save() // load the same db into a new instance @@ -496,8 +504,14 @@ await test('alias blocks', async (t) => { }) await db2.start() t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - deepEqual(await db2.query('person').get(), await db.query('person').get()) + deepEqual( + await client2.query('person').get(), + await client.query('person').get(), + ) }) await test('simulated periodic save', async (t) => { @@ -507,7 +521,7 @@ await test('simulated periodic save', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - await db.setSchema({ + const schema = { types: { book: { props: { @@ -526,77 +540,78 @@ await test('simulated periodic save', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) // create some people const people = await Promise.all([ - db.create('person', { + client.create('person', { name: 'Slim', alias: 'slim', }), - db.create('person', { + client.create('person', { name: 'Slick', alias: 'slick', }), - db.create('person', { + client.create('person', { name: 'Joe', alias: 'joe', }), - db.create('person', { + client.create('person', { name: 'Ben', alias: 'boss', }), - db.create('person', { + client.create('person', { name: 'Steve', }), ]) - db.update('person', people[1], { + client.update('person', people[1], { bf: people[2], }) // create some books for (let i = 0; i < 1000; i++) { - db.create('book', { + client.create('book', { name: `book ${i}`, isbn: '9789295055025', owner: people[i % people.length], }) } - await db.drain() + await client.drain() await db.save() // more books for (let i = 0; i < 1000; i++) { - db.create('book', { + client.create('book', { name: `book ${1000 + i}`, isbn: '9789295055025', owner: people[i % people.length], }) } - await db.drain() + await client.drain() await db.save() // change a node using an alias - db.upsert('person', { alias: 'slim' }, { name: 'Shady' }) - await db.drain() + client.upsert('person', { alias: 'slim' }, { name: 'Shady' }) + await client.drain() await db.save() // replace alias - db.create('person', { + client.create('person', { name: 'Slide', alias: 'slick', }) - await db.drain() + await client.drain() await db.save() // move alias - await db.update('person', people[4], { + await client.update('person', people[4], { alias: 'boss', }) - await db.drain() + await client.drain() await db.save() // load the same db into a new instance @@ -605,10 +620,13 @@ await test('simulated periodic save', async (t) => { }) await db2.start() t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db.server), + }) // Change node using alias saved deepEqual( - await db + await client .query('person') .filter('alias', 'includes', 'slim') .include('alias', 'name') @@ -616,7 +634,7 @@ await test('simulated periodic save', async (t) => { [{ id: 1, alias: 'slim', name: 'Shady' }], ) deepEqual( - await db2 + await client2 .query('person') .filter('alias', 'includes', 'slim') .include('alias', 'name') @@ -626,7 +644,7 @@ await test('simulated periodic save', async (t) => { // Replace alias saved deepEqual( - await db + await client .query('person') .filter('alias', 'includes', 'slick') .include('alias', 'name') @@ -634,7 +652,7 @@ await test('simulated periodic save', async (t) => { [{ id: 6, alias: 'slick', name: 'Slide' }], ) deepEqual( - await db2 + await client2 .query('person') .filter('alias', 'includes', 'slick') .include('alias', 'name') @@ -644,7 +662,7 @@ await test('simulated periodic save', async (t) => { // Move alias saved deepEqual( - await db + await client .query('person') .filter('alias', 'includes', 'boss') .include('alias', 'name') @@ -652,7 +670,7 @@ await test('simulated periodic save', async (t) => { [{ id: 5, name: 'Steve', alias: 'boss' }], ) deepEqual( - await db2 + await client2 .query('person') .filter('alias', 'includes', 'boss') .include('alias', 'name') @@ -662,8 +680,8 @@ await test('simulated periodic save', async (t) => { // All have the same books deepEqual( - await db2.query('person').include('name', 'alias', 'books').get(), - await db.query('person').include('name', 'alias', 'books').get(), + await client2.query('person').include('name', 'alias', 'books').get(), + await client.query('person').include('name', 'alias', 'books').get(), ) }) @@ -674,7 +692,7 @@ await test('edge val', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { round: { name: 'alias', @@ -704,11 +722,11 @@ await test('edge val', async (t) => { }, }) - const sequence1 = await db.create('sequence', {}) - const sequence2 = await db.create('sequence', {}) - const scenario1 = await db.create('scenario', {}) - const scenario2 = await db.create('scenario', {}) - const phase = await db.create('phase', { + const sequence1 = await client.create('sequence', {}) + const sequence2 = await client.create('sequence', {}) + const scenario1 = await client.create('scenario', {}) + const scenario2 = await client.create('scenario', {}) + const phase = await client.create('phase', { scenarios: [ { id: scenario1, @@ -717,7 +735,7 @@ await test('edge val', async (t) => { ], }) await db.save() - db.update('phase', phase, { + client.update('phase', phase, { scenarios: { add: [ { @@ -727,15 +745,15 @@ await test('edge val', async (t) => { ], }, }) - //await db.query('phase').include('scenarios.$sequence').get().inspect() + //await client.query('phase').include('scenarios.$sequence').get().inspect() await db.save() - await db.update('phase', phase, { + await client.update('phase', phase, { scenarios: { delete: [scenario1], }, }) - //await db.query('phase').include('scenarios.$sequence').get().inspect() + //await client.query('phase').include('scenarios.$sequence').get().inspect() }) await test('no mismatch', async (t) => { @@ -745,7 +763,7 @@ await test('no mismatch', async (t) => { await db.start({ clean: true }) t.after(() => db.stop(true)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -755,7 +773,7 @@ await test('no mismatch', async (t) => { }, }) - await db.create('user', { + await client.create('user', { name: 'xxx', }) From d1f62f8aa38893f5528c9e5058703dc12d11b974 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 14:54:14 +0100 Subject: [PATCH 307/449] fix DESC references iterator --- native/query/multiple.zig | 3 +- native/selva/references.zig | 4 +- src/db-query/ast/iteratorType.ts | 18 +++--- src/db-query/ast/multiple.ts | 5 +- src/db-query/ast/sort.ts | 11 +++- test/query-ast/include.ts | 105 +++++++++++++++++-------------- 6 files changed, 80 insertions(+), 66 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 551b8bd079..c919031618 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -93,7 +93,6 @@ fn iteratorEdge( @truncate(ctx.thread.query.index - edgeStartIndex), edgesByteSizeIndex, ); - // try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); nodeCnt += 1; if (nodeCnt >= header.limit) { break; @@ -327,7 +326,7 @@ pub fn references( }, } - i.* += header.includeSize; + i.* += header.includeSize; //+ header.edgeSize; ctx.thread.query.write(nodeCnt, sizeIndex); ctx.thread.query.writeAs( diff --git a/native/selva/references.zig b/native/selva/references.zig index 8a44faf915..36b4a9495e 100644 --- a/native/selva/references.zig +++ b/native/selva/references.zig @@ -85,7 +85,7 @@ pub fn ReferencesIteratorEdges(comptime desc: bool) type { i: u32 = 0, pub fn nextRef(self: *ReferencesIteratorEdges(desc)) ?ReferencesIteratorEdgesResult { if (self.i < self.refs.nr_refs) { - const index = if (desc) self.refs.nr_refs - self.i else self.i; + const index = if (desc) self.refs.nr_refs - self.i - 1 else self.i; const ref = self.refs.unnamed_0.large[index]; const node = Node.getNode(self.dstType, ref.dst); const edgeNode = Node.getNode(self.edgeType, ref.edge); @@ -101,7 +101,7 @@ pub fn ReferencesIteratorEdges(comptime desc: bool) type { } pub fn next(self: *ReferencesIteratorEdges(desc)) ?Node.Node { if (self.i < self.refs.nr_refs) { - const index = if (desc) self.refs.nr_refs - self.i else self.i; + const index = if (desc) self.refs.nr_refs - self.i - 1 else self.i; const ref = self.refs.unnamed_0.large[index]; const node = Node.getNode(self.dstType, ref.dst); self.i = self.i + 1; diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index 4c99d50469..fb5ffecf66 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -59,18 +59,16 @@ export const getIteratorType = ( } else { base += 1 } - } else { - if (hasFilter) { - if (isDesc) { - base += 6 - } else { - base += 2 - } - } else if (isDesc) { - base += 4 + } else if (hasFilter) { + if (isDesc) { + base += 6 } else { - base += 0 + base += 2 } + } else if (isDesc) { + base += 4 + } else { + base += 0 } return base as QueryIteratorTypeEnum diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index c4422abc3c..067c2a850c 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -61,6 +61,8 @@ export const defaultMultiple = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { ) } +// ADD IDS + export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { const rangeStart = ast.range?.start || 0 const headerIndex = pushQueryHeader(ctx.query, { @@ -87,7 +89,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { } if (ast.sort) { - pushSortHeader(ctx.query, sort(ast, ctx, prop.ref!)) + pushSortHeader(ctx.query, sort(ast, ctx, prop.ref!, prop)) } if (ast.filter) { @@ -121,7 +123,6 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { }, edges, ) - props.edgeSize(ctx.query.data, size, headerIndex) } diff --git a/src/db-query/ast/sort.ts b/src/db-query/ast/sort.ts index 03a97b6bff..bffbecad0f 100644 --- a/src/db-query/ast/sort.ts +++ b/src/db-query/ast/sort.ts @@ -1,10 +1,17 @@ -import { TypeDef } from '../../schema/defs/index.js' +import { PropDef, TypeDef } from '../../schema/defs/index.js' import { LangCode, Order } from '../../zigTsExports.js' import { Ctx, QueryAst } from './ast.js' -export const sort = (ast: QueryAst, ctx: Ctx, typeDef: TypeDef) => { +export const sort = ( + ast: QueryAst, + ctx: Ctx, + typeDef: TypeDef, + fromProp?: PropDef, +) => { const prop = typeDef.props.get(ast.sort!.prop) + // ADD SORT ON EDGE + if (!prop) { throw new Error(`Cannot find prop in sort ${ast.sort!.prop}`) } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index a82dac65c1..0044edd6fd 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -59,7 +59,7 @@ await test('include', async (t) => { }, }) - const b = client.create('user', { + const b = await client.create('user', { name: 'mr snurf b', y: 15, x: true, @@ -81,7 +81,10 @@ await test('include', async (t) => { cook: { cookie: 1234, }, - friends: [a, b], + friends: [ + { id: a, $level: 67 }, + { id: b, $level: 68 }, + ], }) } @@ -98,68 +101,74 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - order: 'desc', - sort: { prop: 'y' }, - - filter: { - props: { - flap: { ops: [{ op: '=', val: 9999 }] }, - }, - // and: { - // props: { - // y: { ops: [{ op: '=', val: 100 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 3 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 4 }] }, - // }, - // }, - // }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 670 }] }, - // }, - // or: { - // props: { - // y: { ops: [{ op: '=', val: 15 }] }, - // }, - // }, - // }, - }, + target: b, + // order: 'desc', + // sort: { prop: 'y' }, + + // filter: { + // props: { + // flap: { ops: [{ op: '=', val: 9999 }] }, + // }, + // and: { + // props: { + // y: { ops: [{ op: '=', val: 100 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 3 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 4 }] }, + // }, + // }, + // }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 670 }] }, + // }, + // or: { + // props: { + // y: { ops: [{ op: '=', val: 15 }] }, + // }, + // }, + // }, + // }, props: { y: { include: {} }, name: { include: {} }, friends: { - // order: 'desc', + order: 'desc', // sort: { prop: 'y' }, // can just be the prop? props: { name: { include: {} }, y: { include: {} }, }, - filter: { - props: { - y: { - ops: [{ op: '>', val: 10 }], - }, - }, - }, - }, - mrFriend: { - props: { - y: { include: {} }, - }, + // filter: { + // props: { + // y: { + // ops: [{ op: '>', val: 10 }], + // }, + // }, + // }, edges: { props: { $level: { include: {} }, }, }, }, + // mrFriend: { + // props: { + // y: { include: {} }, + // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, + // }, }, } From 6a9d452c93e41f4f81db627c7755431a011abe9d Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 18 Feb 2026 14:59:15 +0100 Subject: [PATCH 308/449] more types --- src/db-client/query2/index.ts | 94 +++++++++++----- src/db-client/query2/types.ts | 106 ++++++++++++------- test/aggregate/basic.ts | 10 +- test/aggregate/deep.ts | 194 ++++++++++------------------------ test/query/ast.ts | 1 - test/query/db.ts | 14 +++ 6 files changed, 210 insertions(+), 209 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index c5f7870357..1f44cac048 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -10,6 +10,8 @@ import type { FilterEdges, InferSchemaOutput, NumberPaths, + ExpandDotPath, + UnionToIntersection, } from './types.js' import type { ResolvedProps, SchemaOut } from '../../schema/index.js' import { astToQueryCtx } from '../../db-query/ast/toCtx.js' @@ -43,13 +45,22 @@ class Query< } ast: QueryAst include< - F extends ( - | (keyof (ResolvedProps & EdgeProps) & string) - | Path - | '*' - | '**' - | ((q: SelectFn) => Query) - )[], + F extends [ + ( + | (keyof (ResolvedProps & EdgeProps) & string) + | Path + | '*' + | '**' + | ((q: SelectFn) => Query) + ), + ...( + | (keyof (ResolvedProps & EdgeProps) & string) + | Path + | '*' + | '**' + | ((q: SelectFn) => Query) + )[], + ], >( ...props: F ): NextBranch< @@ -63,6 +74,9 @@ class Query< Aggregate, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: include expects at least one argument') + } for (const prop of props as (string | Function)[]) { if (typeof prop === 'function') { prop((prop: string) => new Query(traverse(this.ast, prop))) @@ -134,7 +148,7 @@ class Query< } sum

>( - ...props: P[] + ...props: [P, ...P[]] ): NextBranch< S, T, @@ -143,11 +157,14 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { [Key in P]: { sum: number } }, + Aggregate & UnionToIntersection>, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: sum expects at least one argument') + } this.ast.sum ??= { props: [] } - this.ast.sum.props.push(...(props as string[])) + this.ast.sum.props.push(...(props as string[])) // Safe cast as P is string-like key return this as any } @@ -167,7 +184,7 @@ class Query< } cardinality

( - ...props: P[] + ...props: [P, ...P[]] ): NextBranch< S, T, @@ -176,16 +193,19 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { [Key in P]: { cardinality: number } }, + Aggregate & UnionToIntersection>, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: cardinality expects at least one argument') + } this.ast.cardinality ??= { props: [] } this.ast.cardinality.props.push(...props) return this as any } avg

>( - ...props: P[] + ...props: [P, ...P[]] ): NextBranch< S, T, @@ -194,16 +214,19 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { [Key in P]: { avg: number } }, + Aggregate & UnionToIntersection>, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: avg expects at least one argument') + } this.ast.avg ??= { props: [] } this.ast.avg.props.push(...(props as string[])) return this as any } hmean

>( - ...props: P[] + ...props: [P, ...P[]] ): NextBranch< S, T, @@ -212,16 +235,19 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { [Key in P]: { hmean: number } }, + Aggregate & UnionToIntersection>, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: hmean expects at least one argument') + } this.ast.hmean ??= { props: [] } this.ast.hmean.props.push(...(props as string[])) return this as any } max

>( - ...props: P[] + ...props: [P, ...P[]] ): NextBranch< S, T, @@ -230,16 +256,20 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { [Key in P]: { max: InferPathType } }, + Aggregate & + UnionToIntersection }>>, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: max expects at least one argument') + } this.ast.max ??= { props: [] } this.ast.max.props.push(...(props as string[])) return this as any } min

>( - ...props: P[] + ...props: [P, ...P[]] ): NextBranch< S, T, @@ -248,9 +278,13 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { [Key in P]: { min: InferPathType } }, + Aggregate & + UnionToIntersection }>>, GroupedKey > { + if (props.length === 0) { + throw new Error('Query: min expects at least one argument') + } this.ast.min ??= { props: [] } this.ast.min.props.push(...(props as string[])) return this as any @@ -267,11 +301,15 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { - [Key in P extends any[] ? P[number] : P]: { stddev: number } - }, + Aggregate & + UnionToIntersection< + ExpandDotPath

+ >, GroupedKey > { + if (!prop) { + throw new Error('Query: stddev expects at least one argument') + } this.ast.stddev ??= { props: [] } const props = Array.isArray(prop) ? prop : [prop] this.ast.stddev.props.push(...(props as string[])) @@ -292,11 +330,15 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & { - [Key in P extends any[] ? P[number] : P]: { variance: number } - }, + Aggregate & + UnionToIntersection< + ExpandDotPath

+ >, GroupedKey > { + if (!prop) { + throw new Error('Query: var expects at least one argument') + } this.ast.variance ??= { props: [] } const props = Array.isArray(prop) ? prop : [prop] this.ast.variance.props.push(...(props as string[])) diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 4f71ea0be9..3769ceb46a 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -233,75 +233,103 @@ export type EdgePaths = { : never) }[keyof FilterEdges & string] -export type Path = [ - Depth, -] extends [never] +type PropsPath = [Depth] extends [never] ? never : { - [K in keyof ResolvedProps & string]: + [K in keyof Props & string]: | K - | (ResolvedProps[K] extends { ref: infer R extends string } + | (Props[K] extends { ref: infer R extends string } ? `${K}.${ | Path - | EdgePaths[K], Prev[Depth]> + | EdgePaths | 'id' | '*' | '**'}` - : ResolvedProps[K] extends { - items: { ref: infer R extends string } & infer Items - } - ? `${K}.${ - | Path - | EdgePaths - | 'id' - | '*' - | '**'}` - : never) - }[keyof ResolvedProps & string] + : Props[K] extends { props: infer P } + ? `${K}.${PropsPath}` + : Props[K] extends { + items: { ref: infer R extends string } & infer Items + } + ? `${K}.${ + | Path + | EdgePaths + | 'id' + | '*' + | '**'}` + : never) + }[keyof Props & string] + +export type Path< + Schema, + T extends keyof Schema, + Depth extends number = 5, +> = PropsPath, Depth> export type ResolveDotPath = T extends `${infer Head}.${infer Tail}` ? { field: Head; select: ResolveDotPath } : T -export type InferPathType< +type InferPropsPathType< S extends { types: any; locales?: any }, - T extends keyof S['types'], + Props, P, > = P extends 'id' ? number - : P extends keyof ResolvedProps - ? InferProp[P], S['types']> + : P extends keyof Props + ? InferProp< + Props[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > : P extends `${infer Head}.${infer Tail}` - ? Head extends keyof ResolvedProps - ? ResolvedProps[Head] extends { - ref: infer R extends string - } - ? Tail extends keyof FilterEdges[Head]> + ? Head extends keyof Props + ? Props[Head] extends { ref: infer R extends string } + ? Tail extends keyof FilterEdges ? InferProp< - ResolvedProps[Head][Tail & - keyof ResolvedProps[Head]], + Props[Head][Tail & keyof Props[Head]], S['types'], S['locales'] extends Record ? S['locales'] : {} > : InferPathType - : ResolvedProps[Head] extends { - items: { ref: infer R extends string } & infer Items - } - ? Tail extends keyof FilterEdges - ? InferProp< - Items[Tail & keyof Items], - S['types'], - S['locales'] extends Record ? S['locales'] : {} - > - : InferPathType - : never + : Props[Head] extends { props: infer NestedProps } + ? InferPropsPathType + : Props[Head] extends { + items: { ref: infer R extends string } & infer Items + } + ? Tail extends keyof FilterEdges + ? InferProp< + Items[Tail & keyof Items], + S['types'], + S['locales'] extends Record ? S['locales'] : {} + > + : InferPathType + : never : never : never +export type InferPathType< + S extends { types: any; locales?: any }, + T extends keyof S['types'], + P, +> = InferPropsPathType, P> + export type NumberPaths< S extends { types: any; locales?: any }, T extends keyof S['types'], > = { [K in Path]: InferPathType extends number ? K : never }[Path] + +export type ExpandDotPath< + T extends string, + V, +> = T extends `${infer Head}.${infer Tail}` + ? { [K in Head]: ExpandDotPath } + : { [K in T]: V } + +export type UnionToIntersection = ( + U extends any ? (k: U) => void : never +) extends (k: infer I) => void + ? I + : never diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 44ca5a7123..e25f2ad5b0 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -1,8 +1,5 @@ -import { equal } from 'node:assert' -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { throws, deepEqual } from '../shared/assert.js' -import { fastPrng } from '../../src/utils/index.js' import { testDb } from '../shared/index.js' await test('sum top level', async (t) => { @@ -66,6 +63,7 @@ await test('sum top level', async (t) => { ) throws(async () => { + // @ts-expect-error await db.query2('vote').sum().get() }, 'sum() returning nothing') @@ -75,7 +73,7 @@ await test('sum top level', async (t) => { 'nested object notation', ) }) -/* + await test('top level count', async (t) => { const db = await testDb(t, { types: { @@ -168,7 +166,7 @@ await test('top level count', async (t) => { ) deepEqual( - await db.query2('vote').filter('NL', '>', 1e6).count().get(), + await db.query2('vote').filter('NL', '>', 255).count().get(), { count: 0 }, 'count, with no match filtering, int value', ) @@ -322,7 +320,7 @@ await test('two phase accumulation', async (t) => { 'stddev, branched References, groupBy', ) }) - +/* await test('numeric types', async (t) => { const db = await testDb(t, { types: { diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index d8fd60a8ad..76a3d42380 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -1,23 +1,12 @@ /* * Deep = Reference(s), Edges and nests */ -import { equal } from 'node:assert' -import { BasedDb } from '../../src/index.js' -import { allCountryCodes } from '../shared/examples.js' import test from '../shared/test.js' -import { throws, deepEqual } from '../shared/assert.js' -import { fastPrng } from '../../src/utils/index.js' +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('sum branched includes', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -64,24 +53,18 @@ await test('sum branched includes', async (t) => { deepEqual( await db - .query('sequence') - .include((select) => { - select('votes').sum('NL', 'AU') - }) - .get() - .toObject(), + .query2('sequence') + .include((select) => select('votes').sum('NL', 'AU')) + .get(), [{ id: 1, votes: { NL: { sum: 30 }, AU: { sum: 15 } } }], 'brached include, sum, references', ) deepEqual( await db - .query('sequence') - .include((select) => { - select('votes').groupBy('country').sum('NL', 'AU') - }) - .get() - .toObject(), + .query2('sequence') + .include((select) => select('votes').groupBy('country').sum('NL', 'AU')) + .get(), [ { id: 1, @@ -96,27 +79,19 @@ await test('sum branched includes', async (t) => { // deepEqual( // await db - // .query('sequence') + // .query2('sequence') // .include((select) => { // select('votes').filter('country', '=', 'aa').sum('NL', 'AU') // string filter not implemented and also filter in refs group not implemented // }) // .get() - // .toObject(), + // , // [{ id: 1, votes: { NL: { sum: 20 }, AU: { sum: 15 } } }], // 'branched include, references, filtered, groupBy', // ) }) await test('count branched includes', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -163,24 +138,18 @@ await test('count branched includes', async (t) => { deepEqual( await db - .query('sequence') - .include((select) => { - select('votes').count() - }) - .get() - .toObject(), + .query2('sequence') + .include((select) => select('votes').count()) + .get(), [{ id: 1, votes: { count: 3 } }], 'brached include, count, references', ) deepEqual( await db - .query('sequence') - .include((select) => { - select('votes').groupBy('country').sum('NL', 'AU') - }) - .get() - .toObject(), + .query2('sequence') + .include((select) => select('votes').groupBy('country').sum('NL', 'AU')) + .get(), [ { id: 1, @@ -195,27 +164,19 @@ await test('count branched includes', async (t) => { // deepEqual( // await db - // .query('sequence') + // .query2('sequence') // .include((select) => { // select('votes').filter('country', '=', 'aa').count() // string filter not implemented and also filter in refs group not implemented // }) // .get() - // .toObject(), + // , // [{ id: 1, votes: { count: 2 } }], // 'count, branched include, references, filtered', // ) }) await test('agg on references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { team: { props: { @@ -306,14 +267,14 @@ await test('agg on references', async (t) => { }) const result = await db - .query('team') - .include('teamName', 'city', (select) => { - select('players').groupBy('position').sum('goalsScored', 'gamesPlayed') - }) + .query2('team') + .include('teamName', 'city', (select) => + select('players').groupBy('position').sum('goalsScored', 'gamesPlayed'), + ) .get() deepEqual( - result.toObject(), + result, [ { id: 1, @@ -353,14 +314,8 @@ await test('agg on references', async (t) => { }) await test('enums', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - const types = ['IPA', 'Lager', 'Ale', 'Stout', 'Wit', 'Dunkel', 'Tripel'] - await db.setSchema({ + const db = await testDb(t, { types: { beer: { props: { @@ -399,7 +354,7 @@ await test('enums', async (t) => { }) deepEqual( - await db.query('beer').avg('price').groupBy('type').get().toObject(), + await db.query2('beer').avg('price').groupBy('type').get(), { Tripel: { price: { avg: 11.85 }, @@ -412,7 +367,7 @@ await test('enums', async (t) => { ) deepEqual( - await db.query('beer').hmean('price').groupBy('type').get().toObject(), + await db.query2('beer').hmean('price').groupBy('type').get(), { Tripel: { price: { hmean: 11.839662447257384 }, @@ -426,13 +381,7 @@ await test('enums', async (t) => { }) await test.skip('refs with enums ', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { movie: { name: 'string', @@ -469,10 +418,9 @@ await test.skip('refs with enums ', async (t) => { deepEqual( await db - .query('actor') + .query2('actor') .include((q) => q('movies').groupBy('genre').count()) - .get() - .toObject(), + .get(), [ { id: 1, @@ -496,13 +444,7 @@ await test.skip('refs with enums ', async (t) => { }) await test('cardinality', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { lunch: { week: 'string', @@ -563,13 +505,13 @@ await test('cardinality', async (t) => { }) deepEqual( - await db.query('lunch').cardinality('Mon').get().toObject(), + await db.query2('lunch').cardinality('Mon').get(), { Mon: { cardinality: 7 } }, 'main cardinality no group by', ) deepEqual( - await db.query('lunch').cardinality('Mon').groupBy('week').get().toObject(), + await db.query2('lunch').cardinality('Mon').groupBy('week').get(), { 27: { Mon: { cardinality: 5 }, @@ -621,9 +563,9 @@ await test('cardinality', async (t) => { // booths: [bg, stp], // }) -// await db.query('fair').include('booths.badgesScanned').get().inspect() +// await db.query2('fair').include('booths.badgesScanned').get().inspect() // await db -// .query('fair') +// .query2('fair') // .cardinality('booths.badgesScanned') // .groupBy('day') // .get() @@ -631,13 +573,7 @@ await test('cardinality', async (t) => { // }) await test('group by reference ids', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { trip: { pickup: 'timestamp', @@ -691,7 +627,7 @@ await test('group by reference ids', async (t) => { }) deepEqual( - await db.query('driver').sum('rank').groupBy('vehicle').get().toObject(), + await db.query2('driver').sum('rank').groupBy('vehicle').get(), { 2: { rank: { sum: 5 }, @@ -702,11 +638,10 @@ await test('group by reference ids', async (t) => { deepEqual( await db - .query('driver') + .query2('driver') .include((q) => q('trips').groupBy('vehicle').max('distance')) .include('*') - .get() - .toObject(), + .get(), [ { id: 1, @@ -724,13 +659,7 @@ await test('group by reference ids', async (t) => { }) await test.skip('nested references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -763,10 +692,10 @@ await test.skip('nested references', async (t) => { strong: 4, }) - // await db.query('user').include('*', '**').get().inspect(10) + // await db.query2('user').include('*', '**').get().inspect(10) deepEqual( - await db.query('user').sum('friends.strong').get().toObject(), + await db.query2('user').sum('friends.strong').get(), { strong: { sum: 7, @@ -777,13 +706,7 @@ await test.skip('nested references', async (t) => { }) await test.skip('edges aggregation', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { movie: { name: 'string', @@ -849,7 +772,7 @@ await test.skip('edges aggregation', async (t) => { }) // await db - // .query('movie') + // .query2('movie') // .include('*', '**') // // .include('actors.$rating') // // .include('actors.name') @@ -864,30 +787,29 @@ await test.skip('edges aggregation', async (t) => { // after: NOK: unreacheable // console.log( // JSON.stringify( - // await db.query('movie').include('actors.strong').get().toObject(), + // await db.query2('movie').include('actors.strong').get(), // ), // ) // before: NOK: error in js: Cannot read properties of undefined (reading 'edges') // after: NOK: zeroing - // await db.query('movie').include('actors.$rating').get().inspect(10) + // await db.query2('movie').include('actors.$rating').get().inspect(10) /*----------------------------*/ /* BRANCHED QUERY */ /*----------------------------*/ // await db - // .query('movie') + // .query2('movie') // .include((q) => q('actors').max('strong').sum('strong2')) // .get() // .inspect(10) deepEqual( await db - .query('movie') + .query2('movie') .include((q) => q('actors').max('$rating')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -911,10 +833,9 @@ await test.skip('edges aggregation', async (t) => { deepEqual( await db - .query('movie') + .query2('movie') .include((q) => q('actors').max('$rating').sum('$hating')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -944,10 +865,9 @@ await test.skip('edges aggregation', async (t) => { deepEqual( await db - .query('movie') + .query2('movie') .include((q) => q('actors').max('$rating', '$hating')) - .get() - .toObject(), + .get(), [ { id: 1, @@ -980,6 +900,6 @@ await test.skip('edges aggregation', async (t) => { /*-----------------------------------*/ // before: OK: error in js: Cannot read properties of undefined (reading 'edges') // after: NOK: feature not implemented - // await db.query('actor').max('$rating').get().inspect(10) - // await db.query('actor').sum('strong').get().inspect(10) // this is OK, summing all strong props in the type actor + // await db.query2('actor').max('$rating').get().inspect(10) + // await db.query2('actor').sum('strong').get().inspect(10) // this is OK, summing all strong props in the type actor }) diff --git a/test/query/ast.ts b/test/query/ast.ts index 6db5673fdc..e09b7478d2 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -223,7 +223,6 @@ await test('query ast creation', async (t) => { deepEqual(q3.ast, { type: 'user', order: 'desc', - sort: { prop: 'id' }, }) const q4 = query('user').sort('age').order('desc') diff --git a/test/query/db.ts b/test/query/db.ts index f50df8e498..6a6bc8f64a 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -106,6 +106,20 @@ await test('query db', async (t) => { deepEqual(res, { age: { sum: 70 } }) } + { + const res = await db.query2('user').sum('friend.age').get() + deepEqual(res, { friend: { age: { sum: 70 } } }) + } + + { + const res = await db + .query2('user') + .include((select) => select('friend').sum('age')) + .get() + + deepEqual(res, [{ friend: { age: { sum: 70 } } }]) + } + { const res = await db.query2('user').sum('age').groupBy('name').get() deepEqual(res, { john: { age: { sum: 21 } }, billy: { age: { sum: 49 } } }) From 493552838895f2b71f7744bc9e28da5854096c7f Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 14:59:23 +0100 Subject: [PATCH 309/449] sort on edge --- src/db-query/ast/sort.ts | 20 +++++++++++++++++--- test/query-ast/include.ts | 11 +++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/db-query/ast/sort.ts b/src/db-query/ast/sort.ts index bffbecad0f..6d2e6ed20b 100644 --- a/src/db-query/ast/sort.ts +++ b/src/db-query/ast/sort.ts @@ -8,14 +8,28 @@ export const sort = ( typeDef: TypeDef, fromProp?: PropDef, ) => { - const prop = typeDef.props.get(ast.sort!.prop) + const field = ast.sort!.prop - // ADD SORT ON EDGE + if (field[0] === '$') { + const prop = fromProp?.edges?.props.get(field) + if (!prop) { + throw new Error(`Cannot find edge prop in sort ${ast.sort!.prop}`) + } + return { + prop: prop.id, + propType: prop?.type, + start: prop.start, + len: prop.size, + edgeType: fromProp?.edges?.id, // do in a bit + order: ast.order === 'asc' ? Order.asc : Order.desc, + lang: LangCode.none, + } + } + const prop = typeDef.props.get(field) if (!prop) { throw new Error(`Cannot find prop in sort ${ast.sort!.prop}`) } - return { prop: prop.id, propType: prop?.type, diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 0044edd6fd..313e755dd9 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -12,6 +12,7 @@ import wait from '../../src/utils/wait.js' import { perf } from '../shared/perf.js' import test from '../shared/test.js' import { deflateSync } from 'zlib' +import { fastPrng } from '../../src/utils/fastPrng.js' await test('include', async (t) => { const db = new BasedDb({ path: t.tmp }) @@ -72,6 +73,8 @@ await test('include', async (t) => { let d = Date.now() + const rand = fastPrng() + for (let i = 0; i < 10; i++) { client.create('user', { name: `mr snurf ${i}`, @@ -82,8 +85,8 @@ await test('include', async (t) => { cookie: 1234, }, friends: [ - { id: a, $level: 67 }, - { id: b, $level: 68 }, + { id: a, $level: rand(0, 1000) }, + { id: b, $level: rand(0, 1000) }, ], }) } @@ -140,8 +143,8 @@ await test('include', async (t) => { y: { include: {} }, name: { include: {} }, friends: { - order: 'desc', - // sort: { prop: 'y' }, // can just be the prop? + order: 'asc', + sort: { prop: '$level' }, // can just be the prop? props: { name: { include: {} }, y: { include: {} }, From d84bfab13db962cda4889af2b443789e778b1ec8 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 18 Feb 2026 15:15:13 +0100 Subject: [PATCH 310/449] more types --- src/db-client/query2/index.ts | 14 ++++++++++---- src/db-client/query2/types.ts | 12 ++++++++++++ test/query/db.ts | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 1f44cac048..6ece8829d0 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -51,14 +51,18 @@ class Query< | Path | '*' | '**' - | ((q: SelectFn) => Query) + | (( + q: SelectFn, + ) => Query) ), ...( | (keyof (ResolvedProps & EdgeProps) & string) | Path | '*' | '**' - | ((q: SelectFn) => Query) + | (( + q: SelectFn, + ) => Query) )[], ], >( @@ -628,10 +632,12 @@ export type ResolveIncludeArgs = T extends ( infer SourceField, any, any, - any, + infer Aggregate, any > - ? { field: SourceField; select: K } + ? [keyof Aggregate] extends [never] + ? { field: SourceField; select: K } + : { field: SourceField; select: { _aggregate: Aggregate } } : T extends string ? ResolveDotPath : T diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 3769ceb46a..ae6a9ff225 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -85,6 +85,18 @@ export type InferProp< Types, Locales extends Record = Record, Selection = never, +> = + IsSelected extends false + ? InferPropLogic + : Selection extends { _aggregate: infer Agg } + ? Agg + : InferPropLogic + +type InferPropLogic< + Prop, + Types, + Locales extends Record = Record, + Selection = never, > = Prop extends { type: 'text' } ? { [K in keyof Locales]-?: string } : Prop extends { type: 'object'; props: infer P } diff --git a/test/query/db.ts b/test/query/db.ts index 6a6bc8f64a..3a28556b2d 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -117,7 +117,7 @@ await test('query db', async (t) => { .include((select) => select('friend').sum('age')) .get() - deepEqual(res, [{ friend: { age: { sum: 70 } } }]) + deepEqual(res, [{ id: 1, friend: { age: { sum: 70 } } }]) } { From 435aecfa84873da42fe0dfdaaa1b2390e14cc73e Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 18 Feb 2026 15:36:14 +0100 Subject: [PATCH 311/449] more aggregate query types --- src/db-client/query2/index.ts | 21 +++++++++++++------ src/schema/schema/hooks.ts | 1 + test/query/ast.ts | 10 +++++++++ test/query/db.ts | 38 +++++++++++++++++++++++++++-------- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 6ece8829d0..72dc75d1b4 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -51,18 +51,14 @@ class Query< | Path | '*' | '**' - | (( - q: SelectFn, - ) => Query) + | ((q: SelectFn) => AnyQuery) ), ...( | (keyof (ResolvedProps & EdgeProps) & string) | Path | '*' | '**' - | (( - q: SelectFn, - ) => Query) + | ((q: SelectFn) => AnyQuery) )[], ], >( @@ -642,6 +638,19 @@ export type ResolveIncludeArgs = T extends ( ? ResolveDotPath : T +// Helper type to simplify include signature +type AnyQuery = Query< + S, + any, + any, + any, + any, + any, + any, + any, + any +> + // Helper type to simplify method return types type NextBranch< S extends { types: any }, diff --git a/src/schema/schema/hooks.ts b/src/schema/schema/hooks.ts index 6daf11bcf3..12e6e2db1f 100644 --- a/src/schema/schema/hooks.ts +++ b/src/schema/schema/hooks.ts @@ -1,5 +1,6 @@ import { isFunction, isRecord } from './shared.js' +// TODO remove these type BasedDbQuery = any type Operator = any diff --git a/test/query/ast.ts b/test/query/ast.ts index e09b7478d2..138f967da3 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -232,4 +232,14 @@ await test('query ast creation', async (t) => { sort: { prop: 'age' }, }) } + + { + const res = query('user').include((select) => select('friend').sum('age')) + deepEqual(res.ast, { + type: 'user', + props: { + friend: { sum: { props: ['age'] } }, + }, + }) + } }) diff --git a/test/query/db.ts b/test/query/db.ts index 3a28556b2d..9769c96552 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -21,11 +21,17 @@ await test('query db', async (t) => { ref: 'user', prop: 'friend', }, + friends: { + items: { + ref: 'user', + prop: 'friends', + }, + }, }, }, }) - db.create('user', { + const john = db.create('user', { name: 'john', isNice: false, age: 21, @@ -38,6 +44,8 @@ await test('query db', async (t) => { name: 'billy', isNice: true, age: 49, + friend: john, + friends: [john], address: { street: 'Mega street', }, @@ -106,20 +114,34 @@ await test('query db', async (t) => { deepEqual(res, { age: { sum: 70 } }) } - { - const res = await db.query2('user').sum('friend.age').get() - deepEqual(res, { friend: { age: { sum: 70 } } }) - } + // TODO wait for marco to check these + // { + // const res = await db.query2('user').sum('friend.age').get() + // deepEqual(res, { friend: { age: { sum: 70 } } }) + // } + + // { + // const res = await db.query2('user').sum('friends.age').get() + // deepEqual(res, { friends: { age: { sum: 70 } } }) + // } { const res = await db .query2('user') - .include((select) => select('friend').sum('age')) + .sum((select) => select('friend').sum('age')) .get() - - deepEqual(res, [{ id: 1, friend: { age: { sum: 70 } } }]) + deepEqual(res, { friend: { age: { sum: 70 } } }) } + // { + // const res = await db + // .query2('user') + // .include((select) => select('friend').sum('age')) + // .get() + + // deepEqual(res, [{ id: 1, friend: { age: { sum: 70 } } }]) + // } + { const res = await db.query2('user').sum('age').groupBy('name').get() deepEqual(res, { john: { age: { sum: 21 } }, billy: { age: { sum: 49 } } }) From afe983f5949ff56e5718b9f897bcfaa0ef579bba Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 15:47:48 +0100 Subject: [PATCH 312/449] fix --- native/query/multiple.zig | 2 ++ src/db-query/ast/ast.ts | 3 +++ src/db-query/ast/iteratorType.ts | 3 +++ src/db-query/ast/multiple.ts | 8 +++++++- src/db-query/ast/sort.ts | 2 +- test/query-ast/include.ts | 14 +++++++------- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index c919031618..a942e81db4 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -241,6 +241,7 @@ pub fn references( const sizeIndex = try ctx.thread.query.reserve(4); const typeEntry = try Node.getType(ctx.db, header.typeId); var nodeCnt: u32 = 0; + std.debug.print("-> {any} \n", .{header.iteratorType}); switch (header.iteratorType) { .default => { @@ -287,6 +288,7 @@ pub fn references( }, .edgeIncludeDesc => { var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); }, .edgeIncludeSort => { diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 1420532e98..992ff94836 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -46,6 +46,9 @@ export type FilterAst = { or?: FilterAst and?: FilterAst edges?: FilterAst + + // this is a bit difficult combining OR filters with edges combined + // this would require and extra check for the type of node how to do? } export type Include = { diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index fb5ffecf66..0dd2afc5a3 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -1,3 +1,4 @@ +import { PropDef } from '../../schema.js' import { QUERY_ITERATOR_DEFAULT, QUERY_ITERATOR_EDGE, @@ -25,6 +26,8 @@ export const getIteratorType = ( let base = QUERY_ITERATOR_DEFAULT + console.log('EDGE TIME', edgeInclude, edge) + if (edge && !edgeInclude) { base = QUERY_ITERATOR_EDGE } diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 067c2a850c..044b4e768a 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -76,7 +76,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { filterSize: 0, searchSize: 0, iteratorType: QueryIteratorType.default, - edgeTypeId: 0, + edgeTypeId: prop.edges?.id ?? 0, edgeSize: 0, edgeFilterSize: 0, size: 0, // this is only used for [IDS] handle this differently @@ -108,6 +108,10 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { props.includeSize(ctx.query.data, size, headerIndex) + // EDGES IN FILTER + // needs to use special iterator + // we need to put the filter on it + if (ast.edges) { const edges = prop.edges if (!edges) { @@ -124,6 +128,8 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { edges, ) props.edgeSize(ctx.query.data, size, headerIndex) + } else { + console.info('EDGES NO!') } props.iteratorType( diff --git a/src/db-query/ast/sort.ts b/src/db-query/ast/sort.ts index 6d2e6ed20b..6133ef7a05 100644 --- a/src/db-query/ast/sort.ts +++ b/src/db-query/ast/sort.ts @@ -20,7 +20,7 @@ export const sort = ( propType: prop?.type, start: prop.start, len: prop.size, - edgeType: fromProp?.edges?.id, // do in a bit + edgeType: fromProp?.edges?.id || 0, order: ast.order === 'asc' ? Order.asc : Order.desc, lang: LangCode.none, } diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 313e755dd9..8ef6ab23e7 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -143,8 +143,8 @@ await test('include', async (t) => { y: { include: {} }, name: { include: {} }, friends: { - order: 'asc', - sort: { prop: '$level' }, // can just be the prop? + // order: 'asc', + // sort: { prop: '$level' }, // can just be the prop? props: { name: { include: {} }, y: { include: {} }, @@ -156,11 +156,11 @@ await test('include', async (t) => { // }, // }, // }, - edges: { - props: { - $level: { include: {} }, - }, - }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, }, // mrFriend: { // props: { From d152e61a0c8aeccefcc4039ef5b1a9b68feeed4d Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 16:29:13 +0100 Subject: [PATCH 313/449] edge include --- native/query/multiple.zig | 76 ++++++++++++++++++++++++++---------- src/db-query/ast/multiple.ts | 3 ++ test/query-ast/include.ts | 24 ++++++------ 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index a942e81db4..79217fe269 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -62,7 +62,7 @@ fn iterator( } fn iteratorEdge( - comptime _: t.QueryIteratorType, + comptime It: t.QueryIteratorType, ctx: *Query.QueryCtx, q: []u8, it: anytype, @@ -72,6 +72,13 @@ fn iteratorEdge( ) !u32 { var offset: u32 = header.offset; var nodeCnt: u32 = 0; + + var filter: []u8 = undefined; + if (It == t.QueryIteratorType.filter) { + filter = utils.sliceNext(header.filterSize, q, i); + try Filter.prepare(filter, ctx, typeEntry); + } + const nestedQuery = q[i.* .. i.* + header.includeSize]; const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; @@ -81,6 +88,12 @@ fn iteratorEdge( } while (it.nextRef()) |ref| { + if (It == t.QueryIteratorType.filter) { + if (!try Filter.filter(ref.node, ctx, filter)) { + continue; + } + } + try ctx.thread.query.append(t.ReadOp.id); try ctx.thread.query.append(Node.getNodeId(ref.node)); try Include.include(ref.node, ctx, nestedQuery, typeEntry); @@ -264,6 +277,25 @@ pub fn references( it.deinit(); }, + .filter => { + var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .descFilter => { + var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .filterSort => { + var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .descFilterSort => { + var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); @@ -282,13 +314,31 @@ pub fn references( nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + .edgeFilter => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeDescFilter => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeFilterSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeDescFilterSort => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeInclude => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); }, .edgeIncludeDesc => { var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); }, .edgeIncludeSort => { @@ -301,25 +351,11 @@ pub fn references( nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - - .filter => { - var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .descFilter => { - var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .filterSort => { - var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .descFilterSort => { - var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); + .edgeIncludeFilter => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); }, + // all filter // filter diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 044b4e768a..769193ef66 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -127,6 +127,9 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { }, edges, ) + // make a iterator type EDGE FILTER OR (means filter fails now also run edge and ignore the failure) + // or add comptime thing that just passed HAS_EDGE then pass the edge in the c and have an extra check + // may be the easier to do props.edgeSize(ctx.query.data, size, headerIndex) } else { console.info('EDGES NO!') diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 8ef6ab23e7..6853bd26a9 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -149,18 +149,18 @@ await test('include', async (t) => { name: { include: {} }, y: { include: {} }, }, - // filter: { - // props: { - // y: { - // ops: [{ op: '>', val: 10 }], - // }, - // }, - // }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, + filter: { + props: { + y: { + ops: [{ op: '>', val: 6 }], + }, + }, + }, + edges: { + props: { + $level: { include: {} }, + }, + }, }, // mrFriend: { // props: { From db0fb6f36ba559fc3864494f0be48ab88914f07b Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 16:32:19 +0100 Subject: [PATCH 314/449] filter --- native/query/multiple.zig | 28 ++++++++++++++++++++-------- src/db-query/ast/filter/filter.ts | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 79217fe269..cf0f68a1fe 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -254,7 +254,6 @@ pub fn references( const sizeIndex = try ctx.thread.query.reserve(4); const typeEntry = try Node.getType(ctx.db, header.typeId); var nodeCnt: u32 = 0; - std.debug.print("-> {any} \n", .{header.iteratorType}); switch (header.iteratorType) { .default => { @@ -332,32 +331,45 @@ pub fn references( nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + // 4 more .edgeInclude => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); }, .edgeIncludeDesc => { var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); }, .edgeIncludeSort => { var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, .edgeIncludeDescSort => { var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iteratorEdge(.edgeInclude, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, .edgeIncludeFilter => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); }, - // all filter - - // filter + .edgeIncludeDescFilter => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeFilterSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeIncludeDescFilterSort => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + // 4 more else => { // not handled diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index 7bcd75405b..36630c96eb 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -86,6 +86,8 @@ const indexOf = ( return -1 } +// filter + EDGE + export const filter = ( ast: FilterAst, ctx: Ctx, From 615131c11f075e3638d2f9a6f71d87542cb1b7df Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 17:09:10 +0100 Subject: [PATCH 315/449] fix --- native/query/filter/filter.zig | 2 ++ native/query/multiple.zig | 10 ++++++++-- src/server/incoming/index.ts | 2 ++ src/server/server.ts | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 61857e8d57..858ad51110 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -100,6 +100,8 @@ inline fn compare( return if (meta.invert) !res else res; } +// MAKE EDGE FILTER + // Check if this becomes better pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { var i: usize = 0; diff --git a/native/query/multiple.zig b/native/query/multiple.zig index cf0f68a1fe..843140a15e 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -331,7 +331,10 @@ pub fn references( nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - // 4 more + // 12 more edgeIncludeFilterEdge + // edgeFilter + // filterORedgeFilter + // filtertAndedgeFilter .edgeInclude => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); @@ -369,7 +372,10 @@ pub fn references( nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - // 4 more + // 12 more edgeIncludeFilterEdge + // edgeFilter + // filterORedgeFilter + // filtertAndedgeFilter else => { // not handled diff --git a/src/server/incoming/index.ts b/src/server/incoming/index.ts index 082e46bd54..9c0db494e1 100644 --- a/src/server/incoming/index.ts +++ b/src/server/incoming/index.ts @@ -142,6 +142,8 @@ export default ( app.options('/*', (res, req) => httpHandler(server, req, res)) app.head('/*', (res, req) => httpHandler(server, req, res)) app.trace('/*', (res, req) => httpHandler(server, req, res)) + + // app.ad } server.uwsApp = app diff --git a/src/server/server.ts b/src/server/server.ts index 5934dd2b8e..cbfb8f2b55 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -109,6 +109,7 @@ server.functions.addRoutes({ await server.start() ``` */ + export class BasedServer { public console: Console = console From 6a60dfb719b35079ef9783002c528681db9f05fb Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 16:18:21 +0100 Subject: [PATCH 316/449] Use auto --- clibs/lib/selva/db.c | 3 +-- clibs/lib/selva/fields.c | 52 ++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 6d05be3ad5..899cf1ae26 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -115,10 +115,9 @@ void selva_expire_node_cancel(struct SelvaDb *db, node_type_t type, node_id_t no static void expire_cb(struct SelvaExpireToken *tok, void *) { struct SelvaDbExpireToken *token = containerof(tok, typeof(*token), token); - struct SelvaTypeEntry *te; struct SelvaNodeRes res; - te = selva_get_type_by_index(token->db, token->type); + auto te = selva_get_type_by_index(token->db, token->type); assert(te); res = selva_find_node(te, token->node_id); if (res.node) { diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index 0a89f43718..a8a17158a9 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -66,7 +66,7 @@ size_t selva_fields_get_data_size(const struct SelvaFieldSchema *fs) static struct SelvaFieldInfo alloc_block(struct SelvaFields *fields, const struct SelvaFieldSchema *fs) { - char *data = (char *)fields->data; + auto *data = (char *)fields->data; const size_t off = fields->data_len; const size_t field_data_size = selva_fields_get_data_size(fs); const size_t new_size = ALIGNED_SIZE(off + field_data_size, SELVA_FIELDS_DATA_ALIGN); @@ -95,7 +95,7 @@ static struct SelvaFieldInfo alloc_block(struct SelvaFields *fields, const struc #endif static inline void *nfo2p(const struct SelvaFields *fields, const struct SelvaFieldInfo *nfo) { - char *data = (char *)fields->data; + auto *data = (char *)fields->data; void *p = data + (nfo->off << SELVA_FIELDS_OFF); if (unlikely((char *)p > data + fields->data_len)) { @@ -141,8 +141,8 @@ struct SelvaNodeLargeReference *selva_fields_ensure_reference( struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; - struct SelvaFieldInfo *nfo = ensure_field(fields, fs); + auto fields = &node->fields; + auto nfo = ensure_field(fields, fs); return nfo2p(fields, nfo); } @@ -153,7 +153,7 @@ struct SelvaNodeLargeReference *selva_fields_ensure_reference( */ static struct SelvaFieldInfo *ensure_field_references(struct SelvaFields *fields, const struct SelvaFieldSchema *fs, enum SelvaNodeReferenceType type) { - struct SelvaFieldInfo *nfo = ensure_field(fields, fs); + auto nfo = ensure_field(fields, fs); if (fs->type == SELVA_FIELD_TYPE_REFERENCES) { struct SelvaNodeReferences *refs = nfo2p(fields, nfo); @@ -335,7 +335,7 @@ static void write_ref( struct SelvaNode * restrict edge, struct SelvaNodeLargeReference **ref_out) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo; struct SelvaNodeLargeReference ref = { .dst = dst->node_id, @@ -367,7 +367,7 @@ static void write_refs( struct SelvaNode * restrict edge, struct SelvaNodeReferenceAny *ref_out) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; void *vp = nfo2p(fields, &fields->fields_map[fs->field]); struct SelvaNodeReferences refs; @@ -804,7 +804,7 @@ static size_t remove_references_tail( const struct SelvaFieldSchema *fs, size_t limit) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs; size_t removed = 0; @@ -828,7 +828,7 @@ static size_t remove_references_tail( static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, bool ignore_src_dependent) { struct SelvaTypeEntry *te = selva_get_type_by_index(db, node->type); - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; assert(fs->field < fields->nr_fields); struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs; @@ -905,7 +905,7 @@ static void remove_references(struct SelvaDb *db, struct SelvaNode *node, const __attribute__((nonnull(1, 2))) static void unload_references(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs; @@ -932,7 +932,7 @@ static void unload_references(struct SelvaNode *node, const struct SelvaFieldSch static inline int _selva_fields_get_mutable_string(struct SelvaNode *node, const struct SelvaFieldSchema *fs, bool unsafe, size_t len, struct selva_string **s) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo; if (fs->type != SELVA_FIELD_TYPE_STRING) { @@ -965,7 +965,7 @@ struct selva_string *selva_fields_ensure_string(struct SelvaNode *node, const st return nullptr; } - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo = ensure_field(fields, fs); return get_mutable_string(fields, fs, nfo, initial_len, false); @@ -1130,7 +1130,7 @@ int selva_fields_get_text( const char **str, size_t *len) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; const struct SelvaFieldInfo *nfo; const struct SelvaTextField *text; struct selva_string *s; @@ -1166,7 +1166,7 @@ int selva_fields_get_text( void *selva_fields_ensure_micro_buffer(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo; if (fs->type != SELVA_FIELD_TYPE_MICRO_BUFFER) { @@ -1318,7 +1318,7 @@ int selva_fields_references_insert( return 0; } else if (reorder) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; assert(fs->field < fields->nr_fields); struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs = nfo2p(fields, nfo); @@ -1351,7 +1351,7 @@ int selva_fields_references_insert( return err; } else { if (ref_out) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; assert(fs->field < fields->nr_fields); struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs = nfo2p(fields, nfo); @@ -1425,7 +1425,7 @@ int selva_fields_reference_set( size_t selva_fields_prealloc_refs(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, size_t nr_refs_min) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; if (unlikely(fs->type != SELVA_FIELD_TYPE_REFERENCES)) { db_panic("Invalid type: %s", selva_str_field_type(fs->type)); @@ -1503,7 +1503,7 @@ static void selva_fields_references_insert_tail_nonempty_src_field( size_t nr_ids, selva_fields_references_insert_tail_cb_t fn) { - const struct SelvaFields *fields = &src->fields; + auto fields = &src->fields; assert(fs_src->field < fields->nr_fields); const struct SelvaFieldInfo *nfo = &fields->fields_map[fs_src->field]; typeof_field(struct SelvaNodeReferences, nr_refs) *index_len = (typeof(index_len))((char *)nfo2p(fields, nfo) + offsetof(struct SelvaNodeReferences, nr_refs)); @@ -1837,7 +1837,7 @@ static struct SelvaNode *next_ref_edge_node(struct SelvaTypeEntry *edge_type) struct SelvaNodeLargeReference *selva_fields_get_reference(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; assert(fs->field < fields->nr_fields); const struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; @@ -1848,7 +1848,7 @@ struct SelvaNodeLargeReference *selva_fields_get_reference(struct SelvaNode *nod struct SelvaNodeReferences *selva_fields_get_references(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; assert(fs->field < fields->nr_fields); const struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; @@ -1859,7 +1859,7 @@ struct SelvaNodeReferences *selva_fields_get_references(struct SelvaNode *node, struct selva_string *selva_fields_get_selva_string(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; const struct SelvaFieldInfo *nfo; assert(fs->type == SELVA_FIELD_TYPE_STRING); @@ -1878,7 +1878,7 @@ struct selva_string *selva_fields_get_selva_string(struct SelvaNode *node, const struct SelvaFieldsPointer selva_fields_get_raw(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; const struct SelvaFieldInfo *nfo; enum SelvaFieldType type; @@ -1945,7 +1945,7 @@ static void del_field_string(struct SelvaFields *fields, struct SelvaFieldInfo * static int fields_del(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, bool unload) { - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; struct SelvaFieldInfo *nfo; enum SelvaFieldType type; @@ -2102,7 +2102,7 @@ void selva_fields_init_node(struct SelvaTypeEntry *te, struct SelvaNode *node, b void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) { const struct SelvaNodeSchema *ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { @@ -2136,7 +2136,7 @@ void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) static inline void fields_destroy(struct SelvaDb *db, struct SelvaNode *node, bool unload) { const struct SelvaNodeSchema *ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); - struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { @@ -2212,7 +2212,7 @@ static inline void hash_ref(selva_hash_state_t *hash_state, const struct SelvaNo void selva_fields_hash_update(selva_hash_state_t *hash_state, struct SelvaDb *, const struct SelvaFieldsSchema *schema, const struct SelvaNode *node) { - const struct SelvaFields *fields = &node->fields; + auto fields = &node->fields; const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { From 321ec7cbea6d2ab3189319289fc5e89acec07c0d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 16:18:34 +0100 Subject: [PATCH 317/449] No O_SEARCH on Linux --- clibs/lib/selva/db.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 899cf1ae26..475f0b94c8 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -306,7 +306,11 @@ int selva_db_chdir(struct SelvaDb *db, const char *pathname_str, size_t pathname close(db->dirfd); } - fd = open(buf, O_SEARCH | O_DIRECTORY | O_CLOEXEC); + fd = open(buf, +#ifdef __MACH__ + O_SEARCH | +#endif + O_DIRECTORY | O_CLOEXEC); if (fd == -1) { return SELVA_EIO; } From e8608cbb5a810e0b33e7ccf381c0a624feae3ec5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 16:19:11 +0100 Subject: [PATCH 318/449] More comments about counted_by --- clibs/include/cdefs.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clibs/include/cdefs.h b/clibs/include/cdefs.h index caf7141522..cb3890598c 100644 --- a/clibs/include/cdefs.h +++ b/clibs/include/cdefs.h @@ -204,7 +204,8 @@ * unsigned int len; * char buf[] __counted_by(len); * }; - * __builtin_dynamic_object_size(p->buf) == p->len * sizeof(*p->buf) + * *__builtin_counted_by_ref(p->puf) = len; + * __builtin_dynamic_object_size(p->buf) == p->len * sizeof(*p->buf); */ #define __counted_by(member) __attribute__((__counted_by__(member))) #else @@ -213,13 +214,16 @@ #endif #ifndef __pcounted_by -#if __has_attribute(__counted_by__) && !defined(__clang__) +#if __has_attribute(__counted_by__) && \ + !defined(__clang__) && \ + (__GNUC__ >= 14 && __GNUC_MINOR__ > 2) /** * struct foo { * unsigned int len; * char *buf __pcounted_by(len); * }; - * __builtin_dynamic_object_size(p->buf) == p->len * sizeof(*p->buf) + * *__builtin_counted_by_ref(p->puf) = len; + * __builtin_dynamic_object_size(p->buf) == p->len * sizeof(*p->buf); */ #define __pcounted_by(member) __attribute__((__counted_by__(member))) #else From d4df355c55af5985ef905cb7994789096811548d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 16:19:29 +0100 Subject: [PATCH 319/449] The compiler may not have reproducible attr --- clibs/include/selva/db.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index d9ac3378a0..5a439be96d 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -91,7 +91,9 @@ SELVA_EXPORT int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block_i, char *errlog_buf, size_t errlog_size) __attribute__((nonnull)); SELVA_EXPORT +#if __has_c_attribute(reproducible) [[reproducible]] +#endif inline node_type_t selva_get_max_type(const struct SelvaDb *db) #ifndef __zig { @@ -106,7 +108,9 @@ inline node_type_t selva_get_max_type(const struct SelvaDb *db) * Find a type by type id. */ SELVA_EXPORT +#if __has_c_attribute(reproducible) [[reproducible]] +#endif inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) #ifndef __zig { @@ -124,7 +128,9 @@ inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_t * Get the type for node. */ SELVA_EXPORT +#if __has_c_attribute(reproducible) [[reproducible]] +#endif inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) #ifndef __zig { @@ -136,7 +142,9 @@ inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct #endif SELVA_EXPORT +#if __has_c_attribute(reproducible) [[reproducible]] +#endif inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) #ifndef __zig { From 1456cf2d03b7a5a09321de62dacb8ce90b7d96ad Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 16:19:48 +0100 Subject: [PATCH 320/449] Bump to gcc 15.2 --- podman/Containerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/podman/Containerfile b/podman/Containerfile index 64ad3b143c..fdd0523a1b 100644 --- a/podman/Containerfile +++ b/podman/Containerfile @@ -1,4 +1,4 @@ -FROM gcc:14.2 +FROM gcc:15.2 ENV NODE_VERSION 24.11.1 ENV NVM_DIR /usr/local/nvm RUN apt-get update && \ From 2cc73bbdb5c77e85d8156d054ef3a740e57484f1 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 16:53:38 +0100 Subject: [PATCH 321/449] Wrong type --- clibs/lib/selva/fields.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index a8a17158a9..f6d31d4b37 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -66,7 +66,7 @@ size_t selva_fields_get_data_size(const struct SelvaFieldSchema *fs) static struct SelvaFieldInfo alloc_block(struct SelvaFields *fields, const struct SelvaFieldSchema *fs) { - auto *data = (char *)fields->data; + auto data = (char *)fields->data; const size_t off = fields->data_len; const size_t field_data_size = selva_fields_get_data_size(fs); const size_t new_size = ALIGNED_SIZE(off + field_data_size, SELVA_FIELDS_DATA_ALIGN); @@ -95,7 +95,7 @@ static struct SelvaFieldInfo alloc_block(struct SelvaFields *fields, const struc #endif static inline void *nfo2p(const struct SelvaFields *fields, const struct SelvaFieldInfo *nfo) { - auto *data = (char *)fields->data; + auto data = (char *)fields->data; void *p = data + (nfo->off << SELVA_FIELDS_OFF); if (unlikely((char *)p > data + fields->data_len)) { From 4807e6c3f878cf1f85ec80ad8e533093947da141 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 17:11:28 +0100 Subject: [PATCH 322/449] fix ver check --- clibs/include/cdefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clibs/include/cdefs.h b/clibs/include/cdefs.h index cb3890598c..69edeb4348 100644 --- a/clibs/include/cdefs.h +++ b/clibs/include/cdefs.h @@ -216,7 +216,7 @@ #ifndef __pcounted_by #if __has_attribute(__counted_by__) && \ !defined(__clang__) && \ - (__GNUC__ >= 14 && __GNUC_MINOR__ > 2) + (__GNUC__ > 15 || (__GNUC__ == 14 && __GNUC_MINOR__ > 2)) /** * struct foo { * unsigned int len; From c0f90b1dccbd069b6723388538d84dc3c578c543 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 17:11:40 +0100 Subject: [PATCH 323/449] Fix db inline functions on gcc `inline` + `extern` functions on gcc needs separate declaration and definition if (some) attributes are used, where as clang allows them combined. The main culprit is how `SELVA_EXPORT` works. --- clibs/include/selva/db.h | 439 ++++++++++++++++++--------------------- 1 file changed, 201 insertions(+), 238 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 5a439be96d..79ac63bdcd 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -91,88 +91,40 @@ SELVA_EXPORT int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block_i, char *errlog_buf, size_t errlog_size) __attribute__((nonnull)); SELVA_EXPORT -#if __has_c_attribute(reproducible) +#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) [[reproducible]] #endif -inline node_type_t selva_get_max_type(const struct SelvaDb *db) -#ifndef __zig -{ - assert(db->types[db->nr_types - 1].type == db->nr_types); - return db->nr_types; -} -#else -; -#endif +inline node_type_t selva_get_max_type(const struct SelvaDb *db); /** * Find a type by type id. */ SELVA_EXPORT -#if __has_c_attribute(reproducible) +#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) [[reproducible]] #endif -inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) -#ifndef __zig -{ - if (type == 0) { - return nullptr; - } - assert(type - 1 < db->nr_types); - return &db->types[type - 1]; -} -#else -; -#endif +inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type); /** * Get the type for node. */ SELVA_EXPORT -#if __has_c_attribute(reproducible) +#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) [[reproducible]] #endif -inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) -#ifndef __zig -{ - assert(node->type - 1 < db->nr_types); - return &db->types[node->type - 1]; -} -#else -; -#endif +inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node); SELVA_EXPORT -#if __has_c_attribute(reproducible) +#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) [[reproducible]] #endif -inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) -#ifndef __zig -{ - return te->type; -} -#else -; -#endif +inline node_type_t selva_get_type(const struct SelvaTypeEntry *te); SELVA_EXPORT -inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te) -#ifndef __zig -{ - return te->blocks->len; -} -#else -; -#endif +inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te); SELVA_EXPORT -inline block_id_t selva_get_block_capacity(const struct SelvaTypeEntry *te) -#ifndef __zig -{ - return te->blocks->block_capacity; -} -#else -; -#endif +inline block_id_t selva_get_block_capacity(const struct SelvaTypeEntry *te); inline block_id_t selva_node_id2block_i3(block_id_t block_capacity, node_id_t node_id) { @@ -181,74 +133,22 @@ inline block_id_t selva_node_id2block_i3(block_id_t block_capacity, node_id_t no } SELVA_EXPORT -inline block_id_t selva_node_id2block_i(const struct SelvaTypeBlocks *blocks, node_id_t node_id) -#ifndef __zig -{ - return selva_node_id2block_i3(blocks->block_capacity, node_id); -} -#else -; -#endif +inline block_id_t selva_node_id2block_i(const struct SelvaTypeBlocks *blocks, node_id_t node_id); SELVA_EXPORT -inline block_id_t selva_node_id2block_i2(const struct SelvaTypeEntry *te, node_id_t node_id) -#ifndef __zig -{ - return selva_node_id2block_i(te->blocks, node_id); -} -#else -; -#endif +inline block_id_t selva_node_id2block_i2(const struct SelvaTypeEntry *te, node_id_t node_id); SELVA_EXPORT -inline node_id_t selva_block_i2start(const struct SelvaTypeEntry *te, block_id_t block_i) -#ifndef __zig -{ - block_id_t block_capacity = te->blocks->block_capacity; - node_id_t start = block_i * block_capacity + 1; - return start; -} -#else -; -#endif +inline node_id_t selva_block_i2start(const struct SelvaTypeEntry *te, block_id_t block_i); SELVA_EXPORT -inline node_id_t selva_block_i2end(const struct SelvaTypeEntry *te, block_id_t block_i) -#ifndef __zig -{ - block_id_t block_capacity = te->blocks->block_capacity; - node_id_t start = block_i * block_capacity + 1; - node_id_t end = start + block_capacity - 1; - return end; -} -#else -; -#endif +inline node_id_t selva_block_i2end(const struct SelvaTypeEntry *te, block_id_t block_i); SELVA_EXPORT -inline void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or_mask, void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx) -#ifndef __zig -{ - for (size_t ti = 0; ti < db->nr_types; ti++) { - struct SelvaTypeEntry *te = &db->types[ti]; - struct SelvaTypeBlocks *blocks = te->blocks; - - for (block_id_t block_i = 0; block_i < blocks->len; block_i++) { - struct SelvaTypeBlock *block = &blocks->blocks[block_i]; - - /* - * Note that we call it or_mask because the cb() is called if any - * bit of the mask is set in the status. - */ - if (atomic_load_explicit(&block->status.atomic, memory_order_consume) & or_mask) { - cb(ctx, db, te, block_i, selva_block_i2start(te, block_i)); - } - } - } -} -#else -; -#endif +inline void selva_foreach_block( + struct SelvaDb *db, + enum SelvaTypeBlockStatus or_mask, + void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx); /** * \addtogroup block_status @@ -256,62 +156,27 @@ inline void selva_foreach_block(struct SelvaDb *db, enum SelvaTypeBlockStatus or */ SELVA_EXPORT -inline enum SelvaTypeBlockStatus selva_block_status_get(const struct SelvaTypeEntry *te, block_id_t block_i) -#ifndef __zig -{ - return atomic_load(&te->blocks->blocks[block_i].status.atomic); -} -#else -; -#endif +inline enum SelvaTypeBlockStatus selva_block_status_get(const struct SelvaTypeEntry *te, block_id_t block_i); SELVA_EXPORT -inline void selva_block_status_replace(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus status) -#ifndef __zig -{ - atomic_store_explicit(&te->blocks->blocks[block_i].status.atomic, (uint32_t)status, memory_order_seq_cst); -} -#else -; -#endif +inline void selva_block_status_replace(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus status); /** * OR mask to the status. * @returns the previous status. */ SELVA_EXPORT -inline enum SelvaTypeBlockStatus selva_block_status_set(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask) -#ifndef __zig -{ - return atomic_fetch_or_explicit(&te->blocks->blocks[block_i].status.atomic, (uint32_t)mask, memory_order_seq_cst); -} -#else -; -#endif +inline enum SelvaTypeBlockStatus selva_block_status_set(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask); /** * Reset mask flags from the status. * @returns the previous status. */ SELVA_EXPORT -inline enum SelvaTypeBlockStatus selva_block_status_reset(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask) -#ifndef __zig -{ - return atomic_fetch_and_explicit(&te->blocks->blocks[block_i].status.atomic, ~(uint32_t)mask, memory_order_seq_cst); -} -#else -; -#endif +inline enum SelvaTypeBlockStatus selva_block_status_reset(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask); SELVA_EXPORT -inline bool selva_block_status_eq(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask) -#ifndef __zig -{ - return (atomic_load(&te->blocks->blocks[block_i].status.atomic) & (uint32_t)mask) == (uint32_t)mask; -} -#else -; -#endif +inline bool selva_block_status_eq(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask); SELVA_EXPORT size_t selva_get_type_status(const struct SelvaTypeEntry *te, size_t len, uint8_t packed_statuses[len]); @@ -325,90 +190,37 @@ size_t selva_get_type_status(const struct SelvaTypeEntry *te, size_t len, uint8_ */ SELVA_EXPORT __attribute__((nonnull, pure)) -inline const struct SelvaNodeSchema *selva_get_ns_by_te(const struct SelvaTypeEntry *te) -#ifndef __zig -{ - return &te->ns; -} -#else -; -#endif +inline const struct SelvaNodeSchema *selva_get_ns_by_te(const struct SelvaTypeEntry *te); SELVA_EXPORT -inline const struct SelvaFieldSchema *get_fs_by_fields_schema_field(const struct SelvaFieldsSchema *fields_schema, field_t field) -#ifndef __zig -{ - if (!fields_schema || field >= fields_schema->nr_fields) { - return nullptr; - } - - return &fields_schema->field_schemas[field]; -} -#else -; -#endif +inline const struct SelvaFieldSchema *get_fs_by_fields_schema_field(const struct SelvaFieldsSchema *fields_schema, field_t field); /** * Get the field schema for field. */ SELVA_EXPORT __attribute__((nonnull, pure)) -inline const struct SelvaFieldSchema *selva_get_fs_by_te_field(const struct SelvaTypeEntry *te, field_t field) -#ifndef __zig -{ - return get_fs_by_fields_schema_field(&te->ns.fields_schema, field); -} -#else -; -#endif +inline const struct SelvaFieldSchema *selva_get_fs_by_te_field(const struct SelvaTypeEntry *te, field_t field); /** * Get the field schema for field. */ SELVA_EXPORT __attribute__((nonnull, pure)) -inline const struct SelvaFieldSchema *selva_get_fs_by_ns_field(const struct SelvaNodeSchema *ns, field_t field) -#ifndef __zig -{ - return get_fs_by_fields_schema_field(&ns->fields_schema, field); -} -#else -; -#endif +inline const struct SelvaFieldSchema *selva_get_fs_by_ns_field(const struct SelvaNodeSchema *ns, field_t field); /** * Get the field schema for field. */ SELVA_EXPORT __attribute__((nonnull, pure)) -inline const struct SelvaFieldSchema *selva_get_fs_by_node(struct SelvaDb *db, struct SelvaNode *node, field_t field) -#ifndef __zig -{ - struct SelvaTypeEntry *type; - - type = selva_get_type_by_node(db, node); - if (!type) { - return nullptr; - } - - return selva_get_fs_by_ns_field(&type->ns, field); -} -#else -; -#endif +inline const struct SelvaFieldSchema *selva_get_fs_by_node(struct SelvaDb *db, struct SelvaNode *node, field_t field); SELVA_EXPORT -#if __has_c_attribute(reproducible) +#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) [[reproducible]] #endif -inline enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs) -#ifndef __zig -{ - return fs->type; -} -#else -; -#endif +inline enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs); /** * Get the EdgeFieldConstraint from a ref field schema. @@ -419,28 +231,10 @@ inline enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs) SELVA_EXPORT __attribute__((returns_nonnull)) __attribute__((nonnull)) -inline const struct EdgeFieldConstraint *selva_get_edge_field_constraint(const struct SelvaFieldSchema *fs) -#ifndef __zig -{ - assert(fs->type == SELVA_FIELD_TYPE_REFERENCE || - fs->type == SELVA_FIELD_TYPE_REFERENCES); - return &fs->edge_constraint; -} -#else -; -#endif +inline const struct EdgeFieldConstraint *selva_get_edge_field_constraint(const struct SelvaFieldSchema *fs); SELVA_EXPORT -inline const struct SelvaFieldsSchema *selva_get_edge_field_fields_schema(struct SelvaDb *db, const struct EdgeFieldConstraint *efc) -#ifndef __zig -{ - struct SelvaTypeEntry *te = selva_get_type_by_index(db, efc->edge_node_type); - - return (te) ? &selva_get_ns_by_te(te)->fields_schema : nullptr; -} -#else -; -#endif +inline const struct SelvaFieldsSchema *selva_get_edge_field_fields_schema(struct SelvaDb *db, const struct EdgeFieldConstraint *efc); /** * Strategy for adding new node expires. @@ -641,3 +435,172 @@ struct SelvaAliases *selva_get_aliases(struct SelvaTypeEntry *type, field_t fiel */ SELVA_EXPORT void selva_remove_all_aliases(struct SelvaTypeEntry *type, node_id_t node_id); + +/* + * Inline functions that can be inlined only in C. + */ +#ifndef __zig +inline node_type_t selva_get_max_type(const struct SelvaDb *db) +{ + assert(db->types[db->nr_types - 1].type == db->nr_types); + return db->nr_types; +} + +inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) +{ + if (type == 0) { + return nullptr; + } + assert((size_t)type - 1 < db->nr_types); + return &db->types[type - 1]; +} + +inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) +{ + assert((size_t)node->type - 1 < db->nr_types); + return &db->types[node->type - 1]; +} + +inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) +{ + return te->type; +} + +inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te) +{ + return te->blocks->len; +} + +inline block_id_t selva_get_block_capacity(const struct SelvaTypeEntry *te) +{ + return te->blocks->block_capacity; +} + +inline block_id_t selva_node_id2block_i(const struct SelvaTypeBlocks *blocks, node_id_t node_id) +{ + return selva_node_id2block_i3(blocks->block_capacity, node_id); +} + +inline block_id_t selva_node_id2block_i2(const struct SelvaTypeEntry *te, node_id_t node_id) +{ + return selva_node_id2block_i(te->blocks, node_id); +} + +inline node_id_t selva_block_i2start(const struct SelvaTypeEntry *te, block_id_t block_i) +{ + block_id_t block_capacity = te->blocks->block_capacity; + node_id_t start = block_i * block_capacity + 1; + return start; +} + +inline node_id_t selva_block_i2end(const struct SelvaTypeEntry *te, block_id_t block_i) +{ + block_id_t block_capacity = te->blocks->block_capacity; + node_id_t start = block_i * block_capacity + 1; + node_id_t end = start + block_capacity - 1; + return end; +} + +inline void selva_foreach_block( + struct SelvaDb *db, + enum SelvaTypeBlockStatus or_mask, + void (*cb)(void *ctx, struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block, node_id_t start), void *ctx) +{ + for (size_t ti = 0; ti < db->nr_types; ti++) { + struct SelvaTypeEntry *te = &db->types[ti]; + struct SelvaTypeBlocks *blocks = te->blocks; + + for (block_id_t block_i = 0; block_i < blocks->len; block_i++) { + struct SelvaTypeBlock *block = &blocks->blocks[block_i]; + + /* + * Note that we call it or_mask because the cb() is called if any + * bit of the mask is set in the status. + */ + if (atomic_load_explicit(&block->status.atomic, memory_order_consume) & or_mask) { + cb(ctx, db, te, block_i, selva_block_i2start(te, block_i)); + } + } + } +} + +inline enum SelvaTypeBlockStatus selva_block_status_get(const struct SelvaTypeEntry *te, block_id_t block_i) +{ + return atomic_load(&te->blocks->blocks[block_i].status.atomic); +} + +inline void selva_block_status_replace(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus status) +{ + atomic_store_explicit(&te->blocks->blocks[block_i].status.atomic, (uint32_t)status, memory_order_seq_cst); +} + +inline enum SelvaTypeBlockStatus selva_block_status_set(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask) +{ + return atomic_fetch_or_explicit(&te->blocks->blocks[block_i].status.atomic, (uint32_t)mask, memory_order_seq_cst); +} + +inline enum SelvaTypeBlockStatus selva_block_status_reset(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask) +{ + return atomic_fetch_and_explicit(&te->blocks->blocks[block_i].status.atomic, ~(uint32_t)mask, memory_order_seq_cst); +} + +inline bool selva_block_status_eq(const struct SelvaTypeEntry *te, block_id_t block_i, enum SelvaTypeBlockStatus mask) +{ + return (atomic_load(&te->blocks->blocks[block_i].status.atomic) & (uint32_t)mask) == (uint32_t)mask; +} + +inline const struct SelvaNodeSchema *selva_get_ns_by_te(const struct SelvaTypeEntry *te) +{ + return &te->ns; +} + +inline const struct SelvaFieldSchema *get_fs_by_fields_schema_field(const struct SelvaFieldsSchema *fields_schema, field_t field) +{ + if (!fields_schema || field >= fields_schema->nr_fields) { + return nullptr; + } + + return &fields_schema->field_schemas[field]; +} + +inline const struct SelvaFieldSchema *selva_get_fs_by_te_field(const struct SelvaTypeEntry *te, field_t field) +{ + return get_fs_by_fields_schema_field(&te->ns.fields_schema, field); +} + +inline const struct SelvaFieldSchema *selva_get_fs_by_ns_field(const struct SelvaNodeSchema *ns, field_t field) +{ + return get_fs_by_fields_schema_field(&ns->fields_schema, field); +} + +inline const struct SelvaFieldSchema *selva_get_fs_by_node(struct SelvaDb *db, struct SelvaNode *node, field_t field) +{ + struct SelvaTypeEntry *type; + + type = selva_get_type_by_node(db, node); + if (!type) { + return nullptr; + } + + return selva_get_fs_by_ns_field(&type->ns, field); +} + +inline enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs) +{ + return fs->type; +} + +inline const struct EdgeFieldConstraint *selva_get_edge_field_constraint(const struct SelvaFieldSchema *fs) +{ + assert(fs->type == SELVA_FIELD_TYPE_REFERENCE || + fs->type == SELVA_FIELD_TYPE_REFERENCES); + return &fs->edge_constraint; +} + +inline const struct SelvaFieldsSchema *selva_get_edge_field_fields_schema(struct SelvaDb *db, const struct EdgeFieldConstraint *efc) +{ + struct SelvaTypeEntry *te = selva_get_type_by_index(db, efc->edge_node_type); + + return (te) ? &selva_get_ns_by_te(te)->fields_schema : nullptr; +} +#endif From 29b6676da242ec3c513bbbd63f0e2a7bca86fe54 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 17:14:33 +0100 Subject: [PATCH 324/449] No O_RESOLVE_BENEATH on Linux --- clibs/lib/selva/io/io.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/clibs/lib/selva/io/io.c b/clibs/lib/selva/io/io.c index 9cee36fdbe..9441106093 100644 --- a/clibs/lib/selva/io/io.c +++ b/clibs/lib/selva/io/io.c @@ -92,9 +92,18 @@ int selva_io_init_file(struct selva_io *io, int dirfd, const char *filename, enu } if (flags & SELVA_IO_FLAGS_WRITE) { - fd = openat(dirfd, filename, O_WRONLY | O_CREAT | O_TRUNC | O_RESOLVE_BENEATH | O_CLOEXEC, 0640); + /* TODO Use Linux openat2 and RESOLVE_BENEATH if supported */ + fd = openat(dirfd, filename, O_WRONLY | O_CREAT | O_TRUNC | +#if __MACH__ + O_RESOLVE_BENEATH | +#endif + O_CLOEXEC, 0640); } else { - fd = openat(dirfd, filename, O_RDONLY | O_RESOLVE_BENEATH | O_CLOEXEC); + fd = openat(dirfd, filename, O_RDONLY | +#if __MACH__ + O_RESOLVE_BENEATH | +#endif + O_CLOEXEC); } if (fd == -1) { goto fopen_err; From e6d9ae867d8ba2159ffa14ee12fd1e05fe1d42e7 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 17:14:48 +0100 Subject: [PATCH 325/449] Use auto to infer types in C --- clibs/lib/selva/fields.c | 96 +++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index f6d31d4b37..e053cd462a 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -261,7 +261,7 @@ static void print_refs(struct SelvaNode *node, const struct SelvaFieldSchema *fs static field_t refs_get_nr_fields(struct SelvaDb *db, const struct EdgeFieldConstraint *efc) { - const struct SelvaFieldsSchema *efc_fields_schema = selva_get_edge_field_fields_schema(db, efc); + auto efc_fields_schema = selva_get_edge_field_fields_schema(db, efc); const field_t nr_fields = efc_fields_schema ? efc_fields_schema->nr_fields - efc_fields_schema->nr_virtual_fields : 0; return nr_fields; @@ -289,8 +289,7 @@ static const struct SelvaFieldSchema *get_edge_dst_fs( const struct SelvaDb *db, const struct SelvaFieldSchema *fs_src) { - const struct EdgeFieldConstraint *efc = &fs_src->edge_constraint; - struct SelvaTypeEntry *type_dst; + const auto efc = &fs_src->edge_constraint; if (fs_src->type != SELVA_FIELD_TYPE_REFERENCE && fs_src->type != SELVA_FIELD_TYPE_REFERENCES) { @@ -298,7 +297,7 @@ static const struct SelvaFieldSchema *get_edge_dst_fs( } /* TODO This could be also handled with a Generic */ - type_dst = selva_get_type_by_index((typeof_unqual(*db) *)db, efc->dst_node_type); + auto type_dst = selva_get_type_by_index((typeof_unqual(*db) *)db, efc->dst_node_type); assert(type_dst->type == efc->dst_node_type); return selva_get_fs_by_te_field(type_dst, efc->inverse_field); @@ -658,29 +657,27 @@ static void clear_ref_dst(struct SelvaDb *db, const struct SelvaFieldSchema *fs_ return; } - struct SelvaTypeEntry *dst_type = selva_get_type_by_index(db, fs_src->edge_constraint.dst_node_type); + auto dst_type = selva_get_type_by_index(db, fs_src->edge_constraint.dst_node_type); assert(dst_type); /* TODO Partials */ - struct SelvaNode *dst = selva_find_node(dst_type, dst_node_id).node; + auto dst = selva_find_node(dst_type, dst_node_id).node; if (!dst) { return; } - const struct SelvaFieldSchema *fs_dst; - fs_dst = get_edge_dst_fs(db, fs_src); + auto fs_dst = get_edge_dst_fs(db, fs_src); if (!fs_dst) { db_panic("field schema not found"); } - struct SelvaFields *fields_dst = &dst->fields; - struct SelvaFieldInfo *nfo_dst; + auto fields_dst = &dst->fields; assert(fs_dst->field < fields_dst->nr_fields); assume(fs_src->type == SELVA_FIELD_TYPE_REFERENCE || fs_src->type == SELVA_FIELD_TYPE_REFERENCES); assume(fs_dst->type == SELVA_FIELD_TYPE_REFERENCE || fs_dst->type == SELVA_FIELD_TYPE_REFERENCES); - nfo_dst = &fields_dst->fields_map[fs_dst->field]; + auto nfo_dst = &fields_dst->fields_map[fs_dst->field]; if (!nfo_dst->in_use) { return; } @@ -718,9 +715,7 @@ static inline void write_ref_2way( struct SelvaNode *edge = nullptr; if (refs_get_type(db, &fs_src->edge_constraint) == SELVA_NODE_REFERENCE_LARGE) { - struct SelvaTypeEntry *edge_type; - - edge_type = selva_get_type_by_index(db, fs_src->edge_constraint.edge_node_type); + auto edge_type = selva_get_type_by_index(db, fs_src->edge_constraint.edge_node_type); edge = next_ref_edge_node(edge_type); } @@ -760,9 +755,9 @@ static inline void write_ref_2way( */ static node_id_t remove_reference(struct SelvaDb *db, struct SelvaNode *src, const struct SelvaFieldSchema *fs_src, node_id_t orig_dst, ssize_t idx, bool ignore_src_dependent) { - struct SelvaFields *fields_src = &src->fields; - struct SelvaFieldInfo *nfo_src = &fields_src->fields_map[fs_src->field]; - struct SelvaTypeEntry *dst_type = selva_get_type_by_index(db, fs_src->edge_constraint.dst_node_type); + auto fields_src = &src->fields; + auto nfo_src = &fields_src->fields_map[fs_src->field]; + auto dst_type = selva_get_type_by_index(db, fs_src->edge_constraint.dst_node_type); node_id_t dst_node_id = 0; assert(dst_type); @@ -805,7 +800,7 @@ static size_t remove_references_tail( size_t limit) { auto fields = &node->fields; - struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; + auto nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs; size_t removed = 0; @@ -827,10 +822,10 @@ static size_t remove_references_tail( static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs, bool ignore_src_dependent) { - struct SelvaTypeEntry *te = selva_get_type_by_index(db, node->type); + auto te = selva_get_type_by_index(db, node->type); auto fields = &node->fields; assert(fs->field < fields->nr_fields); - struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; + auto nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs; if (!nfo->in_use) { @@ -844,7 +839,7 @@ static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct S selva_mark_dirty(te, node->node_id); - struct SelvaTypeEntry *dst_type = selva_get_type_by_index(db, fs->edge_constraint.dst_node_type); + auto dst_type = selva_get_type_by_index(db, fs->edge_constraint.dst_node_type); assert(dst_type); while (refs->nr_refs > 0) { @@ -881,7 +876,7 @@ static struct SelvaNodeReferences *clear_references(struct SelvaDb *db, struct S __attribute__((nonnull(1, 2, 3))) static void remove_references(struct SelvaDb *db, struct SelvaNode *node, const struct SelvaFieldSchema *fs) { - struct SelvaNodeReferences *refs = clear_references(db, node, fs, false); + auto refs = clear_references(db, node, fs, false); if (refs) { switch (refs->size) { case SELVA_NODE_REFERENCE_SMALL: @@ -906,7 +901,7 @@ __attribute__((nonnull(1, 2))) static void unload_references(struct SelvaNode *node, const struct SelvaFieldSchema *fs) { auto fields = &node->fields; - struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; + auto nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs; if (!nfo->in_use) { @@ -966,7 +961,7 @@ struct selva_string *selva_fields_ensure_string(struct SelvaNode *node, const st } auto fields = &node->fields; - struct SelvaFieldInfo *nfo = ensure_field(fields, fs); + auto nfo = ensure_field(fields, fs); return get_mutable_string(fields, fs, nfo, initial_len, false); } @@ -1263,9 +1258,9 @@ static bool add_to_refs_index( const struct SelvaFieldSchema * restrict fs_src, const struct SelvaFieldSchema * restrict fs_dst) { - const enum SelvaNodeReferenceType type = refs_get_type(db, &fs_src->edge_constraint); - struct SelvaFieldInfo *nfo_src = ensure_field_references(&src->fields, fs_src, type); - struct SelvaFieldInfo *nfo_dst = ensure_field_references(&dst->fields, fs_dst, type); + const auto type = refs_get_type(db, &fs_src->edge_constraint); + auto nfo_src = ensure_field_references(&src->fields, fs_src, type); + auto nfo_dst = ensure_field_references(&dst->fields, fs_dst, type); const bool added_src = add_to_refs_index_(src, fs_src, nfo_src, dst->node_id); const bool added_dst = add_to_refs_index_(dst, fs_dst, nfo_dst, src->node_id); @@ -1320,7 +1315,7 @@ int selva_fields_references_insert( } else if (reorder) { auto fields = &node->fields; assert(fs->field < fields->nr_fields); - struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; + auto nfo = &fields->fields_map[fs->field]; struct SelvaNodeReferences *refs = nfo2p(fields, nfo); ssize_t index_old; int err = 0; @@ -1380,15 +1375,13 @@ int selva_fields_reference_set( struct SelvaNode * restrict dst, struct SelvaNodeReferenceAny *ref_out) { - const struct SelvaFieldSchema *fs_dst; - if (fs_src->type != SELVA_FIELD_TYPE_REFERENCE || fs_src->edge_constraint.dst_node_type != dst->type || !dst || src == dst) { return SELVA_EINVAL; } - fs_dst = get_edge_dst_fs(db, fs_src); + auto fs_dst = get_edge_dst_fs(db, fs_src); if (!fs_dst) { return SELVA_EINTYPE; } @@ -1404,7 +1397,7 @@ int selva_fields_reference_set( /* * Remove previous refs. */ - struct SelvaTypeEntry *te_dst = selva_get_type_by_index(db, fs_src->edge_constraint.dst_node_type); + auto te_dst = selva_get_type_by_index(db, fs_src->edge_constraint.dst_node_type); (void)remove_reference(db, src, fs_src, 0, -1, true); selva_mark_dirty(te_dst, dst->node_id); @@ -1432,7 +1425,7 @@ size_t selva_fields_prealloc_refs(struct SelvaDb *db, struct SelvaNode *node, co } const enum SelvaNodeReferenceType type = refs_get_type(db, selva_get_edge_field_constraint(fs)); - struct SelvaFieldInfo *nfo = ensure_field_references(fields, fs, type); + auto nfo = ensure_field_references(fields, fs, type); struct SelvaNodeReferences *refs = nfo2p(fields, nfo); if (refs->nr_refs >= nr_refs_min) { @@ -1505,7 +1498,7 @@ static void selva_fields_references_insert_tail_nonempty_src_field( { auto fields = &src->fields; assert(fs_src->field < fields->nr_fields); - const struct SelvaFieldInfo *nfo = &fields->fields_map[fs_src->field]; + const auto nfo = &fields->fields_map[fs_src->field]; typeof_field(struct SelvaNodeReferences, nr_refs) *index_len = (typeof(index_len))((char *)nfo2p(fields, nfo) + offsetof(struct SelvaNodeReferences, nr_refs)); typeof_field(struct SelvaNodeReferences, index) *index = (typeof(index))((char *)nfo2p(fields, nfo) + offsetof(struct SelvaNodeReferences, index)); ssize_t index_lower_bound = node_id_set_bsearch(*index, *index_len, ids[0]); @@ -1579,7 +1572,6 @@ int selva_fields_references_insert_tail( const node_id_t ids[], size_t nr_ids) { - const struct SelvaFieldSchema *fs_dst; node_type_t type_dst = te_dst->type; if (fs->type != SELVA_FIELD_TYPE_REFERENCES || @@ -1600,7 +1592,7 @@ int selva_fields_references_insert_tail( } } - fs_dst = selva_get_fs_by_te_field(te_dst, fs->edge_constraint.inverse_field); + auto fs_dst = selva_get_fs_by_te_field(te_dst, fs->edge_constraint.inverse_field); if (!fs_dst) { return SELVA_EINTYPE; } @@ -1839,7 +1831,7 @@ struct SelvaNodeLargeReference *selva_fields_get_reference(struct SelvaNode *nod { auto fields = &node->fields; assert(fs->field < fields->nr_fields); - const struct SelvaFieldInfo *nfo = &fields->fields_map[fs->field]; + const auto nfo = &fields->fields_map[fs->field]; return (fs->type != SELVA_FIELD_TYPE_REFERENCE || !nfo->in_use) ? nullptr @@ -1903,7 +1895,7 @@ struct SelvaFieldsPointer selva_fields_get_raw(struct SelvaNode *node, const str }; case SELVA_FIELD_TYPE_STRING: do { - const struct selva_string *s = (const struct selva_string *)((uint8_t *)fields->data + (nfo->off << 3)); + auto s = (const struct selva_string *)((uint8_t *)fields->data + (nfo->off << 3)); size_t len; const uint8_t *str = selva_string_to_buf(s, &len); return (struct SelvaFieldsPointer){ @@ -2000,7 +1992,7 @@ int selva_fields_del_ref(struct SelvaDb *db, struct SelvaNode *node, const struc return SELVA_EINTYPE; } - struct SelvaNodeReferences *refs = selva_fields_get_references(node, fs); + auto refs = selva_fields_get_references(node, fs); if (!refs) { return SELVA_ENOENT; } @@ -2026,8 +2018,7 @@ static void selva_fields_init_defaults(struct SelvaTypeEntry *te, struct SelvaFi * Handle defaults that needs to allocate memory per each node. */ for (size_t i = 0; i < schema->nr_fixed_fields; i++) { - const struct SelvaFieldSchema *fs = get_fs_by_fields_schema_field(schema, i); - + auto fs = get_fs_by_fields_schema_field(schema, i); if (fs->type == SELVA_FIELD_TYPE_STRING) { if (fs->string.default_off > 0) { const void *default_str = schema_buf + fs->string.default_off; @@ -2070,7 +2061,7 @@ static void selva_fields_init_defaults(struct SelvaTypeEntry *te, struct SelvaFi static void selva_fields_init(struct SelvaTypeEntry *te, struct SelvaFields *fields, bool set_defaults) { - const struct SelvaFieldsSchema *schema = &te->ns.fields_schema; + const auto schema = &te->ns.fields_schema; fields->nr_fields = schema->nr_fields - schema->nr_virtual_fields; memcpy(fields->fields_map, schema->template.field_map_buf, schema->template.field_map_len); @@ -2101,16 +2092,15 @@ void selva_fields_init_node(struct SelvaTypeEntry *te, struct SelvaNode *node, b void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) { - const struct SelvaNodeSchema *ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); + auto ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); auto fields = &node->fields; const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { if (fields->fields_map[field].in_use) { - const struct SelvaFieldSchema *fs; int err; - fs = selva_get_fs_by_ns_field(ns, field); + auto fs = selva_get_fs_by_ns_field(ns, field); if (unlikely(!fs)) { db_panic("No field schema found"); } @@ -2135,16 +2125,15 @@ void selva_fields_flush(struct SelvaDb *db, struct SelvaNode *node) static inline void fields_destroy(struct SelvaDb *db, struct SelvaNode *node, bool unload) { - const struct SelvaNodeSchema *ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); + auto ns = selva_get_ns_by_te(selva_get_type_by_node(db, node)); auto fields = &node->fields; const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { if (fields->fields_map[field].in_use) { - const struct SelvaFieldSchema *fs; int err; - fs = selva_get_fs_by_ns_field(ns, field); + auto fs = selva_get_fs_by_ns_field(ns, field); if (unlikely(!fs)) { db_panic("No field schema found"); } @@ -2187,12 +2176,9 @@ static void reference_edge_destroy( bool keep_edge_node) { if (ref->edge != 0) { - struct SelvaTypeEntry *edge_type; - struct SelvaNode *edge_node; - - edge_type = selva_get_type_by_index(db, efc->edge_node_type); + auto edge_type = selva_get_type_by_index(db, efc->edge_node_type); assert(edge_type); - edge_node = selva_find_node(edge_type, ref->edge).node; /* TODO Partials */ + auto edge_node = selva_find_node(edge_type, ref->edge).node; /* TODO Partials */ ref->edge = 0; if (edge_node && !keep_edge_node) { @@ -2216,8 +2202,8 @@ void selva_fields_hash_update(selva_hash_state_t *hash_state, struct SelvaDb *, const field_t nr_fields = fields->nr_fields; for (field_t field = 0; field < nr_fields; field++) { - const struct SelvaFieldInfo *nfo = &fields->fields_map[field]; - const struct SelvaFieldSchema *fs = &schema->field_schemas[field]; + auto nfo = &fields->fields_map[field]; + const auto fs = &schema->field_schemas[field]; const void *p = nfo2p(fields, nfo); switch (fs->type) { From b5bcaaa946051180b8eaf59466dd3e5695b8dd08 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 17:49:03 +0100 Subject: [PATCH 326/449] Mute gcc bug with auto gcc's warin system thinks that `auto` is always used for storage-class but we use it mostly for C23-style type inferrence. --- clibs/common.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clibs/common.mk b/clibs/common.mk index 825a49a307..e6a72c64f1 100644 --- a/clibs/common.mk +++ b/clibs/common.mk @@ -50,6 +50,8 @@ endif ifeq ($(uname_S),Linux) CFLAGS += -g -ggdb3 -fno-math-errno -ftree-vectorize -Wstrict-aliasing=3 + # Mute the gcc bug when `auto` is used to infer the type + CFLAGS += -Wno-old-style-declaration #CFLAGS += -opt-info-vec-optimized #CFLAGS += -ftree-vectorizer-verbose=5 -fopt-info-vec-missed From e5176384d3caec203de857761b83d7926873c6aa Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 17:53:43 +0100 Subject: [PATCH 327/449] reproducible now works We don't need `__has_c_attribute` checks because this codebase won't compile with a compiler that doesn't support modern features. --- clibs/include/selva/db.h | 25 +++++-------------------- clibs/include/selva/fields.h | 12 ++---------- clibs/lib/selva/fields.c | 5 +---- 3 files changed, 8 insertions(+), 34 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 79ac63bdcd..8ded1d309e 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -91,34 +91,22 @@ SELVA_EXPORT int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block_i, char *errlog_buf, size_t errlog_size) __attribute__((nonnull)); SELVA_EXPORT -#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) -[[reproducible]] -#endif -inline node_type_t selva_get_max_type(const struct SelvaDb *db); +inline node_type_t selva_get_max_type(const struct SelvaDb *db) [[reproducible]]; /** * Find a type by type id. */ SELVA_EXPORT -#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) -[[reproducible]] -#endif -inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type); +inline struct SelvaTypeEntry *selva_get_type_by_index(struct SelvaDb *db, node_type_t type) [[reproducible]]; /** * Get the type for node. */ SELVA_EXPORT -#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) -[[reproducible]] -#endif -inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node); +inline struct SelvaTypeEntry *selva_get_type_by_node(struct SelvaDb *db, struct SelvaNode *node) [[reproducible]]; SELVA_EXPORT -#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) -[[reproducible]] -#endif -inline node_type_t selva_get_type(const struct SelvaTypeEntry *te); +inline node_type_t selva_get_type(const struct SelvaTypeEntry *te) [[reproducible]]; SELVA_EXPORT inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te); @@ -217,10 +205,7 @@ __attribute__((nonnull, pure)) inline const struct SelvaFieldSchema *selva_get_fs_by_node(struct SelvaDb *db, struct SelvaNode *node, field_t field); SELVA_EXPORT -#if __has_c_attribute(reproducible) && (defined(__clang__) || __GNUC__ > 15) -[[reproducible]] -#endif -inline enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs); +inline enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs) [[reproducible]]; /** * Get the EdgeFieldConstraint from a ref field schema. diff --git a/clibs/include/selva/fields.h b/clibs/include/selva/fields.h index 00f8379e02..0d181a6191 100644 --- a/clibs/include/selva/fields.h +++ b/clibs/include/selva/fields.h @@ -86,17 +86,9 @@ extern const uint8_t selva_fields_text_tl_empty[_selva_lang_last][8]; #define SELVA_FIELDS_TEXT_TL_EMPTY_LEN 6 -#if __has_c_attribute(unsequenced) -[[unsequenced]] -#else -__purefn -#endif -size_t selva_fields_get_data_size(const struct SelvaFieldSchema *fs); +size_t selva_fields_get_data_size(const struct SelvaFieldSchema *fs) [[unsequenced]]; -#if __has_c_attribute(reproducible) -[[reproducible]] -#endif -void *selva_fields_nfo2p(struct SelvaFields *fields, const struct SelvaFieldInfo *nfo); +void *selva_fields_nfo2p(struct SelvaFields *fields, const struct SelvaFieldInfo *nfo) [[reproducible]]; /** * Ensure that we have a reference struct. diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index e053cd462a..c1c3b91489 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -90,10 +90,7 @@ static struct SelvaFieldInfo alloc_block(struct SelvaFields *fields, const struc }; } -#if __has_c_attribute(reproducible) -[[reproducible]] -#endif -static inline void *nfo2p(const struct SelvaFields *fields, const struct SelvaFieldInfo *nfo) +static inline void *nfo2p(const struct SelvaFields *fields, const struct SelvaFieldInfo *nfo) [[reproducible]] { auto data = (char *)fields->data; void *p = data + (nfo->off << SELVA_FIELDS_OFF); From eb284389bc3ae2ff2d08b93c3bf6679529c497ca Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 18 Feb 2026 18:04:25 +0100 Subject: [PATCH 328/449] More nice type inferrence --- clibs/lib/selva/db.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 475f0b94c8..013b5f6402 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -507,8 +507,8 @@ extern inline const struct SelvaFieldsSchema *selva_get_edge_field_fields_schema static inline void del_node(struct SelvaDb *db, struct SelvaTypeEntry *type, struct SelvaNode *node, bool unload) { - struct SelvaTypeBlock *block = selva_get_block(type->blocks, node->node_id); - struct SelvaNodeIndex *nodes = &block->nodes; + auto block = selva_get_block(type->blocks, node->node_id); + auto nodes = &block->nodes; atomic_fetch_or_explicit(&block->status.atomic, (uint32_t)SELVA_TYPE_BLOCK_STATUS_DIRTY, memory_order_release); @@ -575,7 +575,7 @@ struct SelvaNodeRes selva_find_node(struct SelvaTypeEntry *type, node_id_t node_ return (struct SelvaNodeRes){}; } - struct SelvaTypeBlocks *blocks = type->blocks; + auto blocks = type->blocks; struct SelvaNodeRes res = { .block = selva_node_id2block_i(blocks, node_id), }; @@ -586,7 +586,7 @@ struct SelvaNodeRes selva_find_node(struct SelvaTypeEntry *type, node_id_t node_ goto out; } - struct SelvaNodeIndex *nodes = &block->nodes; + auto nodes = &block->nodes; struct SelvaNode find = { .node_id = node_id, }; @@ -598,7 +598,7 @@ struct SelvaNodeRes selva_find_node(struct SelvaTypeEntry *type, node_id_t node_ struct SelvaNodeRes selva_nfind_node(struct SelvaTypeEntry *type, node_id_t node_id) { - struct SelvaTypeBlocks *blocks = type->blocks; + auto blocks = type->blocks; struct SelvaNodeRes res = { .block = selva_node_id2block_i(blocks, node_id), }; @@ -607,7 +607,7 @@ struct SelvaNodeRes selva_nfind_node(struct SelvaTypeEntry *type, node_id_t node goto out; } - struct SelvaTypeBlock *block = &blocks->blocks[res.block]; + auto block = &blocks->blocks[res.block]; res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_acquire); if (!(res.block_status & SELVA_TYPE_BLOCK_STATUS_INMEM)) { goto out; @@ -660,9 +660,7 @@ struct SelvaNodeRes selva_upsert_node(struct SelvaTypeEntry *type, node_id_t nod */ RB_INSERT_NEXT(SelvaNodeIndex, &block->nodes, type->max_node, node); } else { - struct SelvaNode *prev; - - prev = RB_INSERT(SelvaNodeIndex, &block->nodes, node); + auto prev = RB_INSERT(SelvaNodeIndex, &block->nodes, node); if (prev) { mempool_return(&type->nodepool, node); res.node = prev; @@ -726,7 +724,7 @@ struct SelvaNodeRes selva_min_node(struct SelvaTypeEntry *type) */ static struct SelvaNodeRes selva_max_node_from(struct SelvaTypeEntry *type, block_id_t start) { - struct SelvaTypeBlocks *blocks = type->blocks; + auto blocks = type->blocks; struct SelvaNodeRes res = {}; for (ssize_t i = start; i >= 0; i--) { @@ -785,7 +783,7 @@ struct SelvaNodeRes selva_prev_node(struct SelvaTypeEntry *type, struct SelvaNod struct SelvaNodeRes selva_next_node(struct SelvaTypeEntry *type, struct SelvaNode *node) { - const struct SelvaTypeBlocks *blocks = type->blocks; + const auto blocks = type->blocks; struct SelvaNodeRes res = { .block = selva_node_id2block_i(blocks, node->node_id), .block_status = SELVA_TYPE_BLOCK_STATUS_INMEM, @@ -843,7 +841,7 @@ static void hash_col_fields(struct SelvaTypeEntry *type, node_id_t node_id, selv * colvec fields. */ for (size_t i = 0; i < type->ns.nr_colvec_fields; i++) { - struct SelvaColvec *colvec = &type->col_fields.colvec[i]; + auto colvec = &type->col_fields.colvec[i]; colvec_hash_update(type, node_id, colvec, tmp_hash_state); } @@ -894,7 +892,7 @@ void selva_node_block_hash2(struct SelvaDb *db, struct SelvaTypeEntry *type, str int selva_node_block_hash(struct SelvaDb *db, struct SelvaTypeEntry *type, node_id_t start, selva_hash128_t *hash_out) { - struct SelvaTypeBlock *block = selva_get_block(type->blocks, start); + auto block = selva_get_block(type->blocks, start); if (!block) { return SELVA_ENOENT; From 3850df8d58135c03adfd9bfbb1c9774acbaed05f Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 19:28:22 +0100 Subject: [PATCH 329/449] add iterator --- native/query/filter/filter.zig | 13 ++++++-- native/query/multiple.zig | 56 +++++++++++++++++++++++----------- native/types.zig | 25 +++++++++++++++ 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/native/query/filter/filter.zig b/native/query/filter/filter.zig index 858ad51110..1d8c03198e 100644 --- a/native/query/filter/filter.zig +++ b/native/query/filter/filter.zig @@ -103,12 +103,17 @@ inline fn compare( // MAKE EDGE FILTER // Check if this becomes better -pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { +pub inline fn filter( + node: Node.Node, + ctx: *Query.QueryCtx, + q: []u8, + // comptime hasEdge: bool, + // edge: if (hasEdge) Node.Node else void, +) !bool { var i: usize = 0; var pass: bool = true; var v: []const u8 = undefined; var prop: u8 = 255; - // var end: usize = q.len; var end: usize = q.len; while (i < end) { @@ -130,6 +135,9 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { end = utils.readPtr(u64, q, index + @alignOf(u64) - c.offset).*; break :blk true; }, + // .edge => blk: { + // break :blk true; + // }, .selectRef => blk: { const select = utils.readPtr(t.FilterSelect, q, index + @alignOf(t.FilterSelect) - c.offset); nextIndex += select.size; @@ -161,5 +169,6 @@ pub inline fn filter(node: Node.Node, ctx: *Query.QueryCtx, q: []u8) !bool { i = nextIndex; } } + return pass; } diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 843140a15e..5a5447d2f6 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -74,11 +74,18 @@ fn iteratorEdge( var nodeCnt: u32 = 0; var filter: []u8 = undefined; + var edgeFilter: []u8 = undefined; + if (It == t.QueryIteratorType.filter) { filter = utils.sliceNext(header.filterSize, q, i); try Filter.prepare(filter, ctx, typeEntry); } + if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or It == t.QueryIteratorType.edgeFilterOnEdge) { + edgeFilter = utils.sliceNext(header.edgeFilterSize, q, i); + try Filter.prepare(filter, ctx, typeEntry); + } + const nestedQuery = q[i.* .. i.* + header.includeSize]; const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; @@ -94,18 +101,28 @@ fn iteratorEdge( } } + if (It == t.QueryIteratorType.edgeFilterOnEdge) { + if (!try Filter.filter(ref.node, ctx, edgeFilter)) { + continue; + } + } + try ctx.thread.query.append(t.ReadOp.id); try ctx.thread.query.append(Node.getNodeId(ref.node)); try Include.include(ref.node, ctx, nestedQuery, typeEntry); - try ctx.thread.query.append(t.ReadOp.edge); - const edgesByteSizeIndex = try ctx.thread.query.reserve(4); - const edgeStartIndex = ctx.thread.query.index; - try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); - ctx.thread.query.writeAs( - u32, - @truncate(ctx.thread.query.index - edgeStartIndex), - edgesByteSizeIndex, - ); + + if (It != t.QueryIteratorType.edgeFilterOnEdge) { + try ctx.thread.query.append(t.ReadOp.edge); + const edgesByteSizeIndex = try ctx.thread.query.reserve(4); + const edgeStartIndex = ctx.thread.query.index; + try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - edgeStartIndex), + edgesByteSizeIndex, + ); + } + nodeCnt += 1; if (nodeCnt >= header.limit) { break; @@ -313,6 +330,7 @@ pub fn references( nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + .edgeFilter => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); @@ -331,10 +349,10 @@ pub fn references( nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - // 12 more edgeIncludeFilterEdge - // edgeFilter - // filterORedgeFilter - // filtertAndedgeFilter + .edgeFilterOnEdge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, .edgeInclude => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); @@ -354,6 +372,7 @@ pub fn references( nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + .edgeIncludeFilter => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); @@ -372,10 +391,13 @@ pub fn references( nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - // 12 more edgeIncludeFilterEdge - // edgeFilter - // filterORedgeFilter - // filtertAndedgeFilter + + .edgeIncludeFilterOnEdge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try iteratorEdge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + + // filterAndEdgeFilter else => { // not handled diff --git a/native/types.zig b/native/types.zig index 943a3bc74a..24c4154fc3 100644 --- a/native/types.zig +++ b/native/types.zig @@ -649,6 +649,10 @@ pub const QueryIteratorType = enum(u8) { edgeIncludeDescSort = 35, edgeIncludeDescFilter = 36, edgeIncludeDescFilterSort = 37, + + edgeIncludeFilterOnEdge = 38, + edgeFilterOnEdge = 39, + // default search search = 120, searchFilter = 121, @@ -751,6 +755,27 @@ pub const SubscriptionHeader = packed struct { partialLen: u8, }; +// lets measure +// pub const QueryHeader = packed struct { +// op: QueryType, +// prop: u8, // this is for ref +// typeId: TypeId, +// edgeTypeId: TypeId, +// offset: u32, +// limit: u32, +// filterSize: u16, +// searchSize: u16, +// edgeSize: u16, +// edgeFilterSize: u16, +// includeSize: u16, // cannot be more then 16kb? might be good enough +// size: u16, +// sort: bool, +// filter: bool, +// hasEdge: bool, +// edgeInclude: bool, +// _padding: u4, +// }; + pub const QueryHeader = packed struct { op: QueryType, prop: u8, // this is for ref From 92ed6035b9c57ae716ababa88b1cdcb22fc150e2 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 19:56:15 +0100 Subject: [PATCH 330/449] perf --- test/query-ast/include.ts | 40 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 6853bd26a9..27b806305f 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -75,7 +75,7 @@ await test('include', async (t) => { const rand = fastPrng() - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 1e5; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -104,7 +104,8 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - target: b, + range: { start: 0, end: 1e5 }, + // target: b, // order: 'desc', // sort: { prop: 'y' }, @@ -149,18 +150,18 @@ await test('include', async (t) => { name: { include: {} }, y: { include: {} }, }, - filter: { - props: { - y: { - ops: [{ op: '>', val: 6 }], - }, - }, - }, - edges: { - props: { - $level: { include: {} }, - }, - }, + // filter: { + // props: { + // y: { + // ops: [{ op: '>', val: 6 }], + // }, + // }, + // }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, }, // mrFriend: { // props: { @@ -183,7 +184,6 @@ await test('include', async (t) => { debugBuffer(deflateSync(ctx.query).toString('hex')) - const result = await db.server.getQueryBuf(ctx.query) const queries: any = [] for (let i = 0; i < 10; i++) { const x = ctx.query.slice(0) @@ -191,7 +191,7 @@ await test('include', async (t) => { queries.push(x) } - await perf.skip( + await perf( async () => { const q: any = [] for (let i = 0; i < 10; i++) { @@ -201,15 +201,19 @@ await test('include', async (t) => { }, 'filter speed', { - repeat: 10, + repeat: 100, }, ) // const readSchemaBuf = serializeReaderSchema(ctx.readSchema) + // console.log(result.byteLength) + const result = await db.server.getQueryBuf(ctx.query) const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - console.dir(obj, { depth: 10 }) + // console.dir(obj, { depth: 10 }) + + await wait(1000) // RETURN NULL FOR UNDEFINED From 8b029a87767a7e072c701cefb9eb2975fdba491b Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Wed, 18 Feb 2026 20:28:26 +0100 Subject: [PATCH 331/449] x --- test/query-ast/include.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 27b806305f..94f17815a0 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -75,7 +75,7 @@ await test('include', async (t) => { const rand = fastPrng() - for (let i = 0; i < 1e5; i++) { + for (let i = 0; i < 1e6; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -104,7 +104,7 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - range: { start: 0, end: 1e5 }, + range: { start: 0, end: 1e6 }, // target: b, // order: 'desc', // sort: { prop: 'y' }, From d9ba4352d7db2ab7d74037c4cb2f7cf5e245bd80 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 10:46:40 +0100 Subject: [PATCH 332/449] Infer type --- clibs/lib/selva/db.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 013b5f6402..b35813da9e 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -331,7 +331,7 @@ void selva_db_set_dirty_hook(struct SelvaDb *db, selva_db_dirty_hook_t dirty_hoo */ static inline void selva_del_block_unsafe(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t block_i, bool unload) { - struct SelvaNodeIndex *nodes = &te->blocks->blocks[block_i].nodes; + auto nodes = &te->blocks->blocks[block_i].nodes; struct SelvaNode *node; struct SelvaNode *tmp; @@ -690,13 +690,12 @@ struct SelvaNodeRes selva_upsert_node(struct SelvaTypeEntry *type, node_id_t nod */ static struct SelvaNodeRes selva_min_node_from(struct SelvaTypeEntry *type, block_id_t start) { - struct SelvaTypeBlocks *blocks = type->blocks; + auto blocks = type->blocks; const size_t len = blocks->len; struct SelvaNodeRes res = {}; for (size_t i = start; i < len; i++) { - struct SelvaTypeBlock *block = &blocks->blocks[i]; - struct SelvaNode *node; + auto block = &blocks->blocks[i]; res.block = i; res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_acquire); @@ -704,7 +703,7 @@ static struct SelvaNodeRes selva_min_node_from(struct SelvaTypeEntry *type, bloc break; } - node = RB_MIN(SelvaNodeIndex, &block->nodes); + auto node = RB_MIN(SelvaNodeIndex, &block->nodes); if (node) { res.node = node; break; @@ -728,8 +727,7 @@ static struct SelvaNodeRes selva_max_node_from(struct SelvaTypeEntry *type, bloc struct SelvaNodeRes res = {}; for (ssize_t i = start; i >= 0; i--) { - struct SelvaTypeBlock *block = &blocks->blocks[i]; - struct SelvaNode *node; + auto block = &blocks->blocks[i]; res.block = i; res.block_status = atomic_load_explicit(&block->status.atomic, memory_order_acquire); @@ -737,7 +735,7 @@ static struct SelvaNodeRes selva_max_node_from(struct SelvaTypeEntry *type, bloc break; } - node = RB_MAX(SelvaNodeIndex, &block->nodes); + auto node = RB_MAX(SelvaNodeIndex, &block->nodes); if (node) { res.node = node; break; From ffc9ee0a6c6f97880d3e43ce254957759bf2f8d0 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 10:53:56 +0100 Subject: [PATCH 333/449] Improve logging --- clibs/lib/selva/io/dump.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 2b14f68e75..122934054f 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -1041,8 +1041,9 @@ int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i selva_block_status_replace(te, block_i, prev_block_status); } if (old_hash != new_hash) { - selva_io_errlog(&io, "%s: Block hash mismatch! %.*s != %.*s", + selva_io_errlog(&io, "%s: Block hash mismatch for %u:%u! %.*s != %.*s", __func__, + (unsigned)te->type, (unsigned)block_i, SELVA_HASH_HEX_LEN, selva_hash_to_hex((char [SELVA_HASH_HEX_LEN]){ 0 }, old_hash), SELVA_HASH_HEX_LEN, selva_hash_to_hex((char [SELVA_HASH_HEX_LEN]){ 0 }, new_hash)); err = SELVA_EIO; From 7e45923cde4403d4886fadadf6e3030dbb0af95d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 11:01:07 +0100 Subject: [PATCH 334/449] Crash on load error and don't log ENOENT --- src/db-server/blocks.ts | 8 ++++---- src/db-server/start.ts | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/db-server/blocks.ts b/src/db-server/blocks.ts index b6149e5c51..7504807a67 100644 --- a/src/db-server/blocks.ts +++ b/src/db-server/blocks.ts @@ -66,12 +66,12 @@ export async function loadCommon( db.addOpOnceListener(OpType.loadCommon, id, (buf: Uint8Array) => { const err = readInt32(buf, 0) if (err) { - // TODO read errlog - const errMsg = `Load failed: ${native.selvaStrerror(err)}` const errLog = DECODER.decode(buf.subarray(4)) + const errMsg = `Load failed: ${native.selvaStrerror(err)}\n${errLog}` - console.log(errLog) - db.emit('error', errMsg) + if (!errMsg.includes('ERR_SELVA ENOENT')) { + db.emit('error', errMsg) + } reject(new Error(errMsg)) } else { resolve() diff --git a/src/db-server/start.ts b/src/db-server/start.ts index a25e70bf8d..3aab6a4f6e 100644 --- a/src/db-server/start.ts +++ b/src/db-server/start.ts @@ -92,7 +92,9 @@ export async function realStart(db: DbServer, schema: SchemaOut) { setSchemaOnServer(db, schema) await loadCommon(db) } catch (e) { - console.error(e.message) + if (!e.message.includes('ERR_SELVA ENOENT')) { + throw e + } } if (db.fileSystemPath) { From e3f29509cd79ca51d61aa225285b82a1c25cc661 Mon Sep 17 00:00:00 2001 From: youzi Date: Thu, 19 Feb 2026 11:08:49 +0100 Subject: [PATCH 335/449] wip --- src/db-client/query2/index.ts | 203 +++++++++++++++++++++++++++++++--- test/query/ast.ts | 13 +++ test/query/db.ts | 6 +- 3 files changed, 206 insertions(+), 16 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 72dc75d1b4..e826ef77fc 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -18,11 +18,7 @@ import { astToQueryCtx } from '../../db-query/ast/toCtx.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import type { DbClient } from '../../sdk.js' import { proxyResult } from './result.js' -import type { - StepInput, - IntervalString, - aggFnOptions, -} from '../query/aggregates/types.js' +import type { StepInput, aggFnOptions } from '../query/aggregates/types.js' class Query< S extends { types: any } = { types: any }, @@ -147,6 +143,19 @@ class Query< return this.#addFilter(prop, op, val, opts, true) } + sum) => AnyQuery>( + fn: F, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > sum

>( ...props: [P, ...P[]] ): NextBranch< @@ -159,7 +168,15 @@ class Query< EdgeProps, Aggregate & UnionToIntersection>, GroupedKey - > { + > + sum( + ...props: any[] + ): NextBranch { + if (typeof props[0] === 'function') { + const fn = props[0] + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (props.length === 0) { throw new Error('Query: sum expects at least one argument') } @@ -183,6 +200,19 @@ class Query< return this as any } + cardinality) => AnyQuery>( + fn: F, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > cardinality

( ...props: [P, ...P[]] ): NextBranch< @@ -195,7 +225,15 @@ class Query< EdgeProps, Aggregate & UnionToIntersection>, GroupedKey - > { + > + cardinality( + ...props: any[] + ): NextBranch { + if (typeof props[0] === 'function') { + const fn = props[0] + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (props.length === 0) { throw new Error('Query: cardinality expects at least one argument') } @@ -204,6 +242,19 @@ class Query< return this as any } + avg) => AnyQuery>( + fn: F, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > avg

>( ...props: [P, ...P[]] ): NextBranch< @@ -216,7 +267,15 @@ class Query< EdgeProps, Aggregate & UnionToIntersection>, GroupedKey - > { + > + avg( + ...props: any[] + ): NextBranch { + if (typeof props[0] === 'function') { + const fn = props[0] + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (props.length === 0) { throw new Error('Query: avg expects at least one argument') } @@ -225,6 +284,19 @@ class Query< return this as any } + hmean) => AnyQuery>( + fn: F, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > hmean

>( ...props: [P, ...P[]] ): NextBranch< @@ -237,7 +309,15 @@ class Query< EdgeProps, Aggregate & UnionToIntersection>, GroupedKey - > { + > + hmean( + ...props: any[] + ): NextBranch { + if (typeof props[0] === 'function') { + const fn = props[0] + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (props.length === 0) { throw new Error('Query: hmean expects at least one argument') } @@ -246,6 +326,19 @@ class Query< return this as any } + max) => AnyQuery>( + fn: F, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > max

>( ...props: [P, ...P[]] ): NextBranch< @@ -259,7 +352,15 @@ class Query< Aggregate & UnionToIntersection }>>, GroupedKey - > { + > + max( + ...props: any[] + ): NextBranch { + if (typeof props[0] === 'function') { + const fn = props[0] + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (props.length === 0) { throw new Error('Query: max expects at least one argument') } @@ -268,6 +369,19 @@ class Query< return this as any } + min) => AnyQuery>( + fn: F, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > min

>( ...props: [P, ...P[]] ): NextBranch< @@ -281,7 +395,15 @@ class Query< Aggregate & UnionToIntersection }>>, GroupedKey - > { + > + min( + ...props: any[] + ): NextBranch { + if (typeof props[0] === 'function') { + const fn = props[0] + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (props.length === 0) { throw new Error('Query: min expects at least one argument') } @@ -290,6 +412,20 @@ class Query< return this as any } + stddev) => AnyQuery>( + fn: F, + opts?: aggFnOptions, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > stddev

>( prop: P | P[], opts?: aggFnOptions, @@ -306,7 +442,16 @@ class Query< ExpandDotPath

>, GroupedKey - > { + > + stddev( + prop: any, + opts?: any, + ): NextBranch { + if (typeof prop === 'function') { + const fn = prop + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (!prop) { throw new Error('Query: stddev expects at least one argument') } @@ -319,6 +464,20 @@ class Query< return this as any } + var) => AnyQuery>( + fn: F, + opts?: aggFnOptions, + ): NextBranch< + S, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate & ResolveAggregate, + GroupedKey + > var

>( prop: P | P[], opts?: aggFnOptions, @@ -335,7 +494,16 @@ class Query< ExpandDotPath

>, GroupedKey - > { + > + var( + prop: any, + opts?: any, + ): NextBranch { + if (typeof prop === 'function') { + const fn = prop + fn((prop: string) => new Query(traverse(this.ast, prop))) + return this as any + } if (!prop) { throw new Error('Query: var expects at least one argument') } @@ -638,6 +806,15 @@ export type ResolveIncludeArgs = T extends ( ? ResolveDotPath : T +// ResolveAggregate extracts the aggregate structure from a callback function +type ResolveAggregate = + ResolveIncludeArgs extends { + field: infer F extends string | number | symbol + select: { _aggregate: infer A } + } + ? { [K in F]: A } + : never + // Helper type to simplify include signature type AnyQuery = Query< S, diff --git a/test/query/ast.ts b/test/query/ast.ts index 138f967da3..6ecd58e2e8 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -242,4 +242,17 @@ await test('query ast creation', async (t) => { }, }) } + + { + const res = query('user').sum((select) => select('friends').sum('age')) + console.dir(res.ast, { depth: null }) + // deepEqual(res.ast, { + // type: 'user', + // sum: { + // props: { + // friends: { sum: { props: ['age'] } }, + // }, + // }, + // }) + } }) diff --git a/test/query/db.ts b/test/query/db.ts index 9769c96552..a5cf4f6162 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -111,7 +111,7 @@ await test('query db', async (t) => { { const res = await db.query2('user').sum('age').get() - deepEqual(res, { age: { sum: 70 } }) + deepEqual(res, { age: { sum: 70 } } as any) } // TODO wait for marco to check these @@ -128,9 +128,9 @@ await test('query db', async (t) => { { const res = await db .query2('user') - .sum((select) => select('friend').sum('age')) + .sum((select) => select('friends').sum('age')) .get() - deepEqual(res, { friend: { age: { sum: 70 } } }) + deepEqual(res, { friends: { age: { sum: 21 } } } as any) } // { From b161ffbd219d9fd66b4eb6e4fcec70653c0232cd Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 11:25:27 +0100 Subject: [PATCH 336/449] Even better logs --- clibs/lib/selva/io/dump.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index 122934054f..e1883d5a66 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -997,7 +997,8 @@ int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i } if (io.sdb_version > db->sdb_version) { - selva_io_errlog(&io, "SDB version mismatch! common: %"PRIu32" block: %"PRIu32, db->sdb_version, io.sdb_version); + selva_io_errlog(&io, "%s: SDB version mismatch! common_version: %"PRIu32" block_version: %"PRIu32, + __func__ , db->sdb_version, io.sdb_version); err = SELVA_ENOTSUP; goto fail; } @@ -1013,13 +1014,15 @@ int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i } if (!read_dump_magic(&io, DUMP_MAGIC_BLOCK_HASH)) { - selva_io_errlog(&io, "%s: Invalid block hash magic", __func__); + selva_io_errlog(&io, "%s: Invalid block hash magic (block: %u:%u)", + __func__, (unsigned)te->type, (unsigned)block_i); err = SELVA_EINVAL; goto fail; } if (io.sdb_read(&old_hash, sizeof(old_hash), 1, &io) != 1) { - selva_io_errlog(&io, "%s: Failed to read the hash", __func__); + selva_io_errlog(&io, "%s: Failed to read the hash (block: %u:%u)", + __func__, (unsigned)te->type, (unsigned)block_i); err = SELVA_EINVAL; goto fail; } @@ -1041,7 +1044,7 @@ int selva_dump_load_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_i selva_block_status_replace(te, block_i, prev_block_status); } if (old_hash != new_hash) { - selva_io_errlog(&io, "%s: Block hash mismatch for %u:%u! %.*s != %.*s", + selva_io_errlog(&io, "%s: Block hash mismatch (block: %u:%u)! %.*s != %.*s", __func__, (unsigned)te->type, (unsigned)block_i, SELVA_HASH_HEX_LEN, selva_hash_to_hex((char [SELVA_HASH_HEX_LEN]){ 0 }, old_hash), From 57008ec42f4bbc1b20cf35ca57d1a9ca2d7ac45e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 11:26:47 +0100 Subject: [PATCH 337/449] Don't hash aliases with blocks Aliases are stored in common and thus shouldn't affect the node/block hashing. A change in an alias doesn't mark the block dirty and thus the next time the block is loaded the hash would be different. --- clibs/lib/selva/db.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index b35813da9e..ab7c18fcf9 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -811,28 +811,6 @@ extern inline node_id_t selva_get_node_id(const struct SelvaNode *node); extern inline node_type_t selva_get_node_type(const struct SelvaNode *node); -/** - * Hash the aliases pointing to the given node. - */ -static void hash_aliases(selva_hash_state_t *hash_state, struct SelvaTypeEntry *type, node_id_t dest) -{ - for (size_t i = 0; i < type->ns.nr_alias_fields; i++) { - struct SelvaAliases *aliases = &type->aliases[i]; - const struct SelvaAlias *alias; - struct SelvaAlias find = { - .dest = dest, - }; - field_t f = i; - - alias = RB_FIND(SelvaAliasesByDest, &aliases->alias_by_dest, &find); - if (alias) { - selva_hash_update(hash_state, &f, sizeof(f)); - selva_hash_update(hash_state, &dest, sizeof(dest)); - selva_hash_update(hash_state, alias->name, alias->name_len); - } - } -} - static void hash_col_fields(struct SelvaTypeEntry *type, node_id_t node_id, selva_hash_state_t *tmp_hash_state) { /* @@ -852,7 +830,6 @@ selva_hash128_t selva_node_hash_update(struct SelvaDb *db, struct SelvaTypeEntry selva_hash_reset(tmp_hash_state); selva_hash_update(tmp_hash_state, &node->node_id, sizeof(node->node_id)); selva_fields_hash_update(tmp_hash_state, db, &type->ns.fields_schema, node); - hash_aliases(tmp_hash_state, type, node->node_id); hash_col_fields(type, node->node_id, tmp_hash_state); res = selva_hash_digest(tmp_hash_state); From 8777315a5ee60029c7692cb858a26f5fe98c81f0 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 14:17:09 +0100 Subject: [PATCH 338/449] This function must be static inline --- clibs/include/selva/db.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 8ded1d309e..074cda67d2 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -114,7 +114,7 @@ inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te); SELVA_EXPORT inline block_id_t selva_get_block_capacity(const struct SelvaTypeEntry *te); -inline block_id_t selva_node_id2block_i3(block_id_t block_capacity, node_id_t node_id) +static inline block_id_t selva_node_id2block_i3(block_id_t block_capacity, node_id_t node_id) { assert(node_id > 0); return ((node_id - 1) - ((node_id - 1) % block_capacity)) / block_capacity; From c7a84eb609943272200001bdf57605c15b7c387a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 14:17:23 +0100 Subject: [PATCH 339/449] Separate declaration and definition --- clibs/include/selva/db.h | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index 074cda67d2..ce3d3a5a5b 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -323,28 +323,14 @@ size_t selva_node_count(const struct SelvaTypeEntry *type) __attribute__((nonnul */ SELVA_EXPORT __attribute__((nonnull, pure)) -inline node_id_t selva_get_node_id(const struct SelvaNode *node) -#ifndef __zig -{ - return node->node_id; -} -#else -; -#endif +inline node_id_t selva_get_node_id(const struct SelvaNode *node); /** * Get the type of of node. */ SELVA_EXPORT __attribute__((nonnull, pure)) -inline node_type_t selva_get_node_type(const struct SelvaNode *node) -#ifndef __zig -{ - return node->type; -} -#else -; -#endif +inline node_type_t selva_get_node_type(const struct SelvaNode *node); /** * \addtogroup node_hash @@ -588,4 +574,14 @@ inline const struct SelvaFieldsSchema *selva_get_edge_field_fields_schema(struct return (te) ? &selva_get_ns_by_te(te)->fields_schema : nullptr; } + +inline node_id_t selva_get_node_id(const struct SelvaNode *node) +{ + return node->node_id; +} + +inline node_type_t selva_get_node_type(const struct SelvaNode *node) +{ + return node->type; +} #endif From 553ddefa3f58e062c015062e7004bbdfae83c205 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 14:17:41 +0100 Subject: [PATCH 340/449] More auto just for fun --- clibs/lib/selva/db.c | 4 +--- clibs/lib/selva/io/dump.c | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index ab7c18fcf9..350dd87577 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -142,9 +142,7 @@ void selva_db_expire_tick(struct SelvaDb *db, int64_t now) static bool eq_type_exists(struct SelvaDb *db, node_type_t type, const uint8_t *schema_buf, size_t schema_len) { - struct SelvaTypeEntry *te; - - te = selva_get_type_by_index(db, type); + auto te = selva_get_type_by_index(db, type); return (te && te->schema_len == schema_len && !memcmp(te->schema_buf, schema_buf, schema_len)); } diff --git a/clibs/lib/selva/io/dump.c b/clibs/lib/selva/io/dump.c index e1883d5a66..14e0d90c5b 100644 --- a/clibs/lib/selva/io/dump.c +++ b/clibs/lib/selva/io/dump.c @@ -214,8 +214,8 @@ static void save_node_fields(struct selva_io *io, const struct SelvaFieldsSchema io->sdb_write(&((sdb_nr_fields_t){ fields->nr_fields }), sizeof(sdb_nr_fields_t), 1, io); for (field_t field = 0; field < nr_fields; field++) { - const struct SelvaFieldSchema *fs = get_fs_by_fields_schema_field(schema, field); - struct SelvaFieldInfo *nfo = &fields->fields_map[field]; + auto fs = get_fs_by_fields_schema_field(schema, field); + auto nfo = &fields->fields_map[field]; enum SelvaFieldType type = nfo->in_use ? fs->type : SELVA_FIELD_TYPE_NULL; #if USE_DUMP_MAGIC_FIELD_BEGIN @@ -266,7 +266,6 @@ static void save_expire(struct selva_io *io, struct SelvaDb *db) { struct SVectorIterator it; - struct SelvaExpireToken *token; const sdb_arr_len_t count = selva_expire_count(&db->expiring); write_dump_magic(io, DUMP_MAGIC_EXPIRE); @@ -274,6 +273,8 @@ static void save_expire(struct selva_io *io, struct SelvaDb *db) SVector_ForeachBegin(&it, &db->expiring.list); while (!SVector_Done(&it)) { + struct SelvaExpireToken *token; + token = SVector_Foreach(&it); do { struct SelvaDbExpireToken *dbToken = containerof(token, typeof(*dbToken), token); @@ -294,11 +295,11 @@ static void save_aliases(struct selva_io *io, struct SelvaDb *db) write_dump_magic(io, DUMP_MAGIC_ALIASES); for (size_t ti = 0; ti < db->nr_types; ti++) { - struct SelvaTypeEntry *te = &db->types[ti]; + auto te = &db->types[ti]; const size_t nr_fields = te->ns.nr_alias_fields; for (size_t i = 0; i < nr_fields; i++) { - struct SelvaAliases *aliases = &te->aliases[i]; + auto aliases = &te->aliases[i]; sdb_nr_aliases_t nr_aliases_by_name = aliases->nr_aliases; struct SelvaAlias *alias; @@ -391,7 +392,7 @@ static void selva_dump_save_colvec(struct selva_io *io, struct SelvaDb *, struct static_assert(sizeof(block_i) == sizeof(uint32_t)); for (size_t i = 0; i < te->ns.nr_colvec_fields; i++) { - struct SelvaColvec *colvec = &te->col_fields.colvec[i]; + auto colvec = &te->col_fields.colvec[i]; uint8_t *slab = (uint8_t *)colvec->v[block_i]; uint8_t slab_present = !!slab; @@ -583,7 +584,7 @@ static int load_field_reference(struct selva_io *io, struct SelvaNode *node, con io->sdb_read(&nr_refs, sizeof(nr_refs), 1, io); if (nr_refs) { - struct SelvaNodeLargeReference *ref = selva_fields_ensure_reference(node, fs); + auto ref = selva_fields_ensure_reference(node, fs); io->sdb_read(&ref->dst, sizeof(ref->dst), 1, io); io->sdb_read(&ref->edge, sizeof(ref->edge), 1, io); @@ -604,7 +605,7 @@ static int load_field_references(struct selva_io *io, struct SelvaDb *db, struct } (void)selva_fields_prealloc_refs(db, node, fs, nr_refs); - struct SelvaNodeReferences *refs = selva_fields_get_references(node, fs); + auto refs = selva_fields_get_references(node, fs); if (!refs) { return SELVA_ENOENT; } @@ -631,7 +632,7 @@ static int load_field_references(struct selva_io *io, struct SelvaDb *db, struct __attribute__((warn_unused_result)) static int load_node_fields(struct selva_io *io, struct SelvaDb *db, struct SelvaTypeEntry *te, struct SelvaNode *node) { - struct SelvaNodeSchema *ns = &te->ns; + auto ns = &te->ns; sdb_nr_fields_t nr_fields; int err = 0; @@ -732,7 +733,7 @@ static node_id_t load_node(struct selva_io *io, struct SelvaDb *db, struct Selva return SELVA_ENOENT; } - struct SelvaNode *node = res.node; + auto node = res.node; assert(node && node->type == te->type); err = load_node_fields(io, db, te, node); if (err) { @@ -784,7 +785,7 @@ static int load_colvec(struct selva_io *io, struct SelvaTypeEntry *te) /* * Load the whole slab at once. */ - struct SelvaColvec *colvec = &te->col_fields.colvec[i]; + auto colvec = &te->col_fields.colvec[i]; void *slab = colvec_init_slab(colvec, block_i); if (io->sdb_read(slab, colvec->slab_size, 1, io) != 1) { selva_io_errlog(io, "colvec slab"); @@ -856,7 +857,7 @@ static int load_aliases(struct selva_io *io, struct SelvaDb *db) } for (size_t ti = 0; ti < db->nr_types; ti++) { - struct SelvaTypeEntry *te = &db->types[ti]; + auto te = &db->types[ti]; const size_t nr_fields = te->ns.nr_alias_fields; for (size_t i = 0; i < nr_fields; i++) { From 004ebc40558f354208373b921b6ba247588d390f Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Thu, 19 Feb 2026 10:28:28 -0300 Subject: [PATCH 341/449] cardinality functions on refereces --- native/query/aggregates/aggregates.zig | 2 +- native/query/aggregates/references.zig | 7 +- package-lock.json | 4 - src/zigTsExports.ts | 6 + test/aggregate/deep.ts | 297 +++++++++++++------------ test/aggregate/dev.ts | 81 +++---- 6 files changed, 215 insertions(+), 182 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index 403d9eb98e..b8424ae6eb 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -73,7 +73,7 @@ pub inline fn aggregateProps( i += utils.sizeOf(t.AggProp); continue; }; - if (currentAggDef.aggFunction == t.AggFunction.cardinality) { + if (currentAggDef.aggFunction == .cardinality) { const hllValue = Selva.c.selva_fields_get_selva_string(node, propSchema) orelse null; if (hllValue == null) { i += utils.sizeOf(t.AggProp); diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index 6ea893731d..56cc56cf86 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -12,6 +12,7 @@ const GroupBy = @import("./group.zig"); const GroupByHashMap = @import("./hashMap.zig").GroupByHashMap; const errors = @import("../../errors.zig"); const accumulate = Aggregates.accumulate; +const std = @import("std"); pub inline fn aggregateRefsProps( ctx: *Query.QueryCtx, @@ -31,10 +32,14 @@ pub inline fn aggregateRefsProps( var it = try References.iterator(false, false, ctx.db, from, header.targetProp, fromType); + const hllAccumulator = Selva.c.selva_string_create(null, Selva.c.HLL_INIT_SIZE, Selva.c.SELVA_STRING_MUTABLE); + defer Selva.c.selva_string_free(hllAccumulator); + var aggCtx = Aggregates.AggCtx{ .queryCtx = ctx, .typeEntry = it.dstType, - .limit = 1000, // MV: check it + .limit = std.math.maxInt(u32), // unlimited in branched queries + .hllAccumulator = hllAccumulator, .isSamplingSet = header.isSamplingSet, .accumulatorSize = header.accumulatorSize, .resultsSize = header.resultsSize, diff --git a/package-lock.json b/package-lock.json index 20b4d7c1f2..71baa502af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,10 +174,6 @@ "@based/locale-x86-64-gnu": "*" } }, - "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { - "dev": true, - "optional": true - }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 15ec06137f..8e7a0151cd 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -2242,6 +2242,8 @@ export const QueryIteratorType = { edgeIncludeDescSort: 35, edgeIncludeDescFilter: 36, edgeIncludeDescFilterSort: 37, + edgeIncludeFilterOnEdge: 38, + edgeFilterOnEdge: 39, search: 120, searchFilter: 121, vec: 130, @@ -2277,6 +2279,8 @@ export const QueryIteratorTypeInverse = { 35: 'edgeIncludeDescSort', 36: 'edgeIncludeDescFilter', 37: 'edgeIncludeDescFilterSort', + 38: 'edgeIncludeFilterOnEdge', + 39: 'edgeFilterOnEdge', 120: 'search', 121: 'searchFilter', 130: 'vec', @@ -2312,6 +2316,8 @@ export const QueryIteratorTypeInverse = { edgeIncludeDescSort, edgeIncludeDescFilter, edgeIncludeDescFilterSort, + edgeIncludeFilterOnEdge, + edgeFilterOnEdge, search, searchFilter, vec, diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index d8fd60a8ad..52c8461224 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -582,53 +582,76 @@ await test('cardinality', async (t) => { ) }) -// await test.skip('cardinality on references', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// }) -// await db.start({ clean: true }) -// t.after(() => db.stop()) - -// await db.setSchema({ -// types: { -// booth: { -// company: 'string', -// // badgesScanned: 'number', -// badgesScanned: 'cardinality', -// }, -// fair: { -// day: 'timestamp', -// booths: { -// items: { -// ref: 'booth', -// prop: 'booth', -// }, -// }, -// }, -// }, -// }) - -// const bg = db.create('booth', { -// company: 'big one', -// badgesScanned: ['engineer 1', 'salesman', 'spy', 'annonymous'], -// }) -// const stp = db.create('booth', { -// company: 'just another startup', -// badgesScanned: ['nice ceo', 'entusiastic dev'], -// }) -// db.create('fair', { -// day: new Date('08/02/2024'), -// booths: [bg, stp], -// }) - -// await db.query('fair').include('booths.badgesScanned').get().inspect() -// await db -// .query('fair') -// .cardinality('booths.badgesScanned') -// .groupBy('day') -// .get() -// .inspect() -// }) +await test('cardinality on references', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => db.stop()) + + await db.setSchema({ + types: { + booth: { + company: 'string', + // badgesScanned: 'number', + badgesScanned: 'cardinality', + }, + fair: { + day: 'timestamp', + booths: { + items: { + ref: 'booth', + prop: 'booth', + }, + }, + }, + }, + }) + + const bg = db.create('booth', { + company: 'big one', + badgesScanned: ['engineer 1', 'salesman', 'spy', 'annonymous'], + }) + const stp = db.create('booth', { + company: 'just another startup', + badgesScanned: ['nice ceo', 'entusiastic dev'], + }) + db.create('fair', { + day: new Date('08/02/2024'), + booths: [bg, stp], + }) + + deepEqual( + await db + .query('fair') + .include((s) => s('booths').cardinality('badgesScanned')) + .get(), + [ + { + id: 1, + booths: { + badgesScanned: { + cardinality: 6, + }, + }, + }, + ], + 'branched query with cardinality function', + ) + + /* + * Nested syntax: + */ + + // await db.query('fair').include('booths.badgesScanned').get().inspect() + + // await db + // .query('fair') + // .cardinality('booths.badgesScanned') + // .groupBy('day') + // .get() + // .inspect() +}) await test('group by reference ids', async (t) => { const db = new BasedDb({ @@ -882,98 +905,98 @@ await test.skip('edges aggregation', async (t) => { // .get() // .inspect(10) - deepEqual( - await db - .query('movie') - .include((q) => q('actors').max('$rating')) - .get() - .toObject(), - [ - { - id: 1, - actors: { - $rating: { - max: 55, - }, - }, - }, - { - id: 2, - actors: { - $rating: { - max: 77, - }, - }, - }, - ], - 'single edge aggregation, branched query', - ) + // deepEqual( + // await db + // .query('movie') + // .include((q) => q('actors').max('$rating')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // actors: { + // $rating: { + // max: 55, + // }, + // }, + // }, + // { + // id: 2, + // actors: { + // $rating: { + // max: 77, + // }, + // }, + // }, + // ], + // 'single edge aggregation, branched query', + // ) - deepEqual( - await db - .query('movie') - .include((q) => q('actors').max('$rating').sum('$hating')) - .get() - .toObject(), - [ - { - id: 1, - actors: { - $rating: { - max: 55, - }, - $hating: { - sum: 5, - }, - }, - }, - { - id: 2, - actors: { - $rating: { - max: 77, - }, - $hating: { - sum: 10, - }, - }, - }, - ], - 'multiple edges with multiple agg functions, branched query', - ) + // deepEqual( + // await db + // .query('movie') + // .include((q) => q('actors').max('$rating').sum('$hating')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // actors: { + // $rating: { + // max: 55, + // }, + // $hating: { + // sum: 5, + // }, + // }, + // }, + // { + // id: 2, + // actors: { + // $rating: { + // max: 77, + // }, + // $hating: { + // sum: 10, + // }, + // }, + // }, + // ], + // 'multiple edges with multiple agg functions, branched query', + // ) - deepEqual( - await db - .query('movie') - .include((q) => q('actors').max('$rating', '$hating')) - .get() - .toObject(), - [ - { - id: 1, - actors: { - $rating: { - max: 55, - }, - $hating: { - max: 5, - }, - }, - }, - { - id: 2, - actors: { - $rating: { - max: 77, - }, - $hating: { - max: 7, - }, - }, - }, - ], - 'multiple edges on same agg function, branched query', - ) + // deepEqual( + // await db + // .query('movie') + // .include((q) => q('actors').max('$rating', '$hating')) + // .get() + // .toObject(), + // [ + // { + // id: 1, + // actors: { + // $rating: { + // max: 55, + // }, + // $hating: { + // max: 5, + // }, + // }, + // }, + // { + // id: 2, + // actors: { + // $rating: { + // max: 77, + // }, + // $hating: { + // max: 7, + // }, + // }, + // }, + // ], + // 'multiple edges on same agg function, branched query', + // ) /*-----------------------------------*/ /* STRAIGHT ON TYPE */ diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index 0b45a925e2..170d5cec72 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -227,49 +227,52 @@ await test('yyy', async (t) => { const result = await db .query('team') .include('teamName', 'city', (select) => { - select('players').sum('goalsScored', 'gamesPlayed').groupBy('position') + select('players') + .sum('goalsScored', 'gamesPlayed') + .groupBy('position') + .range(0, 10) }) .get() // result.debug() // result.inspect() - deepEqual( - result.toObject(), - [ - { - id: 1, - teamName: 'Grêmio', - city: 'Porto Alegre', - players: { - Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) - Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) - }, - }, - { - id: 2, - teamName: 'Ajax', - city: 'Amsterdam', - players: { - Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) - Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) - }, - }, - { - id: 3, - teamName: 'Boca Juniors', - city: 'Buenos Aires', - players: {}, // does anybody wants to play for Boca? - }, - { - id: 4, - teamName: 'Barcelona', - city: 'Barcelona', - players: { - Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski - }, - }, - ], - 'Include parent props, with referenced items grouped by their own prop, and aggregations', - ) + // deepEqual( + // result.toObject(), + // [ + // { + // id: 1, + // teamName: 'Grêmio', + // city: 'Porto Alegre', + // players: { + // Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) + // Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) + // }, + // }, + // { + // id: 2, + // teamName: 'Ajax', + // city: 'Amsterdam', + // players: { + // Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) + // Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) + // }, + // }, + // { + // id: 3, + // teamName: 'Boca Juniors', + // city: 'Buenos Aires', + // players: {}, // does anybody wants to play for Boca? + // }, + // { + // id: 4, + // teamName: 'Barcelona', + // city: 'Barcelona', + // players: { + // Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski + // }, + // }, + // ], + // 'Include parent props, with referenced items grouped by their own prop, and aggregations', + // ) }) From cc257fb8695d61a896dd5f969be9857e9bb32072 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Thu, 19 Feb 2026 10:54:08 -0300 Subject: [PATCH 342/449] fix display cardinality style --- src/db-client/query/display.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db-client/query/display.ts b/src/db-client/query/display.ts index 547fb2281b..20582ecd42 100644 --- a/src/db-client/query/display.ts +++ b/src/db-client/query/display.ts @@ -125,7 +125,7 @@ export const prettyPrintVal = (v: any, type: PropTypeEnum): string => { } if (type === PropType.cardinality) { - return `${styleText('blue', v)} ${styleText('italic', styleText('dim', 'unique'))}` + return `${styleText('blue', `${v}`)} ${styleText('italic', styleText('dim', 'unique'))}` } if (type === PropType.timestamp) { From a7eb6fbf225e8ce724668931f1a1995ac1bf8542 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Thu, 19 Feb 2026 10:54:29 -0300 Subject: [PATCH 343/449] fix default precision cardinality test --- test/cardinality.ts | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/test/cardinality.ts b/test/cardinality.ts index b8e140ebf5..fb4e14bc80 100644 --- a/test/cardinality.ts +++ b/test/cardinality.ts @@ -411,16 +411,13 @@ await test('defaultPrecision', async (t) => { t.after(() => db.stop()) await db.setSchema({ - // props: { - // myRootCount: 'cardinality', - // }, types: { - stores: { + store: { name: 'string', customers: { items: { ref: 'customer', - prop: 'customer', + prop: 'client', }, }, }, @@ -435,10 +432,41 @@ await test('defaultPrecision', async (t) => { name: 'Alex Atala', productsBought: ['fork', 'knife', 'knife', 'frying pan'], }) - const sto = db.create('stores', { - name: "Worderland's Kitchen", + const sto = db.create('store', { + name: 'Worderlands Kitchen', customers: [cus], }) - // await db.query('stores').include('*', '**').get().inspect() + const pb = await db.query('customer').include('productsBought').get() + // pb.inspect() + deepEqual( + pb, + [ + { + id: 1, + productsBought: 3, + }, + ], + 'simple cardinality default precision', + ) + + const pbr = await db.query('store').include('*', '**').get() + // pbr.inspect() + deepEqual( + pbr, + [ + { + id: 1, + name: 'Worderlands Kitchen', + customers: [ + { + id: 1, + name: 'Alex Atala', + productsBought: 3, + }, + ], + }, + ], + 'simple cardinality default precision on ref', + ) }) From d06cc7304ba16c241d9241ceff8003d5fcea5526 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 14:25:17 +0100 Subject: [PATCH 344/449] This inline function needs a stupid macro helper > An inline definition of a function with external linkage shall > not contain a definition of a modifiable object with static > storage duration, and shall not contain a reference to an > identifier with internal linkage. --- clibs/include/selva/db.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clibs/include/selva/db.h b/clibs/include/selva/db.h index ce3d3a5a5b..d21f87abb9 100644 --- a/clibs/include/selva/db.h +++ b/clibs/include/selva/db.h @@ -114,10 +114,13 @@ inline block_id_t selva_get_nr_blocks(const struct SelvaTypeEntry *te); SELVA_EXPORT inline block_id_t selva_get_block_capacity(const struct SelvaTypeEntry *te); +#define SELVA_NODE_ID2BLOCK_I3(block_capacity, node_id) \ + (((node_id - 1) - ((node_id - 1) % block_capacity)) / block_capacity) + static inline block_id_t selva_node_id2block_i3(block_id_t block_capacity, node_id_t node_id) { assert(node_id > 0); - return ((node_id - 1) - ((node_id - 1) % block_capacity)) / block_capacity; + return SELVA_NODE_ID2BLOCK_I3(block_capacity, node_id); } SELVA_EXPORT @@ -449,7 +452,7 @@ inline block_id_t selva_get_block_capacity(const struct SelvaTypeEntry *te) inline block_id_t selva_node_id2block_i(const struct SelvaTypeBlocks *blocks, node_id_t node_id) { - return selva_node_id2block_i3(blocks->block_capacity, node_id); + return SELVA_NODE_ID2BLOCK_I3(blocks->block_capacity, node_id); } inline block_id_t selva_node_id2block_i2(const struct SelvaTypeEntry *te, node_id_t node_id) From 39a6cff8411df6cf626b2f17bb71d20cdef4f447 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 15:01:59 +0100 Subject: [PATCH 345/449] Improve SelvaFieldSchema packing and handling --- clibs/include/selva/types.h | 36 +++++++++++++++++++++++++++--------- clibs/lib/selva/alias.c | 4 ++-- clibs/lib/selva/colvec.c | 4 ++-- clibs/lib/selva/fields.c | 6 +++--- clibs/lib/selva/schema.c | 29 ++++++++++++++++------------- 5 files changed, 50 insertions(+), 29 deletions(-) diff --git a/clibs/include/selva/types.h b/clibs/include/selva/types.h index 80adc12167..c1d51cebb5 100644 --- a/clibs/include/selva/types.h +++ b/clibs/include/selva/types.h @@ -58,28 +58,46 @@ struct EdgeFieldConstraint { struct SelvaFieldSchema { field_t field; enum SelvaFieldType type; + /** + * Offset to the default value in te->schema_buf. + * Only valid for field types that can have defaults. + */ + uint32_t default_off; union { + /** + * SELVA_FIELD_TYPE_STRING. + */ struct { size_t fixed_len; /*!< Greater than zero if the string has a fixed maximum length. */ - uint32_t default_off; /*!< Offset to the default value in te->schema_buf. */ uint32_t default_len; - } string; /*!< SELVA_FIELD_TYPE_STRING */ + } string; struct { uint32_t nr_defaults; /*!< Number of defaults for this text field. */ - uint32_t defaults_off; /*!< Offset to the default values in te->schema_buf. */ } text; /*!< SELVA_FIELD_TYPE_TEXT */ - struct EdgeFieldConstraint edge_constraint; /*!< SELVA_FIELD_TYPE_REFERENCE, SELVA_FIELD_TYPE_REFERENCES, SELVA_FIELD_TYPE_WEAK_REFERENCE, and SELVA_FIELD_TYPE_WEAK_REFERENCES. */ + /** + * SELVA_FIELD_TYPE_REFERENCE, SELVA_FIELD_TYPE_REFERENCES, SELVA_FIELD_TYPE_WEAK_REFERENCE, and SELVA_FIELD_TYPE_WEAK_REFERENCES. + */ + struct EdgeFieldConstraint edge_constraint; + /** + * SELVA_FIELD_TYPE_MICRO_BUFFER. + */ struct { - uint32_t default_off; /*!< Offset to the default in the raw schema buffer. */ uint16_t len; /*!< Size of the smb. */ - } smb; /*!< SELVA_FIELD_TYPE_MICRO_BUFFER */ - size_t alias_index; /*!< Index in aliases for SELVA_FIELD_TYPE_ALIAS and SELVA_FIELD_TYPE_ALIASES. */ + } smb; + /** + * SELVA_FIELD_TYPE_ALIAS. + */ + struct { + size_t index; /*!< Index in aliases. */ + } alias; + /** + * SELVA_FIELD_TYPE_COLVEC. + */ struct { uint16_t vec_len; /*!< Length of a single vector. */ uint16_t comp_size; /*!< Component size in the vector. */ - uint32_t default_off; /*!< Offset to the default value in te->schema_buf. */ field_t index; /*!< Index in te->col_fields.colvec.v. */ - } colvec; /*!< SELVA_FIELD_TYPE_COLVEC */ + } colvec; }; } __designated_init; diff --git a/clibs/lib/selva/alias.c b/clibs/lib/selva/alias.c index a1053d2725..1197ef1a4f 100644 --- a/clibs/lib/selva/alias.c +++ b/clibs/lib/selva/alias.c @@ -20,9 +20,9 @@ void selva_init_aliases(struct SelvaTypeEntry *type) const struct SelvaFieldSchema *fs = &fields_schema->field_schemas[i]; if (fs->type == SELVA_FIELD_TYPE_ALIAS) { - struct SelvaAliases *field_aliases = &type->aliases[fs->alias_index]; + struct SelvaAliases *field_aliases = &type->aliases[fs->alias.index]; - assert(fs->alias_index < type->ns.nr_alias_fields); + assert(fs->alias.index < type->ns.nr_alias_fields); field_aliases->field = fs->field; field_aliases->nr_aliases = 0; RB_INIT(&field_aliases->alias_by_name); diff --git a/clibs/lib/selva/colvec.c b/clibs/lib/selva/colvec.c index f21d4021d2..eac7e00e75 100644 --- a/clibs/lib/selva/colvec.c +++ b/clibs/lib/selva/colvec.c @@ -108,9 +108,9 @@ void colvec_init_node(struct SelvaTypeEntry *te, struct SelvaNode *node) assert(fs->type == SELVA_FIELD_TYPE_COLVEC); void *vec = slab + colvec_slab_off(selva_get_block_capacity(te), colvec->vec_size, node->node_id); - if (fs->colvec.default_off > 0) { + if (fs->default_off > 0) { const uint8_t *schema_buf = te->schema_buf; - const void *default_vec = schema_buf + fs->colvec.default_off; + const void *default_vec = schema_buf + fs->default_off; memcpy(vec, default_vec, colvec->vec_size); } else { memset(vec, 0, colvec->vec_size); diff --git a/clibs/lib/selva/fields.c b/clibs/lib/selva/fields.c index c1c3b91489..65657d9842 100644 --- a/clibs/lib/selva/fields.c +++ b/clibs/lib/selva/fields.c @@ -2017,8 +2017,8 @@ static void selva_fields_init_defaults(struct SelvaTypeEntry *te, struct SelvaFi for (size_t i = 0; i < schema->nr_fixed_fields; i++) { auto fs = get_fs_by_fields_schema_field(schema, i); if (fs->type == SELVA_FIELD_TYPE_STRING) { - if (fs->string.default_off > 0) { - const void *default_str = schema_buf + fs->string.default_off; + if (fs->default_off > 0) { + const void *default_str = schema_buf + fs->default_off; size_t default_len = fs->string.default_len; struct SelvaFieldInfo *nfo; int err; @@ -2032,7 +2032,7 @@ static void selva_fields_init_defaults(struct SelvaTypeEntry *te, struct SelvaFi } } else if (fs->type == SELVA_FIELD_TYPE_TEXT) { const size_t nr_defaults = fs->text.nr_defaults; - size_t off = fs->text.defaults_off; + size_t off = fs->default_off; if (nr_defaults > 0 && off > 0) { struct ensure_text_field tf; diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index aea9b8ac0a..eecceeb5b5 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -71,9 +71,9 @@ static int type2fs_micro_buffer(struct schemabuf_parser_ctx *ctx, struct SelvaFi *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_MICRO_BUFFER, + .default_off = 0, .smb = { .len = head.len, - .default_off = 0, }, }; @@ -83,7 +83,7 @@ static int type2fs_micro_buffer(struct schemabuf_parser_ctx *ctx, struct SelvaFi } /* * Default is copied straight from the schema buffer. */ - fs->smb.default_off = calc_default_off(ctx, off); + fs->default_off = calc_default_off(ctx, off); off += head.len; } @@ -126,7 +126,7 @@ static int type2fs_string(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc } /* default is copied straight from the schema buffer. */ - fs->string.default_off = calc_default_off(ctx, off); + fs->default_off = calc_default_off(ctx, off); off += head.default_len; } @@ -156,7 +156,7 @@ static int type2fs_text(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSche fs->text.nr_defaults = head.nr_defaults; if (head.nr_defaults > 0) { /* has defaults */ - fs->text.defaults_off = (uint32_t)((ptrdiff_t)(ctx->buf - ctx->schema_buf) + off); + fs->default_off = (uint32_t)((ptrdiff_t)(ctx->buf - ctx->schema_buf) + off); /* * Iterate over the defaults and skip them. @@ -231,7 +231,9 @@ static int type2fs_alias(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSch *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_ALIAS, - .alias_index = ctx->alias_index++, + .alias = { + .index = ctx->alias_index++, + }, }; return 1; @@ -256,11 +258,11 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_COLVEC, + .default_off = (spec.has_default) ? calc_default_off(ctx, sizeof(spec)) : 0, .colvec = { .vec_len = spec.vec_len, .comp_size = spec.comp_size, .index = ctx->colvec_index++, - .default_off = (spec.has_default) ? calc_default_off(ctx, sizeof(spec)) : 0, }, }; @@ -378,9 +380,10 @@ static bool has_defaults(struct SelvaFieldsSchema *schema) for (size_t i = 0; i < nr_fixed_fields; i++) { const struct SelvaFieldSchema *fs = get_fs_by_fields_schema_field(schema, i); - if ((fs->type == SELVA_FIELD_TYPE_MICRO_BUFFER && fs->smb.default_off > 0) || - (fs->type == SELVA_FIELD_TYPE_STRING && fs->string.default_off > 0) || - (fs->type == SELVA_FIELD_TYPE_TEXT && fs->string.default_off > 0)) { + if ((fs->type == SELVA_FIELD_TYPE_MICRO_BUFFER || + fs->type == SELVA_FIELD_TYPE_STRING || + fs->type == SELVA_FIELD_TYPE_TEXT) && + fs->default_off > 0) { return true; } } @@ -404,12 +407,12 @@ static void make_fixed_fields_template(struct SelvaFieldsSchema *schema, const u const struct SelvaFieldSchema *fs = get_fs_by_fields_schema_field(schema, i); void *field_data = fixed_data_buf + (nfo[i].off << SELVA_FIELDS_OFF); - if (fs->type == SELVA_FIELD_TYPE_MICRO_BUFFER && fs->smb.default_off > 0) { - memcpy(field_data, schema_buf + fs->smb.default_off, fs->smb.len); - } else if (fs->type == SELVA_FIELD_TYPE_STRING && fs->string.default_off > 0) { + if (fs->type == SELVA_FIELD_TYPE_MICRO_BUFFER && fs->default_off > 0) { + memcpy(field_data, schema_buf + fs->default_off, fs->smb.len); + } else if (fs->type == SELVA_FIELD_TYPE_STRING && fs->default_off > 0) { if (fs->string.fixed_len > 0) { /* Fixed string needs to be copied here. */ struct selva_string *s = (struct selva_string *)field_data; - const void *default_str = schema_buf + fs->string.default_off; + const void *default_str = schema_buf + fs->default_off; size_t default_len = fs->string.default_len; int err; From a79da5fa22282d2369eb9b5f0ab26d63126ab9af Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Thu, 19 Feb 2026 11:30:16 -0300 Subject: [PATCH 346/449] lt x leq test --- test/filter/filter.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/filter/filter.ts b/test/filter/filter.ts index 0ad56ee3c3..f88bde75a8 100644 --- a/test/filter/filter.ts +++ b/test/filter/filter.ts @@ -913,3 +913,31 @@ await test.skip('includes', async (t) => { ), ) }) + +await test('lt x leq', async (t) => { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => t.backup(db)) + + await db.setSchema({ + types: { + bucket: { + red: 'uint8', + blue: 'uint8', + }, + }, + }) + + db.create('bucket', { + red: 1, + blue: 3, + }) + db.create('bucket', { + red: 4, + blue: 6, + }) + const b = await db.query('bucket').filter('red', '<', 4).get() + equal(b.toObject().length, 1, 'lt must be different than leq') +}) From 5cf6f82bae5f92c1eab43a419593f24afab1f757 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Thu, 19 Feb 2026 18:23:44 +0100 Subject: [PATCH 347/449] fix --- native/query/multiple.zig | 3 --- test/query-ast/include.ts | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index 5a5447d2f6..a283621326 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -281,7 +281,6 @@ pub fn references( var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); }, - .sort => { var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); @@ -292,7 +291,6 @@ pub fn references( nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - .filter => { var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); @@ -311,7 +309,6 @@ pub fn references( nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, - .edge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 94f17815a0..b170665f05 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -176,6 +176,8 @@ await test('include', async (t) => { }, } + console.dir(ast, { depth: 100 }) + const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) debugBuffer(ctx.query) @@ -191,7 +193,7 @@ await test('include', async (t) => { queries.push(x) } - await perf( + await perf.skip( async () => { const q: any = [] for (let i = 0; i < 10; i++) { @@ -217,8 +219,8 @@ await test('include', async (t) => { // RETURN NULL FOR UNDEFINED - // console.log( - // JSON.stringify(obj).length, - // readSchemaBuf.byteLength + result.byteLength, - // ) + console.log( + // JSON.stringify(obj).length, + result.byteLength, + ) }) From 9a2b0acfbe050e2cb16a666a2a17f6fd3185b89a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 19 Feb 2026 18:24:28 +0100 Subject: [PATCH 348/449] Vector type fixes --- src/db-query/ast/readSchema.ts | 13 ++++++++----- src/protocol/db-read/prop.ts | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/db-query/ast/readSchema.ts b/src/db-query/ast/readSchema.ts index 0ea0cb831a..83c620a9b2 100644 --- a/src/db-query/ast/readSchema.ts +++ b/src/db-query/ast/readSchema.ts @@ -6,7 +6,7 @@ import { } from '../../protocol/index.js' import { SchemaOut } from '../../schema.js' import { PropDef } from '../../schema/defs/index.js' -import { LangCode, PropType } from '../../zigTsExports.js' +import { LangCode, PropType, VectorBaseType } from '../../zigTsExports.js' import { Include } from './ast.js' export const readSchema = (type?: ReaderSchemaEnum): ReaderSchema => { @@ -64,10 +64,13 @@ export const readPropDef = ( {}, ) } - // if (p.type === PropType.vector || p.type === PropType.colVec) { - // readerPropDef.vectorBaseType = p.vectorBaseType - // readerPropDef.len = p.len - // } + if (p.type === PropType.vector || p.type === PropType.colVec) { + // TODO Do something so that this works without ignore + // @ts-ignore + readerPropDef.vectorBaseType = VectorBaseType[p.schema.baseType] + // @ts-ignore + readerPropDef.len = p.schema.size + } // if (p.type === PropType.cardinality) { // readerPropDef.cardinalityMode = p.cardinalityMode // readerPropDef.cardinalityPrecision = p.cardinalityPrecision diff --git a/src/protocol/db-read/prop.ts b/src/protocol/db-read/prop.ts index e26bfea113..0f8853e02e 100644 --- a/src/protocol/db-read/prop.ts +++ b/src/protocol/db-read/prop.ts @@ -4,6 +4,7 @@ import { addProp, addLangProp } from './addProps.js' import { readString } from './string.js' import { readVector } from './vector.js' import { PropType } from '../../zigTsExports.js' +import { VECTOR_BASE_TYPE_SIZE_MAP } from '../../schema.js' const readStringProp = ( prop: ReaderPropDef, @@ -83,7 +84,8 @@ export const readProp = ( prop.typeIndex === PropType.vector || prop.typeIndex === PropType.colVec ) { - const tmp = result.slice(i, i + prop.len!) // maybe align? + const vecSize = prop.len! * VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] + const tmp = result.slice(i, i + vecSize) // maybe align? addProp(prop, readVector(prop, tmp), item) i += prop.len! } From e28c6ed07d00a6d486ef8f532c752e95a61e2660 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Thu, 19 Feb 2026 17:18:04 -0300 Subject: [PATCH 349/449] finishing agg references filter --- native/query/aggregates/aggregates.zig | 2 +- native/query/aggregates/group.zig | 2 +- native/query/aggregates/references.zig | 17 ++++++++++++----- package-lock.json | 4 ++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/native/query/aggregates/aggregates.zig b/native/query/aggregates/aggregates.zig index b8424ae6eb..fcbd1aa320 100644 --- a/native/query/aggregates/aggregates.zig +++ b/native/query/aggregates/aggregates.zig @@ -27,7 +27,7 @@ pub const AggCtx = struct { pub fn iterator( aggCtx: *AggCtx, it: anytype, - comptime hasFilter: bool, + hasFilter: bool, filterBuf: []u8, aggDefs: []u8, accumulatorProp: []u8, diff --git a/native/query/aggregates/group.zig b/native/query/aggregates/group.zig index 8ec33787d8..64e382b408 100644 --- a/native/query/aggregates/group.zig +++ b/native/query/aggregates/group.zig @@ -18,7 +18,7 @@ pub fn iterator( aggCtx: *Aggregates.AggCtx, groupByHashMap: *GroupByHashMap, it: anytype, - comptime hasFilter: bool, + hasFilter: bool, filterBuf: []u8, aggDefs: []u8, ) usize { diff --git a/native/query/aggregates/references.zig b/native/query/aggregates/references.zig index 56cc56cf86..04d77bf98b 100644 --- a/native/query/aggregates/references.zig +++ b/native/query/aggregates/references.zig @@ -13,6 +13,7 @@ const GroupByHashMap = @import("./hashMap.zig").GroupByHashMap; const errors = @import("../../errors.zig"); const accumulate = Aggregates.accumulate; const std = @import("std"); +const Filter = @import("../filter/filter.zig"); pub inline fn aggregateRefsProps( ctx: *Query.QueryCtx, @@ -21,6 +22,8 @@ pub inline fn aggregateRefsProps( fromType: Selva.Type, i: *usize, ) !void { + var filter: []u8 = undefined; + const header = utils.readNext(t.AggRefsHeader, q, i); // utils.debugPrint("aggregateRefsProps header: {any}\n", .{header}); @@ -28,10 +31,14 @@ pub inline fn aggregateRefsProps( @memset(accumulatorProp, 0); defer ctx.db.allocator.free(accumulatorProp); - // filter - var it = try References.iterator(false, false, ctx.db, from, header.targetProp, fromType); + const hasFilter = header.filterSize > 0; + if (hasFilter) { + filter = utils.sliceNext(header.filterSize, q, i); + try Filter.prepare(filter, ctx, it.dstType); + } + const hllAccumulator = Selva.c.selva_string_create(null, Selva.c.HLL_INIT_SIZE, Selva.c.SELVA_STRING_MUTABLE); defer Selva.c.selva_string_free(hllAccumulator); @@ -49,18 +56,18 @@ pub inline fn aggregateRefsProps( var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); defer groupByHashMap.deinit(); - _ = GroupBy.iterator(&aggCtx, &groupByHashMap, &it, false, undefined, q[i.*..]); // TODO: hllAcc + _ = GroupBy.iterator(&aggCtx, &groupByHashMap, &it, hasFilter, filter, q[i.*..]); try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); try ctx.thread.query.append(@as(u32, @intCast(aggCtx.totalResultsSize))); try GroupBy.finalizeRefsGroupResults(&aggCtx, &groupByHashMap, q[i.*..]); } else { - _ = try Aggregates.iterator(&aggCtx, &it, false, undefined, q[i.*..], accumulatorProp); // TODO: hllAcc + _ = try Aggregates.iterator(&aggCtx, &it, hasFilter, filter, q[i.*..], accumulatorProp); try ctx.thread.query.append(@intFromEnum(t.ReadOp.aggregation)); try ctx.thread.query.append(header.targetProp); - try ctx.thread.query.append(@as(u32, header.resultsSize)); // MV: recheck + try ctx.thread.query.append(@as(u32, header.resultsSize)); try Aggregates.finalizeResults(&aggCtx, q[i.*..], accumulatorProp, 0); } } diff --git a/package-lock.json b/package-lock.json index 71baa502af..20b4d7c1f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,6 +174,10 @@ "@based/locale-x86-64-gnu": "*" } }, + "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { + "dev": true, + "optional": true + }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", From 95d7561bf60d1de1d8e107ef8b0b93e45d410396 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 08:19:32 +0100 Subject: [PATCH 350/449] Wrong size for float32 vectors --- src/schema/defs/props/vector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 6c6401713b..b514d029b6 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -20,7 +20,7 @@ const baseTypeSize: { [K in SchemaVector['baseType']]: number } = { uint16: 2, int32: 4, uint32: 4, - float32: 8, + float32: 4, float64: 8, } From 009898e529309aad0ddd0afc5f10e47c9557d5c4 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 08:33:26 +0100 Subject: [PATCH 351/449] Cleanup vector schema --- src/schema/defs/props/vector.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index b514d029b6..470b76ea66 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -1,4 +1,4 @@ -import type { SchemaVector } from '../../../schema.js' +import { VECTOR_BASE_TYPE_SIZE_MAP, type SchemaVector } from '../../../schema.js' import { PropType, type LangCodeEnum, @@ -7,27 +7,17 @@ import { PropTypeSelva, pushSelvaSchemaColvec, pushSelvaSchemaMicroBuffer, + VectorBaseType, } from '../../../zigTsExports.js' import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' import { isTypedArray } from 'util/types' -const baseTypeSize: { [K in SchemaVector['baseType']]: number } = { - int8: 1, - uint8: 1, - int16: 2, - uint16: 2, - int32: 4, - uint32: 4, - float32: 4, - float64: 8, -} - export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) - this.vectorSize = schema.size * baseTypeSize[schema.baseType] + this.vectorSize = schema.size * VECTOR_BASE_TYPE_SIZE_MAP[VectorBaseType[schema.baseType]] } vectorSize: number override type: PropTypeEnum = PropType.vector @@ -65,8 +55,8 @@ export const vector = class Vector extends BasePropDef { export const colvec = class ColVec extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) - this.compSize = baseTypeSize[schema.baseType] - this.vecLen = schema.size * this.compSize + this.compSize = VECTOR_BASE_TYPE_SIZE_MAP[VectorBaseType[schema.baseType]] + this.vecLen = schema.size } compSize: number vecLen: number @@ -85,7 +75,7 @@ export const colvec = class ColVec extends BasePropDef { this.validate(value) const v = new Uint8Array(value.buffer).subarray( 0, - Math.min(value.byteLength, this.vecLen), + Math.min(value.byteLength, this.vecLen * this.compSize), ) buf.set(v, buf.length) } From ef818c53b1cfb2dfef863036fee1413afafefeef Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 09:00:36 +0100 Subject: [PATCH 352/449] Only accept the selected type for vectors --- src/schema/defs/props/vector.ts | 16 ++++++++++++++++ test/modify/props/vector.ts | 20 +++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 470b76ea66..3440b01941 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -14,6 +14,17 @@ import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' import { isTypedArray } from 'util/types' +const t2t = { + 'int8': Int8Array, + 'uint8': Uint8Array, + 'int16': Int16Array, + 'uint16': Uint16Array, + 'int32': Int32Array, + 'uint32': Uint32Array, + 'float32': Float32Array, + 'float64': Float64Array, +} + export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) @@ -25,6 +36,11 @@ export const vector = class Vector extends BasePropDef { if (!isTypedArray(value)) { throw new Error('Not a typed array') } + // @ts-ignore + const t = t2t[this.schema.baseType] + if (!(value instanceof t)) { + throw new Error(`Not a ${t.name}`) + } } override pushValue( buf: AutoSizedUint8Array, diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index c611e83ffb..4ea202d9b2 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -1,8 +1,8 @@ -import { testDb } from '../../shared/index.js' +import { testDb, throws } from '../../shared/index.js' import test from '../../shared/test.js' import assert from 'node:assert' -await test.skip('modify vector', async (t) => { +await test('wrong type', async (t) => { const db = await testDb(t, { types: { thing: { @@ -11,7 +11,21 @@ await test.skip('modify vector', async (t) => { }, }) - const v1 = new Float64Array([1.1, 2.2, 3.3]) + throws(() => db.create('thing', { + vec: new Float64Array([1.1, 2.2, 3.3]), + })) +}) + +await test('modify vector', async (t) => { + const db = await testDb(t, { + types: { + thing: { + vec: { type: 'vector', size: 3, baseType: 'float32' }, + }, + }, + }) + + const v1 = new Float32Array([1.1, 2.2, 3.3]) const id1 = await db.create('thing', { vec: v1, }) From ede73b52799512041ed5f8ae1d758f1b6cf88037 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 09:41:01 +0100 Subject: [PATCH 353/449] prettier --- src/protocol/db-read/prop.ts | 2 +- src/schema/defs/props/vector.ts | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/protocol/db-read/prop.ts b/src/protocol/db-read/prop.ts index 0f8853e02e..cd0a0b35b4 100644 --- a/src/protocol/db-read/prop.ts +++ b/src/protocol/db-read/prop.ts @@ -84,7 +84,7 @@ export const readProp = ( prop.typeIndex === PropType.vector || prop.typeIndex === PropType.colVec ) { - const vecSize = prop.len! * VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] + const vecSize = prop.len! * VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] const tmp = result.slice(i, i + vecSize) // maybe align? addProp(prop, readVector(prop, tmp), item) i += prop.len! diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 3440b01941..42facf3533 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -1,4 +1,7 @@ -import { VECTOR_BASE_TYPE_SIZE_MAP, type SchemaVector } from '../../../schema.js' +import { + VECTOR_BASE_TYPE_SIZE_MAP, + type SchemaVector, +} from '../../../schema.js' import { PropType, type LangCodeEnum, @@ -15,20 +18,21 @@ import type { TypeDef } from '../index.js' import { isTypedArray } from 'util/types' const t2t = { - 'int8': Int8Array, - 'uint8': Uint8Array, - 'int16': Int16Array, - 'uint16': Uint16Array, - 'int32': Int32Array, - 'uint32': Uint32Array, - 'float32': Float32Array, - 'float64': Float64Array, + int8: Int8Array, + uint8: Uint8Array, + int16: Int16Array, + uint16: Uint16Array, + int32: Int32Array, + uint32: Uint32Array, + float32: Float32Array, + float64: Float64Array, } export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) - this.vectorSize = schema.size * VECTOR_BASE_TYPE_SIZE_MAP[VectorBaseType[schema.baseType]] + this.vectorSize = + schema.size * VECTOR_BASE_TYPE_SIZE_MAP[VectorBaseType[schema.baseType]] } vectorSize: number override type: PropTypeEnum = PropType.vector From 108d6333e79c93d62f544fe20bc8b9286aa8d0cc Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 10:34:07 +0100 Subject: [PATCH 354/449] Wrong increment --- src/protocol/db-read/prop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/db-read/prop.ts b/src/protocol/db-read/prop.ts index cd0a0b35b4..385db3db71 100644 --- a/src/protocol/db-read/prop.ts +++ b/src/protocol/db-read/prop.ts @@ -87,7 +87,7 @@ export const readProp = ( const vecSize = prop.len! * VECTOR_BASE_TYPE_SIZE_MAP[prop.vectorBaseType!] const tmp = result.slice(i, i + vecSize) // maybe align? addProp(prop, readVector(prop, tmp), item) - i += prop.len! + i += vecSize } return i } From e352aef58eb3122462d34c47760061f492618cdc Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 10:34:18 +0100 Subject: [PATCH 355/449] Fix vector test --- test/modify/props/vector.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 4ea202d9b2..03588a25ec 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -1,4 +1,4 @@ -import { testDb, throws } from '../../shared/index.js' +import { deepEqual, testDb, throws } from '../../shared/index.js' import test from '../../shared/test.js' import assert from 'node:assert' @@ -42,7 +42,7 @@ await test('modify vector', async (t) => { assert(Math.abs(vecArr[1] - v1[1]) < 0.0001) assert(Math.abs(vecArr[2] - v1[2]) < 0.0001) - const v2 = new Float64Array([4.4, 5.5, 6.6]) + const v2 = new Float32Array([4.4, 5.5, 6.6]) await db.update('thing', id1, { vec: v2, }) @@ -58,8 +58,7 @@ await test('modify vector', async (t) => { await db.update('thing', id1, { vec: null, }) - const res3 = await db.query2('thing', id1).get() - assert(res3.vec === undefined) + deepEqual(await db.query2('thing', id1).get(), { id: 1, vec: new Float32Array([ 0, 0, 0 ]) }) }) await test.skip('modify colvec', async (t) => { From 545a90506cbb98550ddbaf8d47f2a71c9fb94208 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 10:47:20 +0100 Subject: [PATCH 356/449] Test vector on edge --- test/modify/props/vector.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 03588a25ec..869fd939a9 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -11,9 +11,11 @@ await test('wrong type', async (t) => { }, }) - throws(() => db.create('thing', { - vec: new Float64Array([1.1, 2.2, 3.3]), - })) + throws(() => + db.create('thing', { + vec: new Float64Array([1.1, 2.2, 3.3]), + }), + ) }) await test('modify vector', async (t) => { @@ -58,7 +60,10 @@ await test('modify vector', async (t) => { await db.update('thing', id1, { vec: null, }) - deepEqual(await db.query2('thing', id1).get(), { id: 1, vec: new Float32Array([ 0, 0, 0 ]) }) + deepEqual(await db.query2('thing', id1).get(), { + id: 1, + vec: new Float32Array([0, 0, 0]), + }) }) await test.skip('modify colvec', async (t) => { @@ -107,7 +112,7 @@ await test.skip('modify colvec', async (t) => { assert(res3.vec === undefined) }) -await test.skip('modify vector on edge', async (t) => { +await test('modify vector on edge', async (t) => { const db = await testDb(t, { types: { thing: { @@ -123,7 +128,7 @@ await test.skip('modify vector on edge', async (t) => { }, }) - const v1 = new Float64Array([1.1, 2.2, 3.3]) + const v1 = new Float32Array([1.1, 2.2, 3.3]) const targetId = await db.create('thing', { vec: v1 }) const id1 = await db.create('holder', { toThing: { @@ -143,7 +148,7 @@ await test.skip('modify vector on edge', async (t) => { assert.fail('toThing not found') } - const v2 = new Float64Array([4.4, 5.5, 6.6]) + const v2 = new Float32Array([4.4, 5.5, 6.6]) await db.update('holder', id1, { toThing: { id: targetId, From 97ef4c2d8b955eb3e3c5647326c0a2359645dcf9 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 11:10:52 +0100 Subject: [PATCH 357/449] Common validate --- src/schema/defs/props/vector.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 42facf3533..69425ffc97 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -28,6 +28,17 @@ const t2t = { float64: Float64Array, } +function validateVector(value: unknown): asserts value is Uint8Array { + if (!isTypedArray(value)) { + throw new Error('Not a typed array') + } + // @ts-ignore + const t = t2t[this.schema.baseType] + if (!(value instanceof t)) { + throw new Error(`Not a ${t.name}`) + } +} + export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { super(schema, path, typeDef) @@ -37,14 +48,7 @@ export const vector = class Vector extends BasePropDef { vectorSize: number override type: PropTypeEnum = PropType.vector override validate(value: unknown): asserts value is Uint8Array { - if (!isTypedArray(value)) { - throw new Error('Not a typed array') - } - // @ts-ignore - const t = t2t[this.schema.baseType] - if (!(value instanceof t)) { - throw new Error(`Not a ${t.name}`) - } + validateVector.call(this, value) } override pushValue( buf: AutoSizedUint8Array, @@ -82,9 +86,7 @@ export const colvec = class ColVec extends BasePropDef { vecLen: number override type = PropType.colVec override validate(value: unknown): asserts value is Uint8Array { - if (!isTypedArray(value)) { - throw new Error('Not a typed array') - } + validateVector.call(this, value) } override pushValue( buf: AutoSizedUint8Array, From 85a24ec9df14a4ba5356b8b5a4ebe2f92ce20e97 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 11:54:47 +0100 Subject: [PATCH 358/449] Fix set/clear colvec on modify --- clibs/include/selva/colvec.h | 3 +++ clibs/lib/selva/colvec.c | 11 +++++++++++ native/modify/modify.zig | 7 +++++++ native/selva/fields.zig | 32 +++++++++++++++++++++++++++++++- test/modify/props/vector.ts | 14 ++++++++++---- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/clibs/include/selva/colvec.h b/clibs/include/selva/colvec.h index 221e7ae05f..9112923338 100644 --- a/clibs/include/selva/colvec.h +++ b/clibs/include/selva/colvec.h @@ -67,5 +67,8 @@ void *colvec_get_vec(struct SelvaTypeEntry *te, node_id_t node_id, const struct SELVA_EXPORT void colvec_set_vec(struct SelvaTypeEntry *te, node_id_t node_id, const struct SelvaFieldSchema *fs, const void *vec); +SELVA_EXPORT +void colvec_clear_vec(struct SelvaTypeEntry *te, node_id_t node_id, const struct SelvaFieldSchema *fs); + SELVA_EXPORT int colvec_foreach(struct SelvaTypeEntry *te, const struct SelvaFieldSchema *fs, node_id_t start, uint32_t len, void (*cb)(node_id_t node_id, void *vec, void *arg), void *arg); diff --git a/clibs/lib/selva/colvec.c b/clibs/lib/selva/colvec.c index eac7e00e75..e06e5889bb 100644 --- a/clibs/lib/selva/colvec.c +++ b/clibs/lib/selva/colvec.c @@ -149,6 +149,17 @@ void colvec_set_vec(struct SelvaTypeEntry *te, node_id_t node_id, const struct S memcpy(dst, vec, colvec->vec_size); } +void colvec_clear_vec(struct SelvaTypeEntry *te, node_id_t node_id, const struct SelvaFieldSchema *fs) +{ + assert(fs->type == SELVA_FIELD_TYPE_COLVEC); + + struct SelvaColvec *colvec = &te->col_fields.colvec[fs->colvec.index]; + uint8_t *slab = (uint8_t *)colvec->v[selva_node_id2block_i2(te, node_id)]; + void *dst = slab + colvec_slab_off(selva_get_block_capacity(te), colvec->vec_size, node_id); + + memset(dst, 0, colvec->vec_size); +} + int colvec_foreach(struct SelvaTypeEntry *te, const struct SelvaFieldSchema *fs, node_id_t start, uint32_t len, void (*cb)(node_id_t node_id, void *vec, void *arg), void *arg) { struct SelvaColvec *colvec = colvec_get(te, fs); diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 189c3e4766..3f4fe7c5b0 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -223,6 +223,13 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 k += references.size; } }, + .colVec => { + if (prop.size == 0) { + Fields.clearColvec(typeEntry, node, propSchema); + continue; + } + Fields.setColvec(typeEntry, node, propSchema, value); + }, else => { if (prop.size == 0) { try Fields.deleteField(db, node, propSchema); diff --git a/native/selva/fields.zig b/native/selva/fields.zig index b5f708a36a..c14bc4078d 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -106,7 +106,18 @@ pub inline fn setMicroBuffer(node: Node.Node, fieldSchema: Schema.FieldSchema, v )); } -pub inline fn setColvec(te: Node.Type, nodeId: selva.c.node_id_t, fieldSchema: Schema.FieldSchema, vec: []u8) void { +pub inline fn setColvec(te: Node.Type, node: anytype, fieldSchema: Schema.FieldSchema, vec: []u8) void { + var nodeId: selva.c.node_id_t = undefined; + + if (comptime @TypeOf(node) == selva.c.node_id_t) { + nodeId = node; + } else if (comptime @TypeOf(node) == Node.Node) { + nodeId = Node.getNodeId(node); + } else { + @compileLog("Invalid type: ", @TypeOf(node)); + @compileError("Invalid type"); + } + selva.c.colvec_set_vec( te, nodeId, @@ -115,6 +126,25 @@ pub inline fn setColvec(te: Node.Type, nodeId: selva.c.node_id_t, fieldSchema: S ); } +pub inline fn clearColvec(te: Node.Type, node: anytype, fieldSchema: Schema.FieldSchema) void { + var nodeId: selva.c.node_id_t = undefined; + + if (comptime @TypeOf(node) == selva.c.node_id_t) { + nodeId = node; + } else if (comptime @TypeOf(node) == Node.Node) { + nodeId = Node.getNodeId(node); + } else { + @compileLog("Invalid type: ", @TypeOf(node)); + @compileError("Invalid type"); + } + + selva.c.colvec_clear_vec( + te, + nodeId, + fieldSchema, + ); +} + // TODO This is now hll specific but we might want to change it. pub fn ensurePropTypeString( node: Node.Node, diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 869fd939a9..6e786fb704 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -64,15 +64,22 @@ await test('modify vector', async (t) => { id: 1, vec: new Float32Array([0, 0, 0]), }) + + // Undefined + const id3 = await db.create('thing', {}) + deepEqual(await db.query2('thing', id1).get(), { + id: 1, + vec: new Float32Array([0, 0, 0]), + }) }) -await test.skip('modify colvec', async (t) => { +await test('modify colvec', async (t) => { const db = await testDb(t, { types: { thing: { insertOnly: true, props: { - vec: { type: 'colvec', size: 3, baseType: 'float32' }, + vec: { type: 'colvec', size: 3, baseType: 'float64' }, }, }, }, @@ -108,8 +115,7 @@ await test.skip('modify colvec', async (t) => { await db.update('thing', id1, { vec: null, }) - const res3 = await db.query2('thing', id1).get() - assert(res3.vec === undefined) + deepEqual(await db.query2('thing', id1).get(), { id: 1, vec: new Float64Array([0, 0, 0]) }) }) await test('modify vector on edge', async (t) => { From e5c1454931c0d598d1ba1ebff2ac5875b49a7bc5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 12:00:24 +0100 Subject: [PATCH 359/449] More nice typing --- native/selva/fields.zig | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/native/selva/fields.zig b/native/selva/fields.zig index c14bc4078d..3a8d4c4031 100644 --- a/native/selva/fields.zig +++ b/native/selva/fields.zig @@ -19,6 +19,17 @@ const emptyArray: []const [16]u8 = emptySlice; extern "c" const selva_string: opaque {}; +inline fn toNodeId(node: anytype) selva.c.node_id_t { + if (comptime @TypeOf(node) == selva.c.node_id_t) { + return node; + } else if (comptime @TypeOf(node) == Node.Node) { + return Node.getNodeId(node); + } else { + @compileLog("Invalid type: ", @TypeOf(node)); + @compileError("Invalid type"); + } +} + pub fn ensureCardinality(node: Node.Node, fieldSchema: Schema.FieldSchema, hllPrecision: u8, hllMode: bool) *selva.c.struct_selva_string { var data = selva.c.selva_fields_get_selva_string(node, fieldSchema); if (data == null) { @@ -107,16 +118,7 @@ pub inline fn setMicroBuffer(node: Node.Node, fieldSchema: Schema.FieldSchema, v } pub inline fn setColvec(te: Node.Type, node: anytype, fieldSchema: Schema.FieldSchema, vec: []u8) void { - var nodeId: selva.c.node_id_t = undefined; - - if (comptime @TypeOf(node) == selva.c.node_id_t) { - nodeId = node; - } else if (comptime @TypeOf(node) == Node.Node) { - nodeId = Node.getNodeId(node); - } else { - @compileLog("Invalid type: ", @TypeOf(node)); - @compileError("Invalid type"); - } + const nodeId = toNodeId(node); selva.c.colvec_set_vec( te, @@ -127,16 +129,7 @@ pub inline fn setColvec(te: Node.Type, node: anytype, fieldSchema: Schema.FieldS } pub inline fn clearColvec(te: Node.Type, node: anytype, fieldSchema: Schema.FieldSchema) void { - var nodeId: selva.c.node_id_t = undefined; - - if (comptime @TypeOf(node) == selva.c.node_id_t) { - nodeId = node; - } else if (comptime @TypeOf(node) == Node.Node) { - nodeId = Node.getNodeId(node); - } else { - @compileLog("Invalid type: ", @TypeOf(node)); - @compileError("Invalid type"); - } + const nodeId = toNodeId(node); selva.c.colvec_clear_vec( te, @@ -339,9 +332,10 @@ pub fn getAliasByName(typeEntry: Node.Type, field: u8, aliasName: []u8) ?Node.No return res.node; } -pub fn getAliasByNode(typeEntry: Node.Type, node: Node.Node, field: u8) ![]const u8 { +pub fn getAliasByNode(typeEntry: Node.Type, node: anytype, field: u8) ![]const u8 { if (selva.c.selva_get_aliases(typeEntry, field)) |aliases| { - if (selva.c.selva_get_alias_by_dest(aliases, Node.getNodeId(node))) |alias| { + const nodeId = toNodeId(node); + if (selva.c.selva_get_alias_by_dest(aliases, nodeId)) |alias| { var len: usize = undefined; const name = selva.c.selva_get_alias_name(alias, &len); return name[0..len]; From 69d626d05e0714cf8575f309171aa44f12467ffa Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 14:24:51 +0100 Subject: [PATCH 360/449] defaults for vectors --- src/schema/defs/props/vector.ts | 18 ++++-------------- src/schema/schema/vector.ts | 21 +++++++++++++++++++-- test/modify/props/vector.ts | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 69425ffc97..cbc8d757d7 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -2,6 +2,7 @@ import { VECTOR_BASE_TYPE_SIZE_MAP, type SchemaVector, } from '../../../schema.js' +import { vectorBaseType2TypedArray } from '../../../schema/schema/vector.js' import { PropType, type LangCodeEnum, @@ -17,23 +18,11 @@ import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' import { isTypedArray } from 'util/types' -const t2t = { - int8: Int8Array, - uint8: Uint8Array, - int16: Int16Array, - uint16: Uint16Array, - int32: Int32Array, - uint32: Uint32Array, - float32: Float32Array, - float64: Float64Array, -} - function validateVector(value: unknown): asserts value is Uint8Array { if (!isTypedArray(value)) { throw new Error('Not a typed array') } - // @ts-ignore - const t = t2t[this.schema.baseType] + const t = vectorBaseType2TypedArray[this.schema.baseType] if (!(value instanceof t)) { throw new Error(`Not a ${t.name}`) } @@ -106,7 +95,8 @@ export const colvec = class ColVec extends BasePropDef { type: PropTypeSelva.colVec, vecLen: this.vecLen, compSize: this.compSize, - hasDefault: 0, // TODO default + hasDefault: 0, + //hasDefault: this.schema.default, // TODO default }) } } diff --git a/src/schema/schema/vector.ts b/src/schema/schema/vector.ts index 356f64abae..e18db96f72 100644 --- a/src/schema/schema/vector.ts +++ b/src/schema/schema/vector.ts @@ -12,8 +12,21 @@ const vectorBaseTypes = [ 'float32', 'float64', ] as const +type VectorBaseType = (typeof vectorBaseTypes)[number] +export const vectorBaseType2TypedArray = { + int8: Int8Array, + uint8: Uint8Array, + int16: Int16Array, + uint16: Uint16Array, + int32: Int32Array, + uint32: Uint32Array, + float32: Float32Array, + float64: Float64Array, +} +export type VectorBaseType2TypedArray = typeof vectorBaseType2TypedArray + -export type SchemaVector = Base & { +export type SchemaVector = Base & { type: 'vector' | 'colvec' /** * Number of elements in the vector. @@ -23,7 +36,11 @@ export type SchemaVector = Base & { * Base type of the vector. * float64 == number */ - baseType: (typeof vectorBaseTypes)[number] + baseType: T + /** + * Default vector. + */ + default?: InstanceType } export const parseVector = (def: Record): SchemaVector => { diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 6e786fb704..826b6b2e2c 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -18,6 +18,24 @@ await test('wrong type', async (t) => { ) }) +await test('default', async (t) => { + const db = await testDb(t, { + types: { + thing: { + vec: { + type: 'vector', + size: 3, + baseType: 'float32', + default: new Float32Array([1, 2, 3]) + }, + }, + }, + }) + + const id1 = await db.create('thing', {}) + console.log(await db.query('thing', id1).get().inspect()) +}) + await test('modify vector', async (t) => { const db = await testDb(t, { types: { From f878a506c3873ab827afe46e80bb5b54638adc3d Mon Sep 17 00:00:00 2001 From: youzi Date: Fri, 20 Feb 2026 15:03:33 +0100 Subject: [PATCH 361/449] vector validation --- src/schema/def/validation.ts | 7 +-- src/schema/schema/base.ts | 3 +- src/schema/schema/vector.ts | 85 ++++++++++++++++++++++-------------- 3 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/schema/def/validation.ts b/src/schema/def/validation.ts index 6e4ed3d77e..847ce00939 100644 --- a/src/schema/def/validation.ts +++ b/src/schema/def/validation.ts @@ -5,6 +5,7 @@ import type { SchemaTimestamp } from '../schema/timestamp.js' import type { SchemaNumber } from '../schema/number.js' import type { SchemaEnum } from '../schema/enum.js' import { + isVector, MAX_ID, MIN_ID, type SchemaObject, @@ -361,11 +362,7 @@ export const VALIDATION_MAP: Record = { return true }, [PropType.vector]: (value) => { - // Array should be supported - if (!(value instanceof Float32Array)) { - return false - } - return true + return isVector(value) }, // @ts-ignore [PropType.text]: null, diff --git a/src/schema/schema/base.ts b/src/schema/schema/base.ts index a219967a28..77f2af847e 100644 --- a/src/schema/schema/base.ts +++ b/src/schema/schema/base.ts @@ -26,6 +26,7 @@ const isValidation = (v: unknown): v is Validation => isFunction(v) export const parseBase = >( def: Record, result: T, + skipValidation = false, ): T => { assert( def.required === undefined || isBoolean(def.required), @@ -53,7 +54,7 @@ export const parseBase = >( assertExpectedProps(result, def) - if ('default' in result && result.default !== undefined) { + if (!skipValidation && 'default' in result && result.default !== undefined) { // @ts-ignore const validation = getValidator(result) // @ts-ignore diff --git a/src/schema/schema/vector.ts b/src/schema/schema/vector.ts index e18db96f72..4a6fecce97 100644 --- a/src/schema/schema/vector.ts +++ b/src/schema/schema/vector.ts @@ -1,18 +1,12 @@ import { assert, isNatural, isString } from './shared.js' import { parseBase, type Base } from './base.js' +import { VectorBaseType } from '../../zigTsExports.js' -// TODO This should probably come from zigTsExports.ts -const vectorBaseTypes = [ - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'float32', - 'float64', -] as const -type VectorBaseType = (typeof vectorBaseTypes)[number] +const vectorBaseTypes = Object.keys( + VectorBaseType, +) as (keyof typeof VectorBaseType)[] + +export type VectorBaseTypeStr = keyof typeof VectorBaseType export const vectorBaseType2TypedArray = { int8: Int8Array, uint8: Uint8Array, @@ -25,22 +19,43 @@ export const vectorBaseType2TypedArray = { } export type VectorBaseType2TypedArray = typeof vectorBaseType2TypedArray +export type SchemaVector = + Base & { + type: 'vector' | 'colvec' + /** + * Number of elements in the vector. + */ + size: number + /** + * Base type of the vector. + * float64 == number + */ + baseType: T + /** + * Default vector. + */ + default?: InstanceType + } -export type SchemaVector = Base & { - type: 'vector' | 'colvec' - /** - * Number of elements in the vector. - */ - size: number - /** - * Base type of the vector. - * float64 == number - */ - baseType: T - /** - * Default vector. - */ - default?: InstanceType +export function isVector( + value: unknown, +): value is + | Int8Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array { + for (const k in vectorBaseType2TypedArray) { + if ( + value instanceof + vectorBaseType2TypedArray[k as keyof typeof vectorBaseType2TypedArray] + ) + return true + } + return false } export const parseVector = (def: Record): SchemaVector => { @@ -53,10 +68,16 @@ export const parseVector = (def: Record): SchemaVector => { isString(def.baseType) && vectorBaseTypes.includes(def.baseType as any), 'Invalid baseType', ) + assert(def.default === undefined || isVector(def.default), 'Invalid default') - return parseBase(def, { - type: def.type, - size: def.size, - baseType: def.baseType as SchemaVector['baseType'], - }) + return parseBase( + def, + { + type: def.type, + size: def.size, + baseType: def.baseType as SchemaVector['baseType'], + default: def.default, + }, + true, + ) } From affc264a04de348079c8484255469fa7502a2236 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 16:33:28 +0100 Subject: [PATCH 362/449] Handle empty smb field --- native/query/include/include.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/native/query/include/include.zig b/native/query/include/include.zig index 03edea712d..2659c1d2f5 100644 --- a/native/query/include/include.zig +++ b/native/query/include/include.zig @@ -136,7 +136,12 @@ pub fn include( .microBuffer, .vector, .colVec => { // Fixed size try ctx.thread.query.append(header.prop); - try ctx.thread.query.append(value); + if (value.len == 0) { + const fs = try Schema.getFieldSchema(typeEntry, header.prop); + _ = try ctx.thread.query.reserve(fs.unnamed_0.smb.len); + } else { + try ctx.thread.query.append(value); + } }, else => { try append.default(ctx.thread, header.prop, value); From 9736b37464bd71b223fe196c0c2b6ebc712cf173 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Fri, 20 Feb 2026 16:48:15 +0100 Subject: [PATCH 363/449] Default value handling for vector --- src/schema/defs/props/vector.ts | 7 ++++++- test/modify/props/vector.ts | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index cbc8d757d7..7f13aefe9d 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -53,11 +53,16 @@ export const vector = class Vector extends BasePropDef { buf.set(v, buf.length) } override pushSelvaSchema(buf: AutoSizedUint8Array) { + const defaultValue = this.schema['default'] pushSelvaSchemaMicroBuffer(buf, { type: PropTypeSelva.microBuffer, len: this.vectorSize, - hasDefault: 0, // TODO default + hasDefault: ~~!!defaultValue, }) + if (defaultValue) { + const v = new Uint8Array(defaultValue.buffer, 0, this.vectorSize) + buf.set(v, buf.length) + } } } diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 826b6b2e2c..69a04282f0 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -26,14 +26,15 @@ await test('default', async (t) => { type: 'vector', size: 3, baseType: 'float32', - default: new Float32Array([1, 2, 3]) + default: new Float32Array([1, 2.5, 3]) }, }, }, }) + // TODO Test also wrong size for default const id1 = await db.create('thing', {}) - console.log(await db.query('thing', id1).get().inspect()) + deepEqual(await db.query2('thing', id1).get(), { id: id1, vec: new Float32Array([1, 2.5, 3]) }) }) await test('modify vector', async (t) => { From fbae3e2ecdef5ef39463928925ad2b11439ca917 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 07:52:46 +0100 Subject: [PATCH 364/449] more filter --- src/db-query/ast/filter/filter.ts | 11 +++++++++ test/query-ast/aggregates.ts | 2 +- test/query-ast/include.ts | 37 +++++++++++++++++++------------ 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index 36630c96eb..a673b7db95 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -20,8 +20,17 @@ type WalkCtx = { main: { prop: PropDef; ops: FilterOp[] }[] } +export const EdgeStrategy = { + noEdge: 0, + edgeOnly: 1, + mixed: 2, +} as const + +export type EdgeStrategyEnum = (typeof EdgeStrategy)[keyof typeof EdgeStrategy] + // Handle EDGES +// EDGE ONLY? const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { const { tree, main } = walkCtx @@ -88,8 +97,10 @@ const indexOf = ( // filter + EDGE +// EDGE ONLY? export const filter = ( ast: FilterAst, + edgeStrategy: EdgeStrategyEnum, // fix this ctx: Ctx, typeDef: TypeDef, filterIndex: number = 0, diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index fa505c58a7..663406d797 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -57,7 +57,7 @@ await test('basic', async (t) => { const result = await db.server.getQueryBuf(ctx.query) // debugBuffer(result) - const readSchemaBuf = await serializeReaderSchema(ctx.readSchema) + const readSchemaBuf = serializeReaderSchema(ctx.readSchema) const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) // console.dir(obj, { depth: 10 }) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index b170665f05..ac7d7a0521 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -75,7 +75,7 @@ await test('include', async (t) => { const rand = fastPrng() - for (let i = 0; i < 1e6; i++) { + for (let i = 0; i < 1e4; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -101,6 +101,8 @@ await test('include', async (t) => { // GET REFERENCEs // SORT REFERENCES // FILTER REFENRRENS + // FILTER REFS BY EDGE + // ALIAS const ast: QueryAst = { type: 'user', @@ -150,18 +152,25 @@ await test('include', async (t) => { name: { include: {} }, y: { include: {} }, }, - // filter: { - // props: { - // y: { - // ops: [{ op: '>', val: 6 }], - // }, - // }, - // }, - // edges: { - // props: { - // $level: { include: {} }, - // }, - // }, + filter: { + // props: { + // y: { + // ops: [{ op: '>', val: 6 }], + // }, + // }, + edges: { + props: { + $level: { + ops: [{ op: '>', val: 100 }], + }, + }, + }, + }, + edges: { + props: { + $level: { include: {} }, + }, + }, }, // mrFriend: { // props: { @@ -176,7 +185,7 @@ await test('include', async (t) => { }, } - console.dir(ast, { depth: 100 }) + console.dir(ast, { depth: 10 }) const ctx = astToQueryCtx(client.schema!, ast, new AutoSizedUint8Array(1000)) From 67c694fb706e92c729e153172946f63dbe9dc84c Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 09:26:08 +0100 Subject: [PATCH 365/449] start of edge only filter --- native/query/multiple.zig | 21 +++++++++++++++++---- src/db-query/ast/ast.ts | 9 +++++++++ src/db-query/ast/filter/filter.ts | 23 ++++++++++------------- src/db-query/ast/iteratorType.ts | 26 ++++++++++++++++++++++---- src/db-query/ast/multiple.ts | 30 +++++++++++++++++++----------- test/query-ast/include.ts | 21 +++++++++++---------- 6 files changed, 88 insertions(+), 42 deletions(-) diff --git a/native/query/multiple.zig b/native/query/multiple.zig index a283621326..f7c3301245 100644 --- a/native/query/multiple.zig +++ b/native/query/multiple.zig @@ -81,14 +81,17 @@ fn iteratorEdge( try Filter.prepare(filter, ctx, typeEntry); } - if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or It == t.QueryIteratorType.edgeFilterOnEdge) { + if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or + It == t.QueryIteratorType.edgeFilterOnEdge) + { edgeFilter = utils.sliceNext(header.edgeFilterSize, q, i); - try Filter.prepare(filter, ctx, typeEntry); + try Filter.prepare(edgeFilter, ctx, typeEntry); } const nestedQuery = q[i.* .. i.* + header.includeSize]; const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; + while (offset > 0) { _ = it.next() orelse return 0; offset -= 1; @@ -101,8 +104,10 @@ fn iteratorEdge( } } - if (It == t.QueryIteratorType.edgeFilterOnEdge) { - if (!try Filter.filter(ref.node, ctx, edgeFilter)) { + if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or + It == t.QueryIteratorType.edgeFilterOnEdge) + { + if (!try Filter.filter(ref.edge, ctx, edgeFilter)) { continue; } } @@ -309,6 +314,8 @@ pub fn references( nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + + // name this large / hasEdge .edge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); @@ -346,11 +353,17 @@ pub fn references( nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, + + // -------------------- .edgeFilterOnEdge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); }, + // add filter, sort, desc etc + // -------------------- + // split up this file + // then we can name this edgeInclude .edgeInclude => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 992ff94836..36d051e262 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -39,6 +39,14 @@ export type FilterLeaf = FilterAst & { select?: { start: number; end: number } } +export const EdgeStrategy = { + noEdge: 0, + edgeOnly: 1, + mixed: 2, +} as const + +export type EdgeStrategyEnum = (typeof EdgeStrategy)[keyof typeof EdgeStrategy] + export type FilterAst = { props?: { [key: string]: FilterLeaf @@ -46,6 +54,7 @@ export type FilterAst = { or?: FilterAst and?: FilterAst edges?: FilterAst + edgeStrategy?: EdgeStrategyEnum // this is a bit difficult combining OR filters with edges combined // this would require and extra check for the type of node how to do? diff --git a/src/db-query/ast/filter/filter.ts b/src/db-query/ast/filter/filter.ts index a673b7db95..468214ce66 100644 --- a/src/db-query/ast/filter/filter.ts +++ b/src/db-query/ast/filter/filter.ts @@ -20,18 +20,13 @@ type WalkCtx = { main: { prop: PropDef; ops: FilterOp[] }[] } -export const EdgeStrategy = { - noEdge: 0, - edgeOnly: 1, - mixed: 2, -} as const - -export type EdgeStrategyEnum = (typeof EdgeStrategy)[keyof typeof EdgeStrategy] - -// Handle EDGES - -// EDGE ONLY? -const walk = (ast: FilterAst, ctx: Ctx, typeDef: TypeDef, walkCtx: WalkCtx) => { +const walk = ( + ast: FilterAst, + ctx: Ctx, + typeDef: TypeDef, + walkCtx: WalkCtx, + edgeType?: TypeDef, +) => { const { tree, main } = walkCtx for (const field in ast.props) { @@ -100,11 +95,11 @@ const indexOf = ( // EDGE ONLY? export const filter = ( ast: FilterAst, - edgeStrategy: EdgeStrategyEnum, // fix this ctx: Ctx, typeDef: TypeDef, filterIndex: number = 0, lastProp: number = PropType.id, + edgeType?: TypeDef, prevOr?: Uint8Array, ): number => { const startIndex = ctx.query.length @@ -154,6 +149,7 @@ export const filter = ( typeDef, ctx.query.length - startIndex, walkCtx.prop, + edgeType, andOrReplace, ) } else { @@ -205,6 +201,7 @@ export const filter = ( typeDef, ctx.query.length - startIndex + filterIndex, walkCtx.prop, + edgeType, prevOr, ) } diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index 0dd2afc5a3..ab324d6460 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -21,13 +21,11 @@ export const getIteratorType = ( const edgeInclude: boolean = header.edgeSize != 0 const hasSort = header.sort const isDesc = ast.order === 'desc' - const hasSearch = false - const isVector = false + // const hasSearch = false + // const isVector = false let base = QUERY_ITERATOR_DEFAULT - console.log('EDGE TIME', edgeInclude, edge) - if (edge && !edgeInclude) { base = QUERY_ITERATOR_EDGE } @@ -36,6 +34,8 @@ export const getIteratorType = ( base = QUERY_ITERATOR_EDGE_INCLUDE } + // console.log('EDGE TIME', edgeInclude, edge) + // if (hasSearch && !isVector) { // base = QUERY_ITERATOR_SEARCH // } @@ -74,5 +74,23 @@ export const getIteratorType = ( base += 0 } + console.log( + QueryIteratorTypeInverse[base], + base, + QueryIteratorType.edgeFilterOnEdge, + ) + + if (header.edgeFilterSize > 0 && header.filterSize === 0) { + if (header.edgeSize === 0) { + if (base === QueryIteratorType.edge) { + base = QueryIteratorType.edgeFilterOnEdge + } + } else { + if (base === QueryIteratorType.edgeInclude) { + base = QueryIteratorType.edgeIncludeFilterOnEdge + } + } + } + return base as QueryIteratorTypeEnum } diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 769193ef66..74515e0eec 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -9,7 +9,7 @@ import { readQueryHeader, pushSortHeader, } from '../../zigTsExports.js' -import { Ctx, QueryAst } from './ast.js' +import { Ctx, EdgeStrategy, QueryAst } from './ast.js' import { filter } from './filter/filter.js' import { include } from './include.js' import { getIteratorType } from './iteratorType.js' @@ -92,11 +92,28 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { pushSortHeader(ctx.query, sort(ast, ctx, prop.ref!, prop)) } - if (ast.filter) { + if (ast.filter && ast.filter.edgeStrategy == EdgeStrategy.noEdge) { + // step 1 const filterSize = filter(ast.filter, ctx, prop.ref!) props.filterSize(ctx.query.data, filterSize, headerIndex) } + if ( + ast.filter && + ast.filter.edgeStrategy == EdgeStrategy.edgeOnly && + ast.filter.edges + ) { + const edges = prop.edges + if (!edges) { + throw new Error('Ref does not have edges (for filter)') + } + props.edgeTypeId(ctx.query.data, edges.id, headerIndex) + const filterSize = filter(ast.filter.edges, ctx, prop.edges!) + + console.log('EDGE FILTER SIZE', filterSize) + props.edgeFilterSize(ctx.query.data, filterSize, headerIndex) + } + const size = include( ast, { @@ -108,10 +125,6 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { props.includeSize(ctx.query.data, size, headerIndex) - // EDGES IN FILTER - // needs to use special iterator - // we need to put the filter on it - if (ast.edges) { const edges = prop.edges if (!edges) { @@ -127,12 +140,7 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { }, edges, ) - // make a iterator type EDGE FILTER OR (means filter fails now also run edge and ignore the failure) - // or add comptime thing that just passed HAS_EDGE then pass the edge in the c and have an extra check - // may be the easier to do props.edgeSize(ctx.query.data, size, headerIndex) - } else { - console.info('EDGES NO!') } props.iteratorType( diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index ac7d7a0521..46609e0315 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -1,5 +1,5 @@ import { deflate } from 'fflate' -import { QueryAst } from '../../src/db-query/ast/ast.js' +import { EdgeStrategy, QueryAst } from '../../src/db-query/ast/ast.js' import { astToQueryCtx } from '../../src/db-query/ast/toCtx.js' import { resultToObject, @@ -75,7 +75,7 @@ await test('include', async (t) => { const rand = fastPrng() - for (let i = 0; i < 1e4; i++) { + for (let i = 0; i < 1; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -85,8 +85,8 @@ await test('include', async (t) => { cookie: 1234, }, friends: [ - { id: a, $level: rand(0, 1000) }, - { id: b, $level: rand(0, 1000) }, + { id: a, $level: rand(0, 200) }, + { id: b, $level: rand(0, 200) }, ], }) } @@ -152,12 +152,18 @@ await test('include', async (t) => { name: { include: {} }, y: { include: {} }, }, + edges: { + props: { + $level: { include: {} }, + }, + }, filter: { // props: { // y: { // ops: [{ op: '>', val: 6 }], // }, // }, + edgeStrategy: EdgeStrategy.edgeOnly, edges: { props: { $level: { @@ -166,11 +172,6 @@ await test('include', async (t) => { }, }, }, - edges: { - props: { - $level: { include: {} }, - }, - }, }, // mrFriend: { // props: { @@ -222,7 +223,7 @@ await test('include', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - // console.dir(obj, { depth: 10 }) + console.dir(obj, { depth: 10 }) await wait(1000) From 42c4db3f4209c3b772c635dccb99479ffaa17eef Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 10:09:04 +0100 Subject: [PATCH 366/449] split multiple --- native/query/include/include.zig | 12 +- native/query/multiple.zig | 498 --------------------------- native/query/multiple/aggregates.zig | 84 +++++ native/query/multiple/default.zig | 65 ++++ native/query/multiple/ids.zig | 55 +++ native/query/multiple/iterate.zig | 138 ++++++++ native/query/multiple/references.zig | 183 ++++++++++ native/query/query.zig | 23 +- 8 files changed, 544 insertions(+), 514 deletions(-) delete mode 100644 native/query/multiple.zig create mode 100644 native/query/multiple/aggregates.zig create mode 100644 native/query/multiple/default.zig create mode 100644 native/query/multiple/ids.zig create mode 100644 native/query/multiple/iterate.zig create mode 100644 native/query/multiple/references.zig diff --git a/native/query/include/include.zig b/native/query/include/include.zig index 2659c1d2f5..b84b06ac2b 100644 --- a/native/query/include/include.zig +++ b/native/query/include/include.zig @@ -8,8 +8,8 @@ const Fields = @import("../../selva/fields.zig"); const opts = @import("opts.zig"); const append = @import("append.zig"); const t = @import("../../types.zig"); -const multiple = @import("../multiple.zig"); -const single = @import("../single.zig"); +const Multiple = @import("../multiple/references.zig"); +const Single = @import("../single.zig"); const References = @import("../../selva/references.zig"); const aggregateRefs = @import("../aggregates/references.zig"); @@ -53,13 +53,13 @@ pub fn include( switch (op) { // add .referenceEdge? .reference => { - recursionErrorBoundary(single.reference, node, ctx, q, typeEntry, &i); + recursionErrorBoundary(Single.reference, node, ctx, q, typeEntry, &i); }, .referenceEdge => { - recursionErrorBoundary(single.referenceEdge, node, ctx, q, typeEntry, &i); + recursionErrorBoundary(Single.referenceEdge, node, ctx, q, typeEntry, &i); }, .references => { - recursionErrorBoundary(multiple.references, node, ctx, q, typeEntry, &i); + recursionErrorBoundary(Multiple.references, node, ctx, q, typeEntry, &i); }, .partial => { const header = utils.readNext(t.IncludePartialHeader, q, &i); @@ -138,7 +138,7 @@ pub fn include( try ctx.thread.query.append(header.prop); if (value.len == 0) { const fs = try Schema.getFieldSchema(typeEntry, header.prop); - _ = try ctx.thread.query.reserve(fs.unnamed_0.smb.len); + _ = try ctx.thread.query.reserve(fs.unnamed_0.smb.len); } else { try ctx.thread.query.append(value); } diff --git a/native/query/multiple.zig b/native/query/multiple.zig deleted file mode 100644 index f7c3301245..0000000000 --- a/native/query/multiple.zig +++ /dev/null @@ -1,498 +0,0 @@ -const std = @import("std"); -const utils = @import("../utils.zig"); -const Query = @import("common.zig"); -const Include = @import("include/include.zig"); -const Filter = @import("filter/filter.zig"); -const Node = @import("../selva/node.zig"); -const References = @import("../selva/references.zig"); -const Selva = @import("../selva/selva.zig"); -const Thread = @import("../thread/thread.zig"); -const Schema = @import("../selva/schema.zig"); -const t = @import("../types.zig"); -const Sort = @import("../sort/sort.zig"); -const Aggregates = @import("aggregates/aggregates.zig"); -const GroupBy = @import("aggregates/group.zig"); -const GroupByHashMap = @import("aggregates/hashMap.zig").GroupByHashMap; -const String = @import("../string.zig"); -const writeAs = utils.writeAs; -const read = utils.read; - -fn iterator( - comptime It: t.QueryIteratorType, - ctx: *Query.QueryCtx, - q: []u8, - it: anytype, - header: *const t.QueryHeader, // make this type - typeEntry: Node.Type, - i: *usize, -) !u32 { - var offset: u32 = header.offset; - var nodeCnt: u32 = 0; - var filter: []u8 = undefined; - if (It == t.QueryIteratorType.filter) { - filter = utils.sliceNext(header.filterSize, q, i); - try Filter.prepare(filter, ctx, typeEntry); - } - const nestedQuery = q[i.* .. i.* + header.includeSize]; - while (offset > 0) { - const node = it.next() orelse return 0; - if (It == t.QueryIteratorType.filter) { - if (try Filter.filter(node, ctx, filter)) { - offset -= 1; - } - } else { - offset -= 1; - } - } - while (it.next()) |node| { - if (It == t.QueryIteratorType.filter) { - if (!try Filter.filter(node, ctx, filter)) { - continue; - } - } - try ctx.thread.query.append(t.ReadOp.id); - try ctx.thread.query.append(Node.getNodeId(node)); - try Include.include(node, ctx, nestedQuery, typeEntry); - nodeCnt += 1; - if (nodeCnt >= header.limit) { - break; - } - } - return nodeCnt; -} - -fn iteratorEdge( - comptime It: t.QueryIteratorType, - ctx: *Query.QueryCtx, - q: []u8, - it: anytype, - header: *const t.QueryHeader, - typeEntry: Node.Type, - i: *usize, -) !u32 { - var offset: u32 = header.offset; - var nodeCnt: u32 = 0; - - var filter: []u8 = undefined; - var edgeFilter: []u8 = undefined; - - if (It == t.QueryIteratorType.filter) { - filter = utils.sliceNext(header.filterSize, q, i); - try Filter.prepare(filter, ctx, typeEntry); - } - - if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or - It == t.QueryIteratorType.edgeFilterOnEdge) - { - edgeFilter = utils.sliceNext(header.edgeFilterSize, q, i); - try Filter.prepare(edgeFilter, ctx, typeEntry); - } - - const nestedQuery = q[i.* .. i.* + header.includeSize]; - const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); - const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; - - while (offset > 0) { - _ = it.next() orelse return 0; - offset -= 1; - } - - while (it.nextRef()) |ref| { - if (It == t.QueryIteratorType.filter) { - if (!try Filter.filter(ref.node, ctx, filter)) { - continue; - } - } - - if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or - It == t.QueryIteratorType.edgeFilterOnEdge) - { - if (!try Filter.filter(ref.edge, ctx, edgeFilter)) { - continue; - } - } - - try ctx.thread.query.append(t.ReadOp.id); - try ctx.thread.query.append(Node.getNodeId(ref.node)); - try Include.include(ref.node, ctx, nestedQuery, typeEntry); - - if (It != t.QueryIteratorType.edgeFilterOnEdge) { - try ctx.thread.query.append(t.ReadOp.edge); - const edgesByteSizeIndex = try ctx.thread.query.reserve(4); - const edgeStartIndex = ctx.thread.query.index; - try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); - ctx.thread.query.writeAs( - u32, - @truncate(ctx.thread.query.index - edgeStartIndex), - edgesByteSizeIndex, - ); - } - - nodeCnt += 1; - if (nodeCnt >= header.limit) { - break; - } - } - i.* += header.edgeSize; - return nodeCnt; -} - -const IdsIterator = struct { - ids: []u32, - i: u32, - typeEntry: Node.Type, - pub fn next(self: *IdsIterator) ?Node.Node { - if (self.i == self.ids.len) { - return null; - } - const node = Node.getNode(self.typeEntry, self.ids[self.i]); - self.i += 1; - return node; - } -}; - -pub fn ids( - ctx: *Query.QueryCtx, - q: []u8, -) !void { - var i: usize = 0; - const header = utils.readNext(t.QueryHeader, q, &i); - const sizeIndex = try ctx.thread.query.reserve(4); - const size = header.size; - const typeEntry = try Node.getType(ctx.db, header.typeId); - var it = IdsIterator{ .i = 0, .ids = utils.read([]u32, q, size + 4), .typeEntry = typeEntry }; - var nodeCnt: u32 = 0; - switch (header.iteratorType) { - .default => { - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); - }, - .desc => { - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); - }, - .sort => { - const sortHeader = utils.readNext(t.SortHeader, q, &i); - var itSort = try Sort.fromIterator(false, false, ctx.db, ctx.thread, typeEntry, &sortHeader, &it); - nodeCnt = try iterator(.default, ctx, q, &itSort, &header, typeEntry, &i); - itSort.deinit(); - }, - .descSort => { - const sortHeader = utils.readNext(t.SortHeader, q, &i); - var itSort = try Sort.fromIterator(true, false, ctx.db, ctx.thread, typeEntry, &sortHeader, &it); - nodeCnt = try iterator(.default, ctx, q, &itSort, &header, typeEntry, &i); - itSort.deinit(); - }, - else => {}, - } - ctx.thread.query.write(nodeCnt, sizeIndex); -} - -pub fn default( - ctx: *Query.QueryCtx, - q: []u8, -) !void { - var i: usize = 0; - // make default header! use :type in iterator - const header = utils.readNext(t.QueryHeader, q, &i); - const sizeIndex = try ctx.thread.query.reserve(4); - const typeEntry = try Node.getType(ctx.db, header.typeId); - var nodeCnt: u32 = 0; - - switch (header.iteratorType) { - .default => { - var it = Node.iterator(false, typeEntry); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); - }, - .desc => { - var it = Node.iterator(true, typeEntry); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); - }, - - .sort => { - const sortHeader = utils.readNext(t.SortHeader, q, &i); - var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); - }, - .descSort => { - const sortHeader = utils.readNext(t.SortHeader, q, &i); - var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, &i); - }, - - .filter => { - var it = Node.iterator(false, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); - }, - .descFilter => { - var it = Node.iterator(true, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); - }, - - .filterSort => { - const sortHeader = utils.readNext(t.SortHeader, q, &i); - var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); - }, - .descFilterSort => { - const sortHeader = utils.readNext(t.SortHeader, q, &i); - var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, &i); - }, - - else => { - // not handled - }, - } - ctx.thread.query.write(nodeCnt, sizeIndex); -} - -inline fn referencesSort( - comptime desc: bool, - comptime edge: bool, - ctx: *Query.QueryCtx, - q: []u8, - from: Node.Node, - fromType: Selva.Type, - i: *usize, - header: *const t.QueryHeader, - typeEntry: Node.Type, -) !Sort.SortIterator(desc, edge) { - const sortHeader = utils.readNext(t.SortHeader, q, i); - var refs = try References.iterator(desc, edge, ctx.db, from, header.prop, fromType); - return try Sort.fromIterator(desc, edge, ctx.db, ctx.thread, typeEntry, &sortHeader, &refs); -} - -pub fn references( - ctx: *Query.QueryCtx, - q: []u8, - from: Node.Node, - fromType: Selva.Type, - i: *usize, -) !void { - const header = utils.readNext(t.QueryHeader, q, i); - try ctx.thread.query.append(t.ReadOp.references); - try ctx.thread.query.append(header.prop); - const resultByteSizeIndex = try ctx.thread.query.reserve(4); - const startIndex = ctx.thread.query.index; - const sizeIndex = try ctx.thread.query.reserve(4); - const typeEntry = try Node.getType(ctx.db, header.typeId); - var nodeCnt: u32 = 0; - - switch (header.iteratorType) { - .default => { - var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - }, - .desc => { - var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - }, - .sort => { - var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .descSort => { - var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .filter => { - var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .descFilter => { - var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .filterSort => { - var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .descFilterSort => { - var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - - // name this large / hasEdge - .edge => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - }, - .edgeDesc => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - }, - .edgeSort => { - var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .edgeDescSort => { - var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.default, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - - .edgeFilter => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .edgeDescFilter => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .edgeFilterSort => { - var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .edgeDescFilterSort => { - var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iterator(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - - // -------------------- - .edgeFilterOnEdge => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); - }, - // add filter, sort, desc etc - // -------------------- - - // split up this file - // then we can name this edgeInclude - .edgeInclude => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); - }, - .edgeIncludeDesc => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); - }, - .edgeIncludeSort => { - var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .edgeIncludeDescSort => { - var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iteratorEdge(.default, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - - .edgeIncludeFilter => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .edgeIncludeDescFilter => { - var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); - }, - .edgeIncludeFilterSort => { - var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - .edgeIncludeDescFilterSort => { - var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try iteratorEdge(.filter, ctx, q, &it, &header, typeEntry, i); - it.deinit(); - }, - - .edgeIncludeFilterOnEdge => { - var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); - nodeCnt = try iteratorEdge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); - }, - - // filterAndEdgeFilter - - else => { - // not handled - }, - } - - i.* += header.includeSize; //+ header.edgeSize; - ctx.thread.query.write(nodeCnt, sizeIndex); - - ctx.thread.query.writeAs( - u32, - @truncate(ctx.thread.query.index - startIndex), - resultByteSizeIndex, - ); -} - -pub fn aggregates( - ctx: *Query.QueryCtx, - q: []u8, -) !void { - var i: usize = 0; - var nodeCnt: u32 = 0; - - const header = utils.read(t.AggHeader, q, i); - - i += utils.sizeOf(t.AggHeader); - const typeId = header.typeId; - const typeEntry = try Node.getType(ctx.db, typeId); - - const accumulatorProp = try ctx.db.allocator.alloc(u8, header.accumulatorSize); - @memset(accumulatorProp, 0); - defer ctx.db.allocator.free(accumulatorProp); - const hllAccumulator = Selva.c.selva_string_create(null, Selva.c.HLL_INIT_SIZE, Selva.c.SELVA_STRING_MUTABLE); - defer Selva.c.selva_string_free(hllAccumulator); - - var aggCtx = Aggregates.AggCtx{ - .queryCtx = ctx, - .typeEntry = typeEntry, - .limit = header.limit, - .isSamplingSet = header.isSamplingSet, - .hllAccumulator = hllAccumulator, - .accumulatorSize = header.accumulatorSize, - .resultsSize = header.resultsSize, - .totalResultsSize = 0, - }; - - var it = Node.iterator(false, typeEntry); - switch (header.iteratorType) { - .aggregate => { - nodeCnt = try Aggregates.iterator(&aggCtx, &it, false, undefined, q[i..], accumulatorProp); - try Aggregates.finalizeResults(&aggCtx, q[i..], accumulatorProp, 0); - }, - .aggregateFilter => { - const filter = utils.sliceNext(header.filterSize, q, &i); - try Filter.prepare(filter, ctx, typeEntry); - nodeCnt = try Aggregates.iterator(&aggCtx, &it, true, filter, q[i..], accumulatorProp); - try Aggregates.finalizeResults(&aggCtx, q[i..], accumulatorProp, 0); - }, - .groupBy => { - var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); - defer groupByHashMap.deinit(); - nodeCnt = @intCast(GroupBy.iterator(&aggCtx, &groupByHashMap, &it, false, undefined, q[i..])); - try GroupBy.finalizeGroupResults(&aggCtx, &groupByHashMap, q[i..]); - }, - .groupByFilter => { - const filter = utils.sliceNext(header.filterSize, q, &i); - try Filter.prepare(filter, ctx, typeEntry); - var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); - defer groupByHashMap.deinit(); - nodeCnt = @intCast(GroupBy.iterator(&aggCtx, &groupByHashMap, &it, true, filter, q[i..])); - try GroupBy.finalizeGroupResults(&aggCtx, &groupByHashMap, q[i..]); - }, - else => {}, - } -} - -pub fn aggregatesCount( - ctx: *Query.QueryCtx, - q: []u8, -) !void { - var i: usize = 0; - const header = utils.read(t.AggHeader, q, i); - i += utils.sizeOf(t.AggHeader); - const typeId = header.typeId; - const typeEntry = try Node.getType(ctx.db, typeId); - const count: u32 = @truncate(Node.getNodeCount(typeEntry)); - try ctx.thread.query.append(count); -} diff --git a/native/query/multiple/aggregates.zig b/native/query/multiple/aggregates.zig new file mode 100644 index 0000000000..4f7caf30aa --- /dev/null +++ b/native/query/multiple/aggregates.zig @@ -0,0 +1,84 @@ +const utils = @import("../../utils.zig"); +const Query = @import("../common.zig"); +const t = @import("../../types.zig"); +const Node = @import("../../selva/node.zig"); +const Iterate = @import("./iterate.zig"); +const Sort = @import("../../sort/sort.zig"); +const Selva = @import("../../selva/selva.zig"); +const Filter = @import("../filter/filter.zig"); +const GroupByHashMap = @import("../aggregates/hashMap.zig").GroupByHashMap; +const GroupBy = @import("../aggregates/group.zig"); +const Aggregates = @import("../aggregates/aggregates.zig"); + +pub fn aggregates( + ctx: *Query.QueryCtx, + q: []u8, +) !void { + var i: usize = 0; + var nodeCnt: u32 = 0; + + const header = utils.read(t.AggHeader, q, i); + + i += utils.sizeOf(t.AggHeader); + const typeId = header.typeId; + const typeEntry = try Node.getType(ctx.db, typeId); + + const accumulatorProp = try ctx.db.allocator.alloc(u8, header.accumulatorSize); + @memset(accumulatorProp, 0); + defer ctx.db.allocator.free(accumulatorProp); + const hllAccumulator = Selva.c.selva_string_create(null, Selva.c.HLL_INIT_SIZE, Selva.c.SELVA_STRING_MUTABLE); + defer Selva.c.selva_string_free(hllAccumulator); + + var aggCtx = Aggregates.AggCtx{ + .queryCtx = ctx, + .typeEntry = typeEntry, + .limit = header.limit, + .isSamplingSet = header.isSamplingSet, + .hllAccumulator = hllAccumulator, + .accumulatorSize = header.accumulatorSize, + .resultsSize = header.resultsSize, + .totalResultsSize = 0, + }; + + var it = Node.iterator(false, typeEntry); + switch (header.iteratorType) { + .aggregate => { + nodeCnt = try Aggregates.iterator(&aggCtx, &it, false, undefined, q[i..], accumulatorProp); + try Aggregates.finalizeResults(&aggCtx, q[i..], accumulatorProp, 0); + }, + .aggregateFilter => { + const filter = utils.sliceNext(header.filterSize, q, &i); + try Filter.prepare(filter, ctx, typeEntry); + nodeCnt = try Aggregates.iterator(&aggCtx, &it, true, filter, q[i..], accumulatorProp); + try Aggregates.finalizeResults(&aggCtx, q[i..], accumulatorProp, 0); + }, + .groupBy => { + var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); + defer groupByHashMap.deinit(); + nodeCnt = @intCast(GroupBy.iterator(&aggCtx, &groupByHashMap, &it, false, undefined, q[i..])); + try GroupBy.finalizeGroupResults(&aggCtx, &groupByHashMap, q[i..]); + }, + .groupByFilter => { + const filter = utils.sliceNext(header.filterSize, q, &i); + try Filter.prepare(filter, ctx, typeEntry); + var groupByHashMap = GroupByHashMap.init(ctx.db.allocator); + defer groupByHashMap.deinit(); + nodeCnt = @intCast(GroupBy.iterator(&aggCtx, &groupByHashMap, &it, true, filter, q[i..])); + try GroupBy.finalizeGroupResults(&aggCtx, &groupByHashMap, q[i..]); + }, + else => {}, + } +} + +pub fn aggregatesCount( + ctx: *Query.QueryCtx, + q: []u8, +) !void { + var i: usize = 0; + const header = utils.read(t.AggHeader, q, i); + i += utils.sizeOf(t.AggHeader); + const typeId = header.typeId; + const typeEntry = try Node.getType(ctx.db, typeId); + const count: u32 = @truncate(Node.getNodeCount(typeEntry)); + try ctx.thread.query.append(count); +} diff --git a/native/query/multiple/default.zig b/native/query/multiple/default.zig new file mode 100644 index 0000000000..a99d040945 --- /dev/null +++ b/native/query/multiple/default.zig @@ -0,0 +1,65 @@ +const utils = @import("../../utils.zig"); +const Query = @import("../common.zig"); +const t = @import("../../types.zig"); +const Node = @import("../../selva/node.zig"); +const Iterate = @import("./iterate.zig"); +const Sort = @import("../../sort/sort.zig"); + +pub fn default( + ctx: *Query.QueryCtx, + q: []u8, +) !void { + var i: usize = 0; + // make default header! use :type in iterator + const header = utils.readNext(t.QueryHeader, q, &i); + const sizeIndex = try ctx.thread.query.reserve(4); + const typeEntry = try Node.getType(ctx.db, header.typeId); + var nodeCnt: u32 = 0; + + switch (header.iteratorType) { + .default => { + var it = Node.iterator(false, typeEntry); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, &i); + }, + .desc => { + var it = Node.iterator(true, typeEntry); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, &i); + }, + + .sort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, &i); + }, + .descSort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, &i); + }, + + .filter => { + var it = Node.iterator(false, typeEntry); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + .descFilter => { + var it = Node.iterator(true, typeEntry); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + + .filterSort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var it = try Sort.iterator(false, ctx.db, ctx.thread, header.typeId, &sortHeader); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + .descFilterSort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var it = try Sort.iterator(true, ctx.db, ctx.thread, header.typeId, &sortHeader); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, &i); + }, + + else => { + // not handled + }, + } + ctx.thread.query.write(nodeCnt, sizeIndex); +} diff --git a/native/query/multiple/ids.zig b/native/query/multiple/ids.zig new file mode 100644 index 0000000000..f536cc8aad --- /dev/null +++ b/native/query/multiple/ids.zig @@ -0,0 +1,55 @@ +const utils = @import("../../utils.zig"); +const Query = @import("../common.zig"); +const t = @import("../../types.zig"); +const Node = @import("../../selva/node.zig"); +const Iterate = @import("./iterate.zig"); +const Sort = @import("../../sort/sort.zig"); + +pub const IdsIterator = struct { + ids: []u32, + i: u32, + typeEntry: Node.Type, + pub fn next(self: *IdsIterator) ?Node.Node { + if (self.i == self.ids.len) { + return null; + } + const node = Node.getNode(self.typeEntry, self.ids[self.i]); + self.i += 1; + return node; + } +}; + +pub fn ids( + ctx: *Query.QueryCtx, + q: []u8, +) !void { + var i: usize = 0; + const header = utils.readNext(t.QueryHeader, q, &i); + const sizeIndex = try ctx.thread.query.reserve(4); + const size = header.size; + const typeEntry = try Node.getType(ctx.db, header.typeId); + var it = IdsIterator{ .i = 0, .ids = utils.read([]u32, q, size + 4), .typeEntry = typeEntry }; + var nodeCnt: u32 = 0; + switch (header.iteratorType) { + .default => { + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, &i); + }, + .desc => { + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, &i); + }, + .sort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var itSort = try Sort.fromIterator(false, false, ctx.db, ctx.thread, typeEntry, &sortHeader, &it); + nodeCnt = try Iterate.node(.default, ctx, q, &itSort, &header, typeEntry, &i); + itSort.deinit(); + }, + .descSort => { + const sortHeader = utils.readNext(t.SortHeader, q, &i); + var itSort = try Sort.fromIterator(true, false, ctx.db, ctx.thread, typeEntry, &sortHeader, &it); + nodeCnt = try Iterate.node(.default, ctx, q, &itSort, &header, typeEntry, &i); + itSort.deinit(); + }, + else => {}, + } + ctx.thread.query.write(nodeCnt, sizeIndex); +} diff --git a/native/query/multiple/iterate.zig b/native/query/multiple/iterate.zig new file mode 100644 index 0000000000..556ac8202f --- /dev/null +++ b/native/query/multiple/iterate.zig @@ -0,0 +1,138 @@ +const std = @import("std"); +const utils = @import("../../utils.zig"); +const Query = @import("../common.zig"); +const Include = @import("../include/include.zig"); +const Filter = @import("../filter/filter.zig"); +const Node = @import("../../selva/node.zig"); +const References = @import("../../selva/references.zig"); +const Selva = @import("../../selva/selva.zig"); +const Thread = @import("../../thread/thread.zig"); +const Schema = @import("../../selva/schema.zig"); +const t = @import("../../types.zig"); +const Sort = @import("../../sort/sort.zig"); +const Aggregates = @import("../aggregates/aggregates.zig"); +const GroupBy = @import("../aggregates/group.zig"); +const GroupByHashMap = @import("../aggregates/hashMap.zig").GroupByHashMap; +const String = @import("../../string.zig"); +const writeAs = utils.writeAs; +const read = utils.read; + +pub fn node( + comptime It: t.QueryIteratorType, + ctx: *Query.QueryCtx, + q: []u8, + it: anytype, + header: *const t.QueryHeader, // make this type + typeEntry: Node.Type, + i: *usize, +) !u32 { + var offset: u32 = header.offset; + var nodeCnt: u32 = 0; + var filter: []u8 = undefined; + if (It == t.QueryIteratorType.filter) { + filter = utils.sliceNext(header.filterSize, q, i); + try Filter.prepare(filter, ctx, typeEntry); + } + const nestedQuery = q[i.* .. i.* + header.includeSize]; + while (offset > 0) { + const n = it.next() orelse return 0; + if (It == t.QueryIteratorType.filter) { + if (try Filter.filter(n, ctx, filter)) { + offset -= 1; + } + } else { + offset -= 1; + } + } + while (it.next()) |n| { + if (It == t.QueryIteratorType.filter) { + if (!try Filter.filter(n, ctx, filter)) { + continue; + } + } + try ctx.thread.query.append(t.ReadOp.id); + try ctx.thread.query.append(Node.getNodeId(n)); + try Include.include(n, ctx, nestedQuery, typeEntry); + nodeCnt += 1; + if (nodeCnt >= header.limit) { + break; + } + } + return nodeCnt; +} + +pub fn edge( + comptime It: t.QueryIteratorType, + ctx: *Query.QueryCtx, + q: []u8, + it: anytype, + header: *const t.QueryHeader, + typeEntry: Node.Type, + i: *usize, +) !u32 { + var offset: u32 = header.offset; + var nodeCnt: u32 = 0; + + var filter: []u8 = undefined; + var edgeFilter: []u8 = undefined; + + if (It == t.QueryIteratorType.filter) { + filter = utils.sliceNext(header.filterSize, q, i); + try Filter.prepare(filter, ctx, typeEntry); + } + + if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or + It == t.QueryIteratorType.edgeFilterOnEdge) + { + edgeFilter = utils.sliceNext(header.edgeFilterSize, q, i); + try Filter.prepare(edgeFilter, ctx, typeEntry); + } + + const nestedQuery = q[i.* .. i.* + header.includeSize]; + const edgeTypeEntry = try Node.getType(ctx.db, header.edgeTypeId); + const edgeQuery = q[i.* + header.includeSize .. i.* + header.includeSize + header.edgeSize]; + + while (offset > 0) { + _ = it.next() orelse return 0; + offset -= 1; + } + + while (it.nextRef()) |ref| { + if (It == t.QueryIteratorType.filter) { + if (!try Filter.filter(ref.node, ctx, filter)) { + continue; + } + } + + if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or + It == t.QueryIteratorType.edgeFilterOnEdge) + { + if (!try Filter.filter(ref.edge, ctx, edgeFilter)) { + continue; + } + } + + try ctx.thread.query.append(t.ReadOp.id); + try ctx.thread.query.append(Node.getNodeId(ref.node)); + try Include.include(ref.node, ctx, nestedQuery, typeEntry); + + if (It != t.QueryIteratorType.edgeFilterOnEdge) { + try ctx.thread.query.append(t.ReadOp.edge); + const edgesByteSizeIndex = try ctx.thread.query.reserve(4); + const edgeStartIndex = ctx.thread.query.index; + try Include.include(ref.edge, ctx, edgeQuery, edgeTypeEntry); + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - edgeStartIndex), + edgesByteSizeIndex, + ); + } + + nodeCnt += 1; + if (nodeCnt >= header.limit) { + break; + } + } + i.* += header.edgeSize; + return nodeCnt; +} diff --git a/native/query/multiple/references.zig b/native/query/multiple/references.zig new file mode 100644 index 0000000000..9b5eb9602b --- /dev/null +++ b/native/query/multiple/references.zig @@ -0,0 +1,183 @@ +const utils = @import("../../utils.zig"); +const t = @import("../../types.zig"); +const Node = @import("../../selva/node.zig"); +const Selva = @import("../../selva/selva.zig"); +const Sort = @import("../../sort/sort.zig"); +const References = @import("../../selva/references.zig"); +const Query = @import("../common.zig"); +const Iterate = @import("./iterate.zig"); + +inline fn referencesSort( + comptime desc: bool, + comptime edge: bool, + ctx: *Query.QueryCtx, + q: []u8, + from: Node.Node, + fromType: Selva.Type, + i: *usize, + header: *const t.QueryHeader, + typeEntry: Node.Type, +) !Sort.SortIterator(desc, edge) { + const sortHeader = utils.readNext(t.SortHeader, q, i); + var refs = try References.iterator(desc, edge, ctx.db, from, header.prop, fromType); + return try Sort.fromIterator(desc, edge, ctx.db, ctx.thread, typeEntry, &sortHeader, &refs); +} + +pub fn references( + ctx: *Query.QueryCtx, + q: []u8, + from: Node.Node, + fromType: Selva.Type, + i: *usize, +) !void { + const header = utils.readNext(t.QueryHeader, q, i); + try ctx.thread.query.append(t.ReadOp.references); + try ctx.thread.query.append(header.prop); + const resultByteSizeIndex = try ctx.thread.query.reserve(4); + const startIndex = ctx.thread.query.index; + const sizeIndex = try ctx.thread.query.reserve(4); + const typeEntry = try Node.getType(ctx.db, header.typeId); + var nodeCnt: u32 = 0; + + switch (header.iteratorType) { + .default => { + var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + }, + .desc => { + var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + }, + .sort => { + var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .descSort => { + var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .filter => { + var it = try References.iterator(false, false, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .descFilter => { + var it = try References.iterator(true, false, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .filterSort => { + var it = try referencesSort(false, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .descFilterSort => { + var it = try referencesSort(true, false, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + + // name this large / hasEdge + .edge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + }, + .edgeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + }, + .edgeSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeDescSort => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.default, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + + .edgeFilter => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeDescFilter => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeFilterSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeDescFilterSort => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + + // -------------------- + .edgeFilterOnEdge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + // add filter, sort, desc etc + // -------------------- + + // split up this file + // then we can name this edgeInclude + .edgeInclude => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.default, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.default, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.default, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeIncludeDescSort => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.default, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + + .edgeIncludeFilter => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeDescFilter => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.filter, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeFilterSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeIncludeDescFilterSort => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.filter, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + + .edgeIncludeFilterOnEdge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + + else => {}, + } + + i.* += header.includeSize; //+ header.edgeSize; + ctx.thread.query.write(nodeCnt, sizeIndex); + + ctx.thread.query.writeAs( + u32, + @truncate(ctx.thread.query.index - startIndex), + resultByteSizeIndex, + ); +} diff --git a/native/query/query.zig b/native/query/query.zig index 93427ea06a..3426fc9bf9 100644 --- a/native/query/query.zig +++ b/native/query/query.zig @@ -3,13 +3,16 @@ const errors = @import("../errors.zig"); const napi = @import("../napi.zig"); const Query = @import("common.zig"); const utils = @import("../utils.zig"); -const multiple = @import("multiple.zig"); -const single = @import("single.zig"); const Thread = @import("../thread/thread.zig"); const t = @import("../types.zig"); const DbCtx = @import("../db/ctx.zig").DbCtx; const Selva = @import("../selva"); +const ids = @import("multiple/ids.zig").ids; +const default = @import("multiple/default.zig").default; +const Single = @import("single.zig"); +const Aggregates = @import("multiple/aggregates.zig"); + // -------- NAPI ---------- (put in js bridge maybe?) pub fn getQueryBufThread(env: napi.Env, info: napi.Info) callconv(.c) napi.Value { return getQueryBufInternalThread(env, info) catch |err| { @@ -47,14 +50,14 @@ pub fn getQueryThreaded( _ = try thread.query.result(0, queryId, op); switch (op) { - .default => try multiple.default(&ctx, q), - .id => try single.default(false, &ctx, q), - .idFilter => try single.default(true, &ctx, q), - .alias => try single.alias(false, &ctx, q), - .aliasFilter => try single.alias(true, &ctx, q), - .ids => try multiple.ids(&ctx, q), - .aggregates => try multiple.aggregates(&ctx, q), - .aggregatesCount => try multiple.aggregatesCount(&ctx, q), + .default => try default(&ctx, q), + .id => try Single.default(false, &ctx, q), + .idFilter => try Single.default(true, &ctx, q), + .alias => try Single.alias(false, &ctx, q), + .aliasFilter => try Single.alias(true, &ctx, q), + .ids => try ids(&ctx, q), + .aggregates => try Aggregates.aggregates(&ctx, q), + .aggregatesCount => try Aggregates.aggregatesCount(&ctx, q), else => { return errors.DbError.INCORRECT_QUERY_TYPE; }, From 7caf8694754c2e8912f28459fb9a864d67333423 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 10:31:19 +0100 Subject: [PATCH 367/449] more fix --- native/query/multiple/references.zig | 31 ++++++++++++++++++++++++++- native/types.zig | 32 ++++++++-------------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/native/query/multiple/references.zig b/native/query/multiple/references.zig index 9b5eb9602b..aa8fa8537a 100644 --- a/native/query/multiple/references.zig +++ b/native/query/multiple/references.zig @@ -121,7 +121,20 @@ pub fn references( var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try Iterate.edge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); }, - // add filter, sort, desc etc + .edgeFilterOnEdgeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + .edgeFilterOnEdgeSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeFilterOnEdgeSortDesc => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, // -------------------- // split up this file @@ -164,10 +177,26 @@ pub fn references( it.deinit(); }, + // -------------------- .edgeIncludeFilterOnEdge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try Iterate.edge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); }, + .edgeIncludeFilterOnEdgeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeFilterOnEdgeSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeIncludeFilterOnEdgeSortDesc => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.node(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + // -------------------- else => {}, } diff --git a/native/types.zig b/native/types.zig index 24c4154fc3..cfa9e7e6a5 100644 --- a/native/types.zig +++ b/native/types.zig @@ -650,8 +650,15 @@ pub const QueryIteratorType = enum(u8) { edgeIncludeDescFilter = 36, edgeIncludeDescFilterSort = 37, - edgeIncludeFilterOnEdge = 38, - edgeFilterOnEdge = 39, + edgeIncludeFilterOnEdge = 40, + edgeIncludeFilterOnEdgeDesc = 41, + edgeIncludeFilterOnEdgeSort = 42, + edgeIncludeFilterOnEdgeSortDesc = 43, + + edgeFilterOnEdge = 60, + edgeFilterOnEdgeDesc = 61, + edgeFilterOnEdgeSort = 62, + edgeFilterOnEdgeSortDesc = 63, // default search search = 120, @@ -755,27 +762,6 @@ pub const SubscriptionHeader = packed struct { partialLen: u8, }; -// lets measure -// pub const QueryHeader = packed struct { -// op: QueryType, -// prop: u8, // this is for ref -// typeId: TypeId, -// edgeTypeId: TypeId, -// offset: u32, -// limit: u32, -// filterSize: u16, -// searchSize: u16, -// edgeSize: u16, -// edgeFilterSize: u16, -// includeSize: u16, // cannot be more then 16kb? might be good enough -// size: u16, -// sort: bool, -// filter: bool, -// hasEdge: bool, -// edgeInclude: bool, -// _padding: u4, -// }; - pub const QueryHeader = packed struct { op: QueryType, prop: u8, // this is for ref From cb0cdb5d1f3316a640bbc8777d5a4dbe81df0361 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 10:32:46 +0100 Subject: [PATCH 368/449] update exports --- src/zigTsExports.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index 8e7a0151cd..ba8629c5b8 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -2242,8 +2242,14 @@ export const QueryIteratorType = { edgeIncludeDescSort: 35, edgeIncludeDescFilter: 36, edgeIncludeDescFilterSort: 37, - edgeIncludeFilterOnEdge: 38, - edgeFilterOnEdge: 39, + edgeIncludeFilterOnEdge: 40, + edgeIncludeFilterOnEdgeDesc: 41, + edgeIncludeFilterOnEdgeSort: 42, + edgeIncludeFilterOnEdgeSortDesc: 43, + edgeFilterOnEdge: 60, + edgeFilterOnEdgeDesc: 61, + edgeFilterOnEdgeSort: 62, + edgeFilterOnEdgeSortDesc: 63, search: 120, searchFilter: 121, vec: 130, @@ -2279,8 +2285,14 @@ export const QueryIteratorTypeInverse = { 35: 'edgeIncludeDescSort', 36: 'edgeIncludeDescFilter', 37: 'edgeIncludeDescFilterSort', - 38: 'edgeIncludeFilterOnEdge', - 39: 'edgeFilterOnEdge', + 40: 'edgeIncludeFilterOnEdge', + 41: 'edgeIncludeFilterOnEdgeDesc', + 42: 'edgeIncludeFilterOnEdgeSort', + 43: 'edgeIncludeFilterOnEdgeSortDesc', + 60: 'edgeFilterOnEdge', + 61: 'edgeFilterOnEdgeDesc', + 62: 'edgeFilterOnEdgeSort', + 63: 'edgeFilterOnEdgeSortDesc', 120: 'search', 121: 'searchFilter', 130: 'vec', @@ -2317,7 +2329,13 @@ export const QueryIteratorTypeInverse = { edgeIncludeDescFilter, edgeIncludeDescFilterSort, edgeIncludeFilterOnEdge, + edgeIncludeFilterOnEdgeDesc, + edgeIncludeFilterOnEdgeSort, + edgeIncludeFilterOnEdgeSortDesc, edgeFilterOnEdge, + edgeFilterOnEdgeDesc, + edgeFilterOnEdgeSort, + edgeFilterOnEdgeSortDesc, search, searchFilter, vec, From 5cf4b77e612fe46e1a9ce442c26899b9df21d23e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 10:26:06 +0100 Subject: [PATCH 369/449] colvec default --- clibs/lib/selva/schema.c | 22 +++++++++++++--- src/schema/defs/props/vector.ts | 38 ++++++++++++++++------------ test/modify/props/vector.ts | 45 +++++++++++++++++++++++++++++++-- 3 files changed, 83 insertions(+), 22 deletions(-) diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index eecceeb5b5..94e17f6a3c 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -82,7 +82,7 @@ static int type2fs_micro_buffer(struct schemabuf_parser_ctx *ctx, struct SelvaFi return SELVA_EINVAL; } - /* * Default is copied straight from the schema buffer. */ + /* Default is copied straight from the schema buffer. */ fs->default_off = calc_default_off(ctx, off); off += head.len; } @@ -248,17 +248,19 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc uint16_t comp_size; /*!< Component size in the vector. */ schema_bool_t has_default; } __packed spec; + size_t off = 0; if (ctx->len < sizeof(spec)) { return SELVA_EINVAL; } - memcpy(&spec, ctx->buf, sizeof(spec)); + memcpy(&spec, ctx->buf + off, sizeof(spec)); + off += sizeof(spec); *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_COLVEC, - .default_off = (spec.has_default) ? calc_default_off(ctx, sizeof(spec)) : 0, + .default_off = 0, .colvec = { .vec_len = spec.vec_len, .comp_size = spec.comp_size, @@ -266,7 +268,19 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc }, }; - return sizeof(spec); + if (spec.has_default) { + size_t vec_size = spec.vec_len * spec.comp_size; + + if (ctx->len < off + vec_size) { + return SELVA_EINVAL; + } + + /* Default is copied straight from the schema buffer. */ + fs->default_off = calc_default_off(ctx, off); + off += vec_size; + } + + return off; } static struct schemabuf_parser { diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 7f13aefe9d..26edba7e13 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -16,17 +16,7 @@ import { import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' -import { isTypedArray } from 'util/types' - -function validateVector(value: unknown): asserts value is Uint8Array { - if (!isTypedArray(value)) { - throw new Error('Not a typed array') - } - const t = vectorBaseType2TypedArray[this.schema.baseType] - if (!(value instanceof t)) { - throw new Error(`Not a ${t.name}`) - } -} +import { TypedArray } from '../../../protocol/index.js' export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { @@ -36,8 +26,14 @@ export const vector = class Vector extends BasePropDef { } vectorSize: number override type: PropTypeEnum = PropType.vector - override validate(value: unknown): asserts value is Uint8Array { - validateVector.call(this, value) + override validate(value: unknown): asserts value is TypedArray { + const t = vectorBaseType2TypedArray[this.schema['baseType']] + if (!(value instanceof t)) { + throw new Error(`Not a ${t.name}`) + } + if ((value as TypedArray).byteLength > this.vectorSize) { + throw new Error('Vector too long') + } } override pushValue( buf: AutoSizedUint8Array, @@ -80,7 +76,13 @@ export const colvec = class ColVec extends BasePropDef { vecLen: number override type = PropType.colVec override validate(value: unknown): asserts value is Uint8Array { - validateVector.call(this, value) + const t = vectorBaseType2TypedArray[this.schema['baseType']] + if (!(value instanceof t)) { + throw new Error(`Not a ${t.name}`) + } + if ((value as TypedArray).byteLength > this.vecLen * this.compSize) { + throw new Error('Vector too long') + } } override pushValue( buf: AutoSizedUint8Array, @@ -96,12 +98,16 @@ export const colvec = class ColVec extends BasePropDef { buf.set(v, buf.length) } override pushSelvaSchema(buf: AutoSizedUint8Array) { + const defaultValue = this.schema['default'] pushSelvaSchemaColvec(buf, { type: PropTypeSelva.colVec, vecLen: this.vecLen, compSize: this.compSize, - hasDefault: 0, - //hasDefault: this.schema.default, // TODO default + hasDefault: ~~!!defaultValue, }) + if (defaultValue) { + const v = new Uint8Array(defaultValue.buffer, 0, this.vecLen * this.compSize) + buf.set(v, buf.length) + } } } diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 69a04282f0..13faf55ec1 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -2,7 +2,7 @@ import { deepEqual, testDb, throws } from '../../shared/index.js' import test from '../../shared/test.js' import assert from 'node:assert' -await test('wrong type', async (t) => { +await test('incorrect values', async (t) => { const db = await testDb(t, { types: { thing: { @@ -16,6 +16,12 @@ await test('wrong type', async (t) => { vec: new Float64Array([1.1, 2.2, 3.3]), }), ) + + throws(() => + db.create('thing', { + vec: new Float32Array([1.1, 2.2, 3.3, 4,2]), + }), + ) }) await test('default', async (t) => { @@ -31,12 +37,47 @@ await test('default', async (t) => { }, }, }) - // TODO Test also wrong size for default const id1 = await db.create('thing', {}) deepEqual(await db.query2('thing', id1).get(), { id: id1, vec: new Float32Array([1, 2.5, 3]) }) }) +await test('default colvec', async (t) => { + const db = await testDb(t, { + types: { + thing: { + insertOnly: true, + props: { + vec: { + type: 'colvec', + size: 3, + baseType: 'float32', + default: new Float32Array([1, 2.5, 3]) + }, + } + }, + }, + }) + + const id1 = await db.create('thing', {}) + deepEqual(await db.query2('thing', id1).get(), { id: id1, vec: new Float32Array([1, 2.5, 3]) }) +}) + +await test.skip('incorrect default', async (t) => { + throws(() => testDb(t, { + types: { + thing: { + vec: { + type: 'vector', + size: 3, + baseType: 'float32', + default: new Float32Array([1, 2.5, 3, 4.3]) + }, + }, + }, + })) +}) + await test('modify vector', async (t) => { const db = await testDb(t, { types: { From 70030f893fa633521f947662ebaa89c898cabe4f Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 11:21:44 +0100 Subject: [PATCH 370/449] more enums --- native/query/multiple/iterate.zig | 17 +++++++-- native/query/multiple/references.zig | 49 +++++++++++++++++++++++-- native/types.zig | 10 +++++ src/db-query/ast/ast.ts | 1 + src/db-query/ast/iteratorType.ts | 55 ++++++++++++++++++++++++---- src/db-query/ast/multiple.ts | 12 +++--- src/zigTsExports.ts | 24 ++++++++++++ test/query-ast/include.ts | 20 +++++----- 8 files changed, 158 insertions(+), 30 deletions(-) diff --git a/native/query/multiple/iterate.zig b/native/query/multiple/iterate.zig index 556ac8202f..4acfbecb20 100644 --- a/native/query/multiple/iterate.zig +++ b/native/query/multiple/iterate.zig @@ -76,13 +76,18 @@ pub fn edge( var filter: []u8 = undefined; var edgeFilter: []u8 = undefined; - if (It == t.QueryIteratorType.filter) { + if (It == t.QueryIteratorType.filter or + It == t.QueryIteratorType.edgeFilterAndFilterOnEdge or + It == t.QueryIteratorType.edgeIncludeFilterAndFilterOnEdge) + { filter = utils.sliceNext(header.filterSize, q, i); try Filter.prepare(filter, ctx, typeEntry); } if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or - It == t.QueryIteratorType.edgeFilterOnEdge) + It == t.QueryIteratorType.edgeFilterOnEdge or + It == t.QueryIteratorType.edgeFilterAndFilterOnEdge or + It == t.QueryIteratorType.edgeIncludeFilterAndFilterOnEdge) { edgeFilter = utils.sliceNext(header.edgeFilterSize, q, i); try Filter.prepare(edgeFilter, ctx, typeEntry); @@ -98,14 +103,18 @@ pub fn edge( } while (it.nextRef()) |ref| { - if (It == t.QueryIteratorType.filter) { + if (It == t.QueryIteratorType.filter or + It == t.QueryIteratorType.edgeFilterAndFilterOnEdge or + It == t.QueryIteratorType.edgeIncludeFilterAndFilterOnEdge) + { if (!try Filter.filter(ref.node, ctx, filter)) { continue; } } if (It == t.QueryIteratorType.edgeIncludeFilterOnEdge or - It == t.QueryIteratorType.edgeFilterOnEdge) + It == t.QueryIteratorType.edgeFilterOnEdge or + It == t.QueryIteratorType.edgeFilterAndFilterOnEdge) { if (!try Filter.filter(ref.edge, ctx, edgeFilter)) { continue; diff --git a/native/query/multiple/references.zig b/native/query/multiple/references.zig index aa8fa8537a..e023c708b1 100644 --- a/native/query/multiple/references.zig +++ b/native/query/multiple/references.zig @@ -6,6 +6,7 @@ const Sort = @import("../../sort/sort.zig"); const References = @import("../../selva/references.zig"); const Query = @import("../common.zig"); const Iterate = @import("./iterate.zig"); +const std = @import("std"); inline fn referencesSort( comptime desc: bool, @@ -117,6 +118,7 @@ pub fn references( }, // -------------------- + // NAME THESE BETTER .edgeFilterOnEdge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); nodeCnt = try Iterate.edge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); @@ -127,12 +129,12 @@ pub fn references( }, .edgeFilterOnEdgeSort => { var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try Iterate.node(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try Iterate.edge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, .edgeFilterOnEdgeSortDesc => { var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try Iterate.node(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try Iterate.edge(.edgeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, // -------------------- @@ -177,6 +179,26 @@ pub fn references( it.deinit(); }, + .edgeFilterAndFilterOnEdge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + .edgeFilterAndFilterOnEdgeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + .edgeFilterAndFilterOnEdgeSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.edgeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeFilterAndFilterOnEdgeSortDesc => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.edgeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + // -------------------- + // -------------------- .edgeIncludeFilterOnEdge => { var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); @@ -188,12 +210,31 @@ pub fn references( }, .edgeIncludeFilterOnEdgeSort => { var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try Iterate.node(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try Iterate.edge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, .edgeIncludeFilterOnEdgeSortDesc => { var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); - nodeCnt = try Iterate.node(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + nodeCnt = try Iterate.edge(.edgeIncludeFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + // -------------------- + .edgeIncludeFilterAndFilterOnEdge => { + var it = try References.iterator(false, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeIncludeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeFilterAndFilterOnEdgeDesc => { + var it = try References.iterator(true, true, ctx.db, from, header.prop, fromType); + nodeCnt = try Iterate.edge(.edgeIncludeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + }, + .edgeIncludeFilterAndFilterOnEdgeSort => { + var it = try referencesSort(false, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.edgeIncludeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); + it.deinit(); + }, + .edgeIncludeFilterAndFilterOnEdgeSortDesc => { + var it = try referencesSort(true, true, ctx, q, from, fromType, i, &header, typeEntry); + nodeCnt = try Iterate.edge(.edgeIncludeFilterAndFilterOnEdge, ctx, q, &it, &header, typeEntry, i); it.deinit(); }, // -------------------- diff --git a/native/types.zig b/native/types.zig index cfa9e7e6a5..f5e5cc4615 100644 --- a/native/types.zig +++ b/native/types.zig @@ -660,6 +660,16 @@ pub const QueryIteratorType = enum(u8) { edgeFilterOnEdgeSort = 62, edgeFilterOnEdgeSortDesc = 63, + edgeIncludeFilterAndFilterOnEdge = 70, + edgeIncludeFilterAndFilterOnEdgeDesc = 71, + edgeIncludeFilterAndFilterOnEdgeSort = 72, + edgeIncludeFilterAndFilterOnEdgeSortDesc = 73, + + edgeFilterAndFilterOnEdge = 80, + edgeFilterAndFilterOnEdgeDesc = 81, + edgeFilterAndFilterOnEdgeSort = 82, + edgeFilterAndFilterOnEdgeSortDesc = 83, + // default search search = 120, searchFilter = 121, diff --git a/src/db-query/ast/ast.ts b/src/db-query/ast/ast.ts index 36d051e262..3f96d85164 100644 --- a/src/db-query/ast/ast.ts +++ b/src/db-query/ast/ast.ts @@ -43,6 +43,7 @@ export const EdgeStrategy = { noEdge: 0, edgeOnly: 1, mixed: 2, + edgeAndProps: 3, } as const export type EdgeStrategyEnum = (typeof EdgeStrategy)[keyof typeof EdgeStrategy] diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index ab324d6460..4fe4ca67ef 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -10,7 +10,7 @@ import { QueryIteratorType, QueryIteratorTypeInverse, } from '../../zigTsExports.js' -import { QueryAst } from './ast.js' +import { EdgeStrategy, QueryAst } from './ast.js' export const getIteratorType = ( header: QueryHeader, @@ -80,17 +80,58 @@ export const getIteratorType = ( QueryIteratorType.edgeFilterOnEdge, ) - if (header.edgeFilterSize > 0 && header.filterSize === 0) { - if (header.edgeSize === 0) { - if (base === QueryIteratorType.edge) { - base = QueryIteratorType.edgeFilterOnEdge + if ( + ast.filter?.edgeStrategy === EdgeStrategy.edgeOnly || + ast.filter?.edgeStrategy === EdgeStrategy.edgeAndProps + ) { + if (ast.filter?.edgeStrategy === EdgeStrategy.edgeAndProps) { + if (header.edgeSize === 0) { + if (base === QueryIteratorType.edgeFilter) { + base = QueryIteratorType.edgeFilterAndFilterOnEdge + } else if (base === QueryIteratorType.edgeDescFilter) { + base = QueryIteratorType.edgeFilterAndFilterOnEdgeDesc + } else if (base === QueryIteratorType.edgeFilterSort) { + base = QueryIteratorType.edgeFilterAndFilterOnEdgeSort + } else if (base === QueryIteratorType.edgeDescFilterSort) { + base = QueryIteratorType.edgeFilterAndFilterOnEdgeSortDesc + } + } else { + if (base === QueryIteratorType.edgeIncludeFilter) { + base = QueryIteratorType.edgeIncludeFilterAndFilterOnEdge + } else if (base === QueryIteratorType.edgeIncludeDescFilter) { + base = QueryIteratorType.edgeIncludeFilterAndFilterOnEdgeDesc + } else if (base === QueryIteratorType.edgeIncludeFilterSort) { + base = QueryIteratorType.edgeIncludeFilterAndFilterOnEdgeSort + } else if (base === QueryIteratorType.edgeIncludeDescFilterSort) { + base = QueryIteratorType.edgeIncludeFilterAndFilterOnEdgeSortDesc + } } } else { - if (base === QueryIteratorType.edgeInclude) { - base = QueryIteratorType.edgeIncludeFilterOnEdge + if (header.edgeSize === 0) { + if (base === QueryIteratorType.edge) { + base = QueryIteratorType.edgeFilterOnEdge + } else if (base === QueryIteratorType.edgeDesc) { + base = QueryIteratorType.edgeFilterOnEdgeDesc + } else if (base === QueryIteratorType.edgeSort) { + base = QueryIteratorType.edgeFilterOnEdgeSort + } else if (base === QueryIteratorType.edgeDescSort) { + base = QueryIteratorType.edgeFilterOnEdgeSortDesc + } + } else { + if (base === QueryIteratorType.edgeInclude) { + base = QueryIteratorType.edgeIncludeFilterOnEdge + } else if (base === QueryIteratorType.edgeIncludeDesc) { + base = QueryIteratorType.edgeIncludeFilterOnEdgeDesc + } else if (base === QueryIteratorType.edgeIncludeSort) { + base = QueryIteratorType.edgeIncludeFilterOnEdgeSort + } else if (base === QueryIteratorType.edgeIncludeDescSort) { + base = QueryIteratorType.edgeIncludeFilterOnEdgeSortDesc + } } } } + console.log('----->', QueryIteratorTypeInverse[base]) + return base as QueryIteratorTypeEnum } diff --git a/src/db-query/ast/multiple.ts b/src/db-query/ast/multiple.ts index 74515e0eec..e5659544e0 100644 --- a/src/db-query/ast/multiple.ts +++ b/src/db-query/ast/multiple.ts @@ -92,15 +92,19 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { pushSortHeader(ctx.query, sort(ast, ctx, prop.ref!, prop)) } - if (ast.filter && ast.filter.edgeStrategy == EdgeStrategy.noEdge) { - // step 1 + if ( + ast.filter && + (ast.filter.edgeStrategy == EdgeStrategy.noEdge || + ast.filter.edgeStrategy == EdgeStrategy.edgeAndProps) + ) { const filterSize = filter(ast.filter, ctx, prop.ref!) props.filterSize(ctx.query.data, filterSize, headerIndex) } if ( ast.filter && - ast.filter.edgeStrategy == EdgeStrategy.edgeOnly && + (ast.filter.edgeStrategy == EdgeStrategy.edgeOnly || + ast.filter.edgeStrategy == EdgeStrategy.edgeAndProps) && ast.filter.edges ) { const edges = prop.edges @@ -109,8 +113,6 @@ export const references = (ast: QueryAst, ctx: Ctx, prop: PropDef) => { } props.edgeTypeId(ctx.query.data, edges.id, headerIndex) const filterSize = filter(ast.filter.edges, ctx, prop.edges!) - - console.log('EDGE FILTER SIZE', filterSize) props.edgeFilterSize(ctx.query.data, filterSize, headerIndex) } diff --git a/src/zigTsExports.ts b/src/zigTsExports.ts index ba8629c5b8..d9ce35f1a9 100644 --- a/src/zigTsExports.ts +++ b/src/zigTsExports.ts @@ -2250,6 +2250,14 @@ export const QueryIteratorType = { edgeFilterOnEdgeDesc: 61, edgeFilterOnEdgeSort: 62, edgeFilterOnEdgeSortDesc: 63, + edgeIncludeFilterAndFilterOnEdge: 70, + edgeIncludeFilterAndFilterOnEdgeDesc: 71, + edgeIncludeFilterAndFilterOnEdgeSort: 72, + edgeIncludeFilterAndFilterOnEdgeSortDesc: 73, + edgeFilterAndFilterOnEdge: 80, + edgeFilterAndFilterOnEdgeDesc: 81, + edgeFilterAndFilterOnEdgeSort: 82, + edgeFilterAndFilterOnEdgeSortDesc: 83, search: 120, searchFilter: 121, vec: 130, @@ -2293,6 +2301,14 @@ export const QueryIteratorTypeInverse = { 61: 'edgeFilterOnEdgeDesc', 62: 'edgeFilterOnEdgeSort', 63: 'edgeFilterOnEdgeSortDesc', + 70: 'edgeIncludeFilterAndFilterOnEdge', + 71: 'edgeIncludeFilterAndFilterOnEdgeDesc', + 72: 'edgeIncludeFilterAndFilterOnEdgeSort', + 73: 'edgeIncludeFilterAndFilterOnEdgeSortDesc', + 80: 'edgeFilterAndFilterOnEdge', + 81: 'edgeFilterAndFilterOnEdgeDesc', + 82: 'edgeFilterAndFilterOnEdgeSort', + 83: 'edgeFilterAndFilterOnEdgeSortDesc', 120: 'search', 121: 'searchFilter', 130: 'vec', @@ -2336,6 +2352,14 @@ export const QueryIteratorTypeInverse = { edgeFilterOnEdgeDesc, edgeFilterOnEdgeSort, edgeFilterOnEdgeSortDesc, + edgeIncludeFilterAndFilterOnEdge, + edgeIncludeFilterAndFilterOnEdgeDesc, + edgeIncludeFilterAndFilterOnEdgeSort, + edgeIncludeFilterAndFilterOnEdgeSortDesc, + edgeFilterAndFilterOnEdge, + edgeFilterAndFilterOnEdgeDesc, + edgeFilterAndFilterOnEdgeSort, + edgeFilterAndFilterOnEdgeSortDesc, search, searchFilter, vec, diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 46609e0315..9ae8d45b8e 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -75,7 +75,7 @@ await test('include', async (t) => { const rand = fastPrng() - for (let i = 0; i < 1; i++) { + for (let i = 0; i < 10; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -146,8 +146,8 @@ await test('include', async (t) => { y: { include: {} }, name: { include: {} }, friends: { - // order: 'asc', - // sort: { prop: '$level' }, // can just be the prop? + order: 'desc', + sort: { prop: '$level' }, // can just be the prop? props: { name: { include: {} }, y: { include: {} }, @@ -158,12 +158,12 @@ await test('include', async (t) => { }, }, filter: { - // props: { - // y: { - // ops: [{ op: '>', val: 6 }], - // }, - // }, - edgeStrategy: EdgeStrategy.edgeOnly, + edgeStrategy: EdgeStrategy.edgeAndProps, + props: { + y: { + ops: [{ op: '>', val: 5 }], + }, + }, edges: { props: { $level: { @@ -197,7 +197,7 @@ await test('include', async (t) => { debugBuffer(deflateSync(ctx.query).toString('hex')) const queries: any = [] - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 1; i++) { const x = ctx.query.slice(0) writeUint32(x, i + 1, 0) queries.push(x) From 1b4b3b0ddf7ea0e2bf2739a83506a922ff54cdbf Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 11:23:11 +0100 Subject: [PATCH 371/449] add logs, clean up --- native/query/multiple/iterate.zig | 2 +- native/query/multiple/references.zig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/native/query/multiple/iterate.zig b/native/query/multiple/iterate.zig index 4acfbecb20..72d3a0111a 100644 --- a/native/query/multiple/iterate.zig +++ b/native/query/multiple/iterate.zig @@ -22,7 +22,7 @@ pub fn node( ctx: *Query.QueryCtx, q: []u8, it: anytype, - header: *const t.QueryHeader, // make this type + header: *const t.QueryHeader, typeEntry: Node.Type, i: *usize, ) !u32 { diff --git a/native/query/multiple/references.zig b/native/query/multiple/references.zig index e023c708b1..87a824e24a 100644 --- a/native/query/multiple/references.zig +++ b/native/query/multiple/references.zig @@ -8,6 +8,7 @@ const Query = @import("../common.zig"); const Iterate = @import("./iterate.zig"); const std = @import("std"); +// Has to be inlined to force stack allocation inline fn referencesSort( comptime desc: bool, comptime edge: bool, From f9e4fcd615656238ec748d5663d09d568ea6fe95 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 11:25:23 +0100 Subject: [PATCH 372/449] perf test --- test/query-ast/include.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 9ae8d45b8e..98a8b04d89 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -106,7 +106,7 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - range: { start: 0, end: 1e6 }, + range: { start: 0, end: 5 }, // target: b, // order: 'desc', // sort: { prop: 'y' }, @@ -197,13 +197,13 @@ await test('include', async (t) => { debugBuffer(deflateSync(ctx.query).toString('hex')) const queries: any = [] - for (let i = 0; i < 1; i++) { + for (let i = 0; i < 10; i++) { const x = ctx.query.slice(0) writeUint32(x, i + 1, 0) queries.push(x) } - await perf.skip( + await perf( async () => { const q: any = [] for (let i = 0; i < 10; i++) { From 5acca2e0213deca830902ddfd5d94fe12bd62b77 Mon Sep 17 00:00:00 2001 From: Jim de Beer Date: Mon, 23 Feb 2026 11:40:26 +0100 Subject: [PATCH 373/449] fix problem with edge --- native/query/multiple/iterate.zig | 4 +++- test/query-ast/include.ts | 26 ++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/native/query/multiple/iterate.zig b/native/query/multiple/iterate.zig index 72d3a0111a..638d7e009e 100644 --- a/native/query/multiple/iterate.zig +++ b/native/query/multiple/iterate.zig @@ -125,7 +125,9 @@ pub fn edge( try ctx.thread.query.append(Node.getNodeId(ref.node)); try Include.include(ref.node, ctx, nestedQuery, typeEntry); - if (It != t.QueryIteratorType.edgeFilterOnEdge) { + if (It != t.QueryIteratorType.edgeFilterOnEdge and + It != t.QueryIteratorType.edgeFilterAndFilterOnEdge) + { try ctx.thread.query.append(t.ReadOp.edge); const edgesByteSizeIndex = try ctx.thread.query.reserve(4); const edgeStartIndex = ctx.thread.query.index; diff --git a/test/query-ast/include.ts b/test/query-ast/include.ts index 98a8b04d89..198cdd3376 100644 --- a/test/query-ast/include.ts +++ b/test/query-ast/include.ts @@ -75,7 +75,7 @@ await test('include', async (t) => { const rand = fastPrng() - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 1e6; i++) { client.create('user', { name: `mr snurf ${i}`, y: i, @@ -106,7 +106,7 @@ await test('include', async (t) => { const ast: QueryAst = { type: 'user', - range: { start: 0, end: 5 }, + range: { start: 0, end: 1e6 }, // target: b, // order: 'desc', // sort: { prop: 'y' }, @@ -146,18 +146,19 @@ await test('include', async (t) => { y: { include: {} }, name: { include: {} }, friends: { - order: 'desc', - sort: { prop: '$level' }, // can just be the prop? + // order: 'desc', + // sort: { prop: '$level' }, // can just be the prop? props: { name: { include: {} }, y: { include: {} }, }, - edges: { - props: { - $level: { include: {} }, - }, - }, + // edges: { + // props: { + // $level: { include: {} }, + // }, + // }, filter: { + // wrong include (if no edges provided) edgeStrategy: EdgeStrategy.edgeAndProps, props: { y: { @@ -209,11 +210,12 @@ await test('include', async (t) => { for (let i = 0; i < 10; i++) { q.push(db.server.getQueryBuf(queries[i])) } - await Promise.all(q) + const x = await Promise.all(q) + // console.log(x) }, 'filter speed', { - repeat: 100, + repeat: 10, }, ) @@ -223,7 +225,7 @@ await test('include', async (t) => { const obj = resultToObject(ctx.readSchema, result, result.byteLength - 4) - console.dir(obj, { depth: 10 }) + // console.dir(obj, { depth: 10 }) await wait(1000) From 34d03bd31890ea686126bcccee1bf9715833fb72 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 11:49:18 +0100 Subject: [PATCH 374/449] spec -> head in schema --- clibs/lib/selva/schema.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index 94e17f6a3c..89376abd90 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -187,31 +187,31 @@ static int type2fs_refs(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSche field_t inverse_field; node_type_t edge_node_type; uint32_t capped; - } __packed spec; + } __packed head; - static_assert(sizeof(spec) == 11); + static_assert(sizeof(head) == 11); - if (ctx->len < sizeof(spec)) { + if (ctx->len < sizeof(head)) { return SELVA_EINVAL; } - memcpy(&spec, buf, sizeof(spec)); + memcpy(&head, buf, sizeof(head)); - enum EdgeFieldConstraintFlag flags = spec.flags & (EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT); + enum EdgeFieldConstraintFlag flags = head.flags & (EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT); *fs = (struct SelvaFieldSchema){ .field = field, .type = type, .edge_constraint = { .flags = flags, - .inverse_field = spec.inverse_field, - .dst_node_type = spec.dst_node_type, - .edge_node_type = spec.edge_node_type, - .limit = spec.capped, + .inverse_field = head.inverse_field, + .dst_node_type = head.dst_node_type, + .edge_node_type = head.edge_node_type, + .limit = head.capped, }, }; - return sizeof(spec); + return sizeof(head); } static int type2fs_reference(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *schema, field_t field) @@ -247,29 +247,29 @@ static int type2fs_colvec(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSc uint16_t vec_len; /*!< Length of a single vector. */ uint16_t comp_size; /*!< Component size in the vector. */ schema_bool_t has_default; - } __packed spec; + } __packed head; size_t off = 0; - if (ctx->len < sizeof(spec)) { + if (ctx->len < sizeof(head)) { return SELVA_EINVAL; } - memcpy(&spec, ctx->buf + off, sizeof(spec)); - off += sizeof(spec); + memcpy(&head, ctx->buf + off, sizeof(head)); + off += sizeof(head); *fs = (struct SelvaFieldSchema){ .field = field, .type = SELVA_FIELD_TYPE_COLVEC, .default_off = 0, .colvec = { - .vec_len = spec.vec_len, - .comp_size = spec.comp_size, + .vec_len = head.vec_len, + .comp_size = head.comp_size, .index = ctx->colvec_index++, }, }; - if (spec.has_default) { - size_t vec_size = spec.vec_len * spec.comp_size; + if (head.has_default) { + size_t vec_size = head.vec_len * head.comp_size; if (ctx->len < off + vec_size) { return SELVA_EINVAL; From d382131eca0699131107b0392fb1dfca57214fd0 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 12:00:54 +0100 Subject: [PATCH 375/449] Use packed struct for the schema hdr --- clibs/lib/selva/schema.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/clibs/lib/selva/schema.c b/clibs/lib/selva/schema.c index 89376abd90..773552fdf6 100644 --- a/clibs/lib/selva/schema.c +++ b/clibs/lib/selva/schema.c @@ -21,12 +21,13 @@ #include "io.h" #include "schema.h" -#define SCHEMA_MIN_SIZE 8 -#define SCHEMA_OFF_BLOCK_CAPACITY 0 /*!< u32 */ -#define SCHEMA_OFF_NR_FIELDS 4 /*!< u8 */ -#define SCHEMA_OFF_NR_FIXED_FIELDS 5 /*!< u8 */ -#define SCHEMA_OFF_NR_VIRTUAL_FIELDS 6 /*!< u8 */ -#define SCHEMA_OFF_VERSION 7 /*!< u8 */ +struct SelvaSchemaHeader { + uint32_t block_capacity; + uint8_t nr_fields; + uint8_t nr_fixed_fields; + uint8_t nr_virtual_fields; + uint8_t sdb_version; +} __packed; struct schemabuf_parser_ctx { struct SelvaTypeEntry *te; @@ -323,19 +324,18 @@ static struct schemabuf_parser { int schemabuf_get_info(struct schema_info *nfo, const uint8_t *buf, size_t len) { - uint32_t block_capacity; + struct SelvaSchemaHeader head; - if (len < SCHEMA_MIN_SIZE) { + if (len < sizeof(struct SelvaSchemaHeader)) { return SELVA_EINVAL; } - memcpy(&block_capacity, buf + SCHEMA_OFF_BLOCK_CAPACITY, sizeof(block_capacity)); - + memcpy(&head, buf, sizeof(head)); *nfo = (struct schema_info){ - .block_capacity = block_capacity, - .nr_fields = buf[SCHEMA_OFF_NR_FIELDS], - .nr_fixed_fields = buf[SCHEMA_OFF_NR_FIXED_FIELDS], - .nr_virtual_fields = buf[SCHEMA_OFF_NR_VIRTUAL_FIELDS], + .block_capacity = head.block_capacity, + .nr_fields = head.nr_fields, + .nr_fixed_fields = head.nr_fixed_fields, + .nr_virtual_fields = head.nr_virtual_fields, }; if (nfo->nr_fixed_fields > nfo->nr_fields || @@ -482,7 +482,7 @@ static int parse2(struct schemabuf_parser_ctx *ctx, struct SelvaFieldsSchema *fi } make_field_map_template(fields_schema); - make_fixed_fields_template(fields_schema, buf - SCHEMA_MIN_SIZE); + make_fixed_fields_template(fields_schema, buf - sizeof(struct SelvaSchemaHeader)); return 0; } @@ -501,21 +501,21 @@ int schemabuf_parse_ns(struct SelvaNodeSchema *ns, const uint8_t *buf, size_t le .alias_index = 0, }; - if (len < SCHEMA_MIN_SIZE) { + if (len < sizeof(struct SelvaSchemaHeader)) { return SELVA_EINVAL; } /* We just assume that fields_schema is allocated properly. */ - ctx.version = buf[SCHEMA_OFF_VERSION]; - fields_schema->nr_fields = buf[SCHEMA_OFF_NR_FIELDS]; - fields_schema->nr_fixed_fields = buf[SCHEMA_OFF_NR_FIXED_FIELDS]; + ctx.version = buf[offsetof(struct SelvaSchemaHeader, sdb_version)]; + fields_schema->nr_fields = buf[offsetof(struct SelvaSchemaHeader, nr_fields)]; + fields_schema->nr_fixed_fields = buf[offsetof(struct SelvaSchemaHeader, nr_fixed_fields)]; if (ctx.version > max_version) { /* Can't load a schema created with a newer version. */ return SELVA_ENOTSUP; } - int err = parse2(&ctx, fields_schema, buf + SCHEMA_MIN_SIZE, len - SCHEMA_MIN_SIZE); + int err = parse2(&ctx, fields_schema, buf + sizeof(struct SelvaSchemaHeader), len - sizeof(struct SelvaSchemaHeader)); ns->nr_alias_fields = ctx.alias_index; ns->nr_colvec_fields = ctx.colvec_index; From a95f945e7961c83429cc18fc5edb0599fa1a9ff1 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 14:07:05 +0100 Subject: [PATCH 376/449] prettier --- test/modify/props/vector.ts | 45 +++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/test/modify/props/vector.ts b/test/modify/props/vector.ts index 13faf55ec1..bb0ee55301 100644 --- a/test/modify/props/vector.ts +++ b/test/modify/props/vector.ts @@ -19,7 +19,7 @@ await test('incorrect values', async (t) => { throws(() => db.create('thing', { - vec: new Float32Array([1.1, 2.2, 3.3, 4,2]), + vec: new Float32Array([1.1, 2.2, 3.3, 4, 2]), }), ) }) @@ -32,14 +32,17 @@ await test('default', async (t) => { type: 'vector', size: 3, baseType: 'float32', - default: new Float32Array([1, 2.5, 3]) + default: new Float32Array([1, 2.5, 3]), }, }, }, }) const id1 = await db.create('thing', {}) - deepEqual(await db.query2('thing', id1).get(), { id: id1, vec: new Float32Array([1, 2.5, 3]) }) + deepEqual(await db.query2('thing', id1).get(), { + id: id1, + vec: new Float32Array([1, 2.5, 3]), + }) }) await test('default colvec', async (t) => { @@ -52,30 +55,35 @@ await test('default colvec', async (t) => { type: 'colvec', size: 3, baseType: 'float32', - default: new Float32Array([1, 2.5, 3]) + default: new Float32Array([1, 2.5, 3]), }, - } + }, }, }, }) const id1 = await db.create('thing', {}) - deepEqual(await db.query2('thing', id1).get(), { id: id1, vec: new Float32Array([1, 2.5, 3]) }) + deepEqual(await db.query2('thing', id1).get(), { + id: id1, + vec: new Float32Array([1, 2.5, 3]), + }) }) await test.skip('incorrect default', async (t) => { - throws(() => testDb(t, { - types: { - thing: { - vec: { - type: 'vector', - size: 3, - baseType: 'float32', - default: new Float32Array([1, 2.5, 3, 4.3]) + throws(() => + testDb(t, { + types: { + thing: { + vec: { + type: 'vector', + size: 3, + baseType: 'float32', + default: new Float32Array([1, 2.5, 3, 4.3]), + }, }, }, - }, - })) + }), + ) }) await test('modify vector', async (t) => { @@ -175,7 +183,10 @@ await test('modify colvec', async (t) => { await db.update('thing', id1, { vec: null, }) - deepEqual(await db.query2('thing', id1).get(), { id: 1, vec: new Float64Array([0, 0, 0]) }) + deepEqual(await db.query2('thing', id1).get(), { + id: 1, + vec: new Float64Array([0, 0, 0]), + }) }) await test('modify vector on edge', async (t) => { From 99bbb5ae569b9b2fc607a0655abbef5f795f4221 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 14:13:46 +0100 Subject: [PATCH 377/449] Fix save test --- test/save/save.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/save/save.ts b/test/save/save.ts index 9816026781..ae8dbf76d1 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -763,7 +763,7 @@ await test('no mismatch', async (t) => { await db.start({ clean: true }) t.after(() => db.stop(true)) - const client = await db.setSchema({ + const schema = { types: { user: { props: { @@ -771,7 +771,8 @@ await test('no mismatch', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) await client.create('user', { name: 'xxx', @@ -782,15 +783,17 @@ await test('no mismatch', async (t) => { const db2 = new BasedDb({ path: t.tmp, }) - t.after(() => t.backup(db2)) - await db2.start() - await db2.create('user', { - name: 'xxx', + + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), }) - await db2.create('user', { + await client2.create('user', { + name: 'xxx', + }) + await client2.create('user', { name: 'xxx2', }) From e904ac1ea8cf5324109e6a41d4b744cb8f41d7c7 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 15:12:05 +0100 Subject: [PATCH 378/449] Use DbClient --- test/shared/test.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/shared/test.ts b/test/shared/test.ts index 40b96b1ade..c6dd6ff432 100644 --- a/test/shared/test.ts +++ b/test/shared/test.ts @@ -1,7 +1,7 @@ import { styleText } from 'node:util' import { fileURLToPath } from 'url' import { join, dirname, resolve } from 'path' -import { BasedDb } from '../../src/index.js' +import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' import { deepEqual } from './assert.js' import { wait, bufToHex } from '../../src/utils/index.js' import fs from 'node:fs/promises' @@ -72,17 +72,19 @@ const test: { return } - const fields = ['*', '**'] - const make = async (db) => { + const make = async (db: BasedDb) => { + const client = new DbClient({ + hooks: getDefaultHooks(db.server), + }) const checksums: any[] = [] const data: any[] = [] const counts: any[] = [] for (const type in db.server.schema?.types) { - let x = await db.query(type).include(fields).get() - checksums.push(x.checksum) - data.push(x.toObject()) - counts.push(await db.query(type).count().get().toObject().count) + let x = await client.query2(type).include('*', '**').get() + checksums.push(x['checksum']) + data.push(x) + counts.push((await client.query2(type).count().get()).count) } return [checksums, data, counts] From 4b640cdf1b47c61f585f5543d9fa1b632a5c80f1 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 15:12:22 +0100 Subject: [PATCH 379/449] Use DbClient --- test/save/saveEdge.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/save/saveEdge.ts b/test/save/saveEdge.ts index 9c88a7bfb3..e629cf142e 100644 --- a/test/save/saveEdge.ts +++ b/test/save/saveEdge.ts @@ -9,7 +9,7 @@ await test('save edge', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -23,8 +23,8 @@ await test('save edge', async (t) => { }, }) - const user1 = await db.create('user', {}) - const user2 = await db.create('user', { + const user1 = await client.create('user', {}) + const user2 = await client.create('user', { bestFriend: { id: user1, $uint8: 21, @@ -33,14 +33,14 @@ await test('save edge', async (t) => { await db.save() - await db.update('user', user2, { + await client.update('user', user2, { bestFriend: { id: user1, $uint8: 42, }, }) - deepEqual(await db.query('user', user2).include('**').get(), { + deepEqual(await client.query('user', user2).include('**').get(), { id: 2, bestFriend: { id: 1, From 554b7e2de1112018b7a5ee31edfa93c7eabfc0b0 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Mon, 23 Feb 2026 15:24:47 +0100 Subject: [PATCH 380/449] Use DbClient --- test/save/blockHash.ts | 15 +++--- test/save/saveInterval.ts | 21 ++++---- test/save/saveRange.ts | 111 ++++++++++++++++++++------------------ 3 files changed, 78 insertions(+), 69 deletions(-) diff --git a/test/save/blockHash.ts b/test/save/blockHash.ts index 99ea5757ee..e261d0304d 100644 --- a/test/save/blockHash.ts +++ b/test/save/blockHash.ts @@ -21,7 +21,7 @@ await test('isomorphic types have equal hashes', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - await db.setSchema({ + const schema = { types: { article: { title: 'string', @@ -32,23 +32,24 @@ await test('isomorphic types have equal hashes', async (t) => { body: 'string', }, }, - }) + } as const + const client = await db.setSchema(schema) for (let i = 0; i < 200_000; i++) { - db.create('article', { + client.create('article', { title: 'party in the house', body: 'there was', }) - db.create('story', { + client.create('story', { title: 'party in the house', body: 'there was', }) } - await db.drain() + await client.drain() deepEqual( - (await db.query('article').get()).checksum, - (await db.query('story').get()).checksum, + (await client.query('article').get()).checksum, + (await client.query('story').get()).checksum, ) assert( native.equals( diff --git a/test/save/saveInterval.ts b/test/save/saveInterval.ts index e86b3fbaa9..93e6397628 100644 --- a/test/save/saveInterval.ts +++ b/test/save/saveInterval.ts @@ -1,5 +1,5 @@ import { setTimeout } from 'node:timers/promises' -import { BasedDb } from '../../src/index.js' +import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' @@ -11,7 +11,7 @@ await test('saveInterval', async (t) => { await db.start({ clean: true }) t.after(() => db.destroy()) - await db.setSchema({ + const schema = { types: { user: { props: { @@ -20,23 +20,23 @@ await test('saveInterval', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) - db.create('user', { + client.create('user', { externalId: 'cool', potato: 'fries', }) - db.create('user', { + client.create('user', { externalId: 'cool2', potato: 'wedge', }) - await db.drain() - + await client.drain() await setTimeout(1e3) - const res1 = await db.query('user').get().toObject() + const res1 = await client.query('user').get().toObject() await db.stop(true) @@ -45,9 +45,12 @@ await test('saveInterval', async (t) => { }) await db2.start() t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) await db2.schemaIsSet() - const res2 = await db2.query('user').get().toObject() + const res2 = await client2.query('user').get().toObject() deepEqual(res1, res2) }) diff --git a/test/save/saveRange.ts b/test/save/saveRange.ts index 386415a58e..a91756fe20 100644 --- a/test/save/saveRange.ts +++ b/test/save/saveRange.ts @@ -1,5 +1,5 @@ import { readdir } from 'node:fs/promises' -import { BasedDb } from '../../src/index.js' +import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' import test from '../shared/test.js' import { italy } from '../shared/examples.js' import { deepEqual, equal, notEqual } from '../shared/assert.js' @@ -14,7 +14,7 @@ await test('save simple range', async (t) => { return db.destroy() }) - await db.setSchema({ + const schema = { types: { user: { props: { @@ -25,7 +25,8 @@ await test('save simple range', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) const N = 800_000 const slen = 80 @@ -40,7 +41,7 @@ await test('save simple range', async (t) => { xn1 ^= xn2 } - db.create('user', { + client.create('user', { age: i, name: 'mr flop ' + i, email: 'abuse@disaster.co.uk', @@ -48,18 +49,18 @@ await test('save simple range', async (t) => { }) } - await db.drain() + await client.drain() const save1_start = performance.now() await db.save() const save1_end = performance.now() const firstHash = await hashType(db.server, 'user') - db.update('user', 1, { + client.update('user', 1, { age: 1337, }) - await db.drain() - deepEqual(await db.query('user').include('age').range(0, 1).get(), [ + await client.drain() + deepEqual(await client.query('user').include('age').range(0, 1).get(), [ { id: 1, age: 1337, @@ -92,26 +93,30 @@ await test('save simple range', async (t) => { 'schema.bin', ]) - const load_start = performance.now() - const newDb = new BasedDb({ + //const load_start = performance.now() + const db2 = new BasedDb({ path: t.tmp, }) - await newDb.start() - t.after(() => newDb.destroy()) - const load_end = performance.now() + await db2.start() + t.after(() => db2.destroy()) + //const load_end = performance.now() + + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - const thirdHash = await hashType(newDb.server, 'user') + const thirdHash = await hashType(db2.server, 'user') notEqual(firstHash, secondHash) equal(secondHash, thirdHash) - deepEqual(await newDb.query('user').include('age').range(0, 1).get(), [ + deepEqual(await client2.query('user').include('age').range(0, 1).get(), [ { id: 1, age: 1337, }, ]) deepEqual( - await newDb + await client2 .query('user') .include('age') .range(200000, 200000 + 1) @@ -124,7 +129,7 @@ await test('save simple range', async (t) => { ], ) - deepEqual(await newDb.query('user').include('name').range(0, 2).get(), [ + deepEqual(await client2.query('user').include('name').range(0, 2).get(), [ { id: 1, name: 'mr flop 1', @@ -136,7 +141,7 @@ await test('save simple range', async (t) => { ]) deepEqual( - await newDb + await client2 .query('user') .include('name') .range(200_000, 200_000 + 2) @@ -161,7 +166,7 @@ await test('reference changes', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -180,22 +185,22 @@ await test('reference changes', async (t) => { }) const users = Array.from({ length: 3 }, (_, k) => - db.create('user', { + client.create('user', { name: 'mr flop ' + k, }), ) - await db.drain() + await client.drain() equal( await countDirtyBlocks(db.server), 1, 'creating new users creates a dirty range', ) - db.create('doc', { + client.create('doc', { title: 'The Wonders of AI', creator: users[0], }) - await db.drain() + await client.drain() equal( await countDirtyBlocks(db.server), 2, @@ -205,13 +210,13 @@ await test('reference changes', async (t) => { await db.save() equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') - const doc2 = db.create('doc', { + const doc2 = client.create('doc', { title: 'The Slops of AI', }) - const doc3 = db.create('doc', { + const doc3 = client.create('doc', { title: 'The Hype of AI', }) - await db.drain() + await client.drain() equal( await countDirtyBlocks(db.server), 1, @@ -221,8 +226,8 @@ await test('reference changes', async (t) => { equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') // Link user -> doc - db.update('user', users[1], { docs: [doc2] }) - await db.drain() + client.update('user', users[1], { docs: [doc2] }) + await client.drain() equal( await countDirtyBlocks(db.server), 2, @@ -232,8 +237,8 @@ await test('reference changes', async (t) => { equal(await countDirtyBlocks(db.server), 0, 'saving clears dirt') // Link doc -> user - db.update('doc', doc3, { creator: users[2] }) - await db.drain() + client.update('doc', doc3, { creator: users[2] }) + await client.drain() equal( await countDirtyBlocks(db.server), 2, @@ -250,7 +255,7 @@ await test('ref block moves', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { a: { props: { @@ -267,22 +272,22 @@ await test('ref block moves', async (t) => { }, }) - const a1 = await db.create('a', { x: 1 }) - const b1 = await db.create('b', { y: 1, aref: a1 }) + const a1 = await client.create('a', { x: 1 }) + const b1 = await client.create('b', { y: 1, aref: a1 }) for (let i = 0; i < 100_000; i++) { - db.create('a', { x: i % 256 }) - db.create('b', { y: i % 256 }) + client.create('a', { x: i % 256 }) + client.create('b', { y: i % 256 }) } - await db.drain() + await client.drain() for (let i = 0; i < 100_000; i++) { - db.delete('a', i + 2) - db.delete('b', i + 2) + client.delete('a', i + 2) + client.delete('b', i + 2) } - const an = await db.create('a', { x: 2 }) - const bn = await db.create('b', { y: 2, aref: an }) + const an = await client.create('a', { x: 2 }) + const bn = await client.create('b', { y: 2, aref: an }) await db.save() - await db.update('a', a1, { bref: bn }) + await client.update('a', a1, { bref: bn }) // t.backup will continue the test from here }) @@ -293,7 +298,7 @@ await test('ref removal', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { a: { props: { @@ -311,12 +316,12 @@ await test('ref removal', async (t) => { }) for (let i = 0; i < 100_000; i++) { - const a = db.create('a', { x: i % 256 }) - db.create('b', { y: 255 - (i % 256), aref: a }) + const a = client.create('a', { x: i % 256 }) + client.create('b', { y: 255 - (i % 256), aref: a }) } await db.save() for (let i = 0; i < 100_000; i++) { - db.update('a', i + 1, { bref: null }) + client.update('a', i + 1, { bref: null }) } // t.backup will continue the test from here @@ -329,7 +334,7 @@ await test('refs removal with delete', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { a: { props: { @@ -346,12 +351,12 @@ await test('refs removal with delete', async (t) => { }, }) - const a = db.create('a', { x: 13 }) + const a = client.create('a', { x: 13 }) for (let i = 0; i < 10; i++) { - db.create('b', { y: 255 - (i % 256), aref: a }) + client.create('b', { y: 255 - (i % 256), aref: a }) } await db.save() - db.delete('a', a) + client.delete('a', a) }) await test('large block gap', async (t) => { @@ -361,7 +366,7 @@ await test('large block gap', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { b: { blockCapacity: 10_000, @@ -372,11 +377,11 @@ await test('large block gap', async (t) => { }, }) - db.create('b', { + client.create('b', { y: 10, }) for (let i = 268435456; i < 268468224; i++) { - db.create( + client.create( 'b', { id: i, @@ -386,5 +391,5 @@ await test('large block gap', async (t) => { ) } - await db.drain() + await client.drain() }) From 7d1fe18666b863ab30f7f52ddbc23de5a2ec113d Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 23 Feb 2026 17:07:35 +0100 Subject: [PATCH 381/449] aggregates test --- src/db-client/query2/index.ts | 103 ++++++++++++++++++++-------------- test/aggregate/basic.ts | 11 +++- 2 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index e826ef77fc..ca5af610d3 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -427,8 +427,7 @@ class Query< GroupedKey > stddev

>( - prop: P | P[], - opts?: aggFnOptions, + ...args: [...P[], aggFnOptions] | [P, ...P[]] ): NextBranch< S, T, @@ -437,27 +436,36 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & - UnionToIntersection< - ExpandDotPath

- >, + Aggregate & UnionToIntersection>, GroupedKey > stddev( - prop: any, - opts?: any, + ...args: any[] ): NextBranch { - if (typeof prop === 'function') { - const fn = prop + if (typeof args[0] === 'function') { + const fn = args[0] fn((prop: string) => new Query(traverse(this.ast, prop))) return this as any } - if (!prop) { + if (args.length === 0) { throw new Error('Query: stddev expects at least one argument') } this.ast.stddev ??= { props: [] } - const props = Array.isArray(prop) ? prop : [prop] - this.ast.stddev.props.push(...(props as string[])) + let opts: any + let props: string[] + if ( + typeof args[args.length - 1] === 'object' && + !Array.isArray(args[args.length - 1]) + ) { + opts = args[args.length - 1] + props = args.slice(0, -1) + } else if (Array.isArray(args[0])) { + props = args[0] + opts = args[1] + } else { + props = args + } + this.ast.stddev.props.push(...props) if (opts?.mode) { this.ast.stddev.samplingMode = opts.mode } @@ -479,8 +487,7 @@ class Query< GroupedKey > var

>( - prop: P | P[], - opts?: aggFnOptions, + ...args: [...P[], aggFnOptions] | [P, ...P[]] ): NextBranch< S, T, @@ -489,27 +496,34 @@ class Query< SourceField, IsRoot, EdgeProps, - Aggregate & - UnionToIntersection< - ExpandDotPath

- >, + Aggregate & UnionToIntersection>, GroupedKey > - var( - prop: any, - opts?: any, - ): NextBranch { - if (typeof prop === 'function') { - const fn = prop + var(...args: any[]): NextBranch { + if (typeof args[0] === 'function') { + const fn = args[0] fn((prop: string) => new Query(traverse(this.ast, prop))) return this as any } - if (!prop) { + if (args.length === 0) { throw new Error('Query: var expects at least one argument') } this.ast.variance ??= { props: [] } - const props = Array.isArray(prop) ? prop : [prop] - this.ast.variance.props.push(...(props as string[])) + let opts: any + let props: string[] + if ( + typeof args[args.length - 1] === 'object' && + !Array.isArray(args[args.length - 1]) + ) { + opts = args[args.length - 1] + props = args.slice(0, -1) + } else if (Array.isArray(args[0])) { + props = args[0] + opts = args[1] + } else { + props = args + } + this.ast.variance.props.push(...props) if (opts?.mode) { this.ast.variance.samplingMode = opts.mode } @@ -687,16 +701,16 @@ export class BasedQuery2< } db: DbClient async get(): Promise< - [keyof Aggregate] extends [never] - ? IsSingle extends true - ? PickOutput< - S, - T, - ResolveInclude, K> - > | null - : PickOutput, K>>[] - : GroupedKey extends string - ? Record + [GroupedKey] extends [string] + ? Record + : [keyof Aggregate] extends [never] + ? IsSingle extends true + ? PickOutput< + S, + T, + ResolveInclude, K> + > | null + : PickOutput, K>>[] : Aggregate > { if ( @@ -797,11 +811,16 @@ export type ResolveIncludeArgs = T extends ( any, any, infer Aggregate, - any + infer GroupedKey > - ? [keyof Aggregate] extends [never] - ? { field: SourceField; select: K } - : { field: SourceField; select: { _aggregate: Aggregate } } + ? [GroupedKey] extends [string] + ? { + field: SourceField + select: { _aggregate: Record } + } + : [keyof Aggregate] extends [never] + ? { field: SourceField; select: K } + : { field: SourceField; select: { _aggregate: Aggregate } } : T extends string ? ResolveDotPath : T diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index e25f2ad5b0..871f037606 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -123,7 +123,7 @@ await test('top level count', async (t) => { db.create('sequence', { votes: [nl2] }) db.create('sequence', { votes: [au1] }) - // // top level ---------------------------------- + // top level ---------------------------------- deepEqual( await db.query2('vote').count().get(), @@ -159,6 +159,11 @@ await test('top level count', async (t) => { // 'count, with no match filtering, string value', // ) + console.log(await db.query2('vote').filter('NL', '=', 20).get()) // correct (1 item) + console.dir(await db.query2('vote').filter('NL', '=', 20).count().ast, { + depth: null, + }) // not correct (count: 3) + deepEqual( await db.query2('vote').filter('NL', '=', 20).count().get(), { count: 1 }, @@ -320,7 +325,7 @@ await test('two phase accumulation', async (t) => { 'stddev, branched References, groupBy', ) }) -/* + await test('numeric types', async (t) => { const db = await testDb(t, { types: { @@ -863,7 +868,7 @@ await test('numeric types', async (t) => { 'hmean, references, group by', ) }) - +/* await test('fixed length strings', async (t) => { const db = await testDb(t, { types: { From e4f5740c3e839610967798c64b8c7ea0c57e77c3 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 23 Feb 2026 17:10:18 +0100 Subject: [PATCH 382/449] update test --- test/aggregate/basic.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 871f037606..6c7daffd14 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -1,6 +1,7 @@ import test from '../shared/test.js' -import { throws, deepEqual } from '../shared/assert.js' +import { throws, deepEqual, equal } from '../shared/assert.js' import { testDb } from '../shared/index.js' +import { fastPrng } from '../../src/utils/fastPrng.js' await test('sum top level', async (t) => { const db = await testDb(t, { @@ -868,7 +869,7 @@ await test('numeric types', async (t) => { 'hmean, references, group by', ) }) -/* + await test('fixed length strings', async (t) => { const db = await testDb(t, { types: { @@ -897,7 +898,7 @@ await test('fixed length strings', async (t) => { db.drain() db.create('shelve', { code: `S${rnd(0, 10)}`, - products: p, + products: [p], }) } @@ -929,7 +930,7 @@ await test('fixed length strings', async (t) => { 'fixed length strings on references', ) }) - +/* await test('range', async (t) => { const db = await testDb(t, { types: { From a7df7d5389d2bb0d4950a2bfd4bfea0eb14b468c Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 23 Feb 2026 17:22:55 +0100 Subject: [PATCH 383/449] update --- test/aggregate/deep.ts | 137 ++++++----------------------------------- 1 file changed, 19 insertions(+), 118 deletions(-) diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index ba19234ef2..5b308a11c8 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -525,13 +525,7 @@ await test('cardinality', async (t) => { }) await test('cardinality on references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { booth: { company: 'string', @@ -563,19 +557,17 @@ await test('cardinality on references', async (t) => { booths: [bg, stp], }) -<<<<<<< HEAD -// await db.query2('fair').include('booths.badgesScanned').get().inspect() -// await db -// .query2('fair') -// .cardinality('booths.badgesScanned') -// .groupBy('day') -// .get() -// .inspect() -// }) -======= + // await db.query2('fair').include('booths.badgesScanned').get().inspect() + // await db + // .query2('fair') + // .cardinality('booths.badgesScanned') + // .groupBy('day') + // .get() + // .inspect() + // }) deepEqual( await db - .query('fair') + .query2('fair') .include((s) => s('booths').cardinality('badgesScanned')) .get(), [ @@ -595,16 +587,15 @@ await test('cardinality on references', async (t) => { * Nested syntax: */ - // await db.query('fair').include('booths.badgesScanned').get().inspect() + // await db.query2('fair').include('booths.badgesScanned').get().inspect() // await db - // .query('fair') + // .query2('fair') // .cardinality('booths.badgesScanned') // .groupBy('day') // .get() // .inspect() }) ->>>>>>> ede73b52799512041ed5f8ae1d758f1b6cf88037 await test('group by reference ids', async (t) => { const db = await testDb(t, { @@ -731,8 +722,10 @@ await test.skip('nested references', async (t) => { deepEqual( await db.query2('user').sum('friends.strong').get(), { - strong: { - sum: 7, + friends: { + strong: { + sum: 7, + }, }, }, 'nested references access with dot sintax', @@ -839,100 +832,9 @@ await test.skip('edges aggregation', async (t) => { // .get() // .inspect(10) -<<<<<<< HEAD - deepEqual( - await db - .query2('movie') - .include((q) => q('actors').max('$rating')) - .get(), - [ - { - id: 1, - actors: { - $rating: { - max: 55, - }, - }, - }, - { - id: 2, - actors: { - $rating: { - max: 77, - }, - }, - }, - ], - 'single edge aggregation, branched query', - ) - - deepEqual( - await db - .query2('movie') - .include((q) => q('actors').max('$rating').sum('$hating')) - .get(), - [ - { - id: 1, - actors: { - $rating: { - max: 55, - }, - $hating: { - sum: 5, - }, - }, - }, - { - id: 2, - actors: { - $rating: { - max: 77, - }, - $hating: { - sum: 10, - }, - }, - }, - ], - 'multiple edges with multiple agg functions, branched query', - ) - - deepEqual( - await db - .query2('movie') - .include((q) => q('actors').max('$rating', '$hating')) - .get(), - [ - { - id: 1, - actors: { - $rating: { - max: 55, - }, - $hating: { - max: 5, - }, - }, - }, - { - id: 2, - actors: { - $rating: { - max: 77, - }, - $hating: { - max: 7, - }, - }, - }, - ], - 'multiple edges on same agg function, branched query', - ) -======= // deepEqual( // await db - // .query('movie') + // .query2('movie') // .include((q) => q('actors').max('$rating')) // .get() // .toObject(), @@ -959,7 +861,7 @@ await test.skip('edges aggregation', async (t) => { // deepEqual( // await db - // .query('movie') + // .query2('movie') // .include((q) => q('actors').max('$rating').sum('$hating')) // .get() // .toObject(), @@ -992,7 +894,7 @@ await test.skip('edges aggregation', async (t) => { // deepEqual( // await db - // .query('movie') + // .query2('movie') // .include((q) => q('actors').max('$rating', '$hating')) // .get() // .toObject(), @@ -1022,7 +924,6 @@ await test.skip('edges aggregation', async (t) => { // ], // 'multiple edges on same agg function, branched query', // ) ->>>>>>> ede73b52799512041ed5f8ae1d758f1b6cf88037 /*-----------------------------------*/ /* STRAIGHT ON TYPE */ From 1803dba0c1ac45fdbcdcd115ecc2c7b0f0329382 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 23 Feb 2026 17:42:22 +0100 Subject: [PATCH 384/449] port aggregate tests --- test/aggregate/dev.ts | 37 ++++++-------- test/aggregate/experimental.ts | 21 +++----- test/aggregate/groupBY.ts | 89 ++++++++++----------------------- test/aggregate/multiple.ts | 30 +++++------- test/aggregate/overall.perf.ts | 55 +++++++-------------- test/aggregate/temporal.ts | 90 ++++++++++------------------------ test/aggregate/validation.ts | 34 ++++--------- test_type.ts | 16 ++++++ 8 files changed, 131 insertions(+), 241 deletions(-) create mode 100644 test_type.ts diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index 170d5cec72..c2db462c23 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -1,6 +1,7 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' // await test('kev', async (t) => { // const db = new BasedDb({ @@ -25,26 +26,26 @@ import { deepEqual } from '../shared/assert.js' // db.create('trip', { driver: 'lala', distance: 20, rate: 10 }) // db.create('trip', { driver: 'lele', distance: 40, rate: 10 }) -// // console.log((await db.query('trip').include('distance').get()).debug()) +// // console.log((await db.query2('trip').include('distance').get()).debug()) // // console.log( // // ( -// // await db.query('trip').harmonicMean('distance').avg('distance').get() +// // await db.query2('trip').harmonicMean('distance').avg('distance').get() // // ).debug(), // // ) -// // console.log((await db.query('trip').sum('distance', 'rate').get()).debug()) +// // console.log((await db.query2('trip').sum('distance', 'rate').get()).debug()) // console.log( -// (await db.query('trip').filter('distance', '>', 10).get()).debug(), +// (await db.query2('trip').filter('distance', '>', 10).get()).debug(), // ) // console.log( // ( -// await db.query('trip').sum('distance').filter('distance', '>', 10).get() +// await db.query2('trip').sum('distance').filter('distance', '>', 10).get() // ).debug(), // ) // console.log( // ( // await db -// .query('trip') +// .query2('trip') // .sum('distance') // .filter('rate', '>', 8) // .groupBy('driver') @@ -103,16 +104,16 @@ import { deepEqual } from '../shared/assert.js' // driver: d1, // }) -// // await db.query('trip').include('*', '**').get().inspect(10) +// // await db.query2('trip').include('*', '**').get().inspect(10) // // await db -// // .query('driver') +// // .query2('driver') // // .include((t) => t('trips').include('distance')) // // .get() // // .inspect(10) // const lala = await db -// .query('driver') +// .query2('driver') // .include((t) => // t('trips') // .sum('distance') @@ -128,15 +129,7 @@ import { deepEqual } from '../shared/assert.js' // }) await test('yyy', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { team: { props: { @@ -225,13 +218,13 @@ await test('yyy', async (t) => { }) const result = await db - .query('team') - .include('teamName', 'city', (select) => { + .query2('team') + .include('teamName', 'city', (select) => select('players') .sum('goalsScored', 'gamesPlayed') .groupBy('position') - .range(0, 10) - }) + .range(0, 10), + ) .get() // result.debug() diff --git a/test/aggregate/experimental.ts b/test/aggregate/experimental.ts index 47d0b2a731..01a3d0827f 100644 --- a/test/aggregate/experimental.ts +++ b/test/aggregate/experimental.ts @@ -1,15 +1,10 @@ import { BasedDb, groupBy } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('dev', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { lunch: { week: 'string', @@ -65,7 +60,7 @@ await test('dev', async (t) => { } await db.create('lunch', week27) - // const eaters = await db.query('lunch').get() + // const eaters = await db.query2('lunch').get() // eaters.inspect() // // knwon from raw data: @@ -89,10 +84,10 @@ await test('dev', async (t) => { lala: 10, }) - // console.log(await db.query('lunch').include('Mon').get()) + // console.log(await db.query2('lunch').include('Mon').get()) deepEqual( - await db.query('lunch').cardinality('Mon').get(), + await db.query2('lunch').cardinality('Mon').get(), { Mon: { cardinality: 7 }, }, @@ -100,7 +95,7 @@ await test('dev', async (t) => { ) deepEqual( - await db.query('lunch').cardinality('Mon').groupBy('week').get(), + await db.query2('lunch').cardinality('Mon').groupBy('week').get(), { 27: { Mon: { cardinality: 5 }, @@ -111,11 +106,11 @@ await test('dev', async (t) => { }, 'cardinality main groupBy', ) - // await db.query('lunch').sum('lala').groupBy('week').get().inspect() + // await db.query2('lunch').sum('lala').groupBy('week').get().inspect() // await db.create('lunch', { // week: 0, // lala: 10, // lele: 11, // }) - // await db.query('lunch').sum('lala', 'lele').get().inspect() + // await db.query2('lunch').sum('lala', 'lele').get().inspect() }) diff --git a/test/aggregate/groupBY.ts b/test/aggregate/groupBY.ts index 6361f17869..fae3b6f18c 100644 --- a/test/aggregate/groupBY.ts +++ b/test/aggregate/groupBY.ts @@ -1,19 +1,10 @@ -import { equal } from 'node:assert' import { BasedDb } from '../../src/index.js' -import { allCountryCodes } from '../shared/examples.js' import test from '../shared/test.js' import { throws, deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('sum group by', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -59,7 +50,7 @@ await test('sum group by', async (t) => { const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( - await db.query('vote').sum('NL', 'AU').groupBy('country').get().toObject(), + await db.query2('vote').sum('NL', 'AU').groupBy('country').get(), { bb: { NL: { sum: 10 }, AU: { sum: 0 } }, aa: { NL: { sum: 20 }, AU: { sum: 15 } }, @@ -68,34 +59,26 @@ await test('sum group by', async (t) => { ) deepEqual( - await db.query('vote').groupBy('country').get().toObject(), + await db.query2('vote').groupBy('country').get(), { bb: {}, aa: {} }, 'groupBy with no aggregation function', ) // deepEqual( // await db - // .query('vote') + // .query2('vote') // .filter('country', '=', 'bb') // filter string not implemented yet // .groupBy('country') // .sum('NL', 'AU') // .get() - // .toObject(), + // , // { bb: { NL: { sum: 10 }, AU: { sum: 0 } } }, // 'filter, groupBy on single distinct value', // ) }) await test('count group by', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -141,7 +124,7 @@ await test('count group by', async (t) => { const s = db.create('sequence', { votes: [nl1, nl2, au1] }) deepEqual( - await db.query('vote').count().groupBy('country').get().toObject(), + await db.query2('vote').count().groupBy('country').get(), { bb: { count: 1, @@ -155,27 +138,19 @@ await test('count group by', async (t) => { // deepEqual( // await db - // .query('vote') + // .query2('vote') // .filter('country', '=', 'bb') // filter string not implemented yet // .groupBy('country') // .count() // .get() - // .toObject(), + // , // { bb: { count: 1 } }, // 'count, filter, groupBy on single distinct value', // ) }) await test('variable key sum', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -245,10 +220,9 @@ await test('variable key sum', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((q) => q('contributors').sum('flap'), 'name') - .get() - .toObject(), + .get(), [ { id: 1, @@ -265,7 +239,7 @@ await test('variable key sum', async (t) => { ) deepEqual( - await db.query('user').groupBy('name').sum('flap').get().toObject(), + await db.query2('user').groupBy('name').sum('flap').get(), { Flippie: { flap: { sum: 20 } }, 'Carlo Cipolla': { flap: { sum: 80 } }, @@ -277,7 +251,7 @@ await test('variable key sum', async (t) => { ) deepEqual( - await db.query('user').groupBy('country').sum('flap').get().toObject(), + await db.query2('user').groupBy('country').sum('flap').get(), { $undefined: { flap: { sum: 40 } }, NL: { flap: { sum: 30 } }, @@ -289,12 +263,9 @@ await test('variable key sum', async (t) => { deepEqual( await db - .query('article') - .include((select) => { - select('contributors').groupBy('name').sum('flap') - }) - .get() - .toObject(), + .query2('article') + .include((select) => select('contributors').groupBy('name').sum('flap')) + .get(), [ { id: 1, @@ -317,13 +288,7 @@ await test('variable key sum', async (t) => { }) await test('group by unique numbers', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { trip: { pickup: 'timestamp', @@ -354,7 +319,7 @@ await test('group by unique numbers', async (t) => { }) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIduint8').get(), + await db.query2('trip').sum('distance').groupBy('vendorIduint8').get(), { 13: { distance: { sum: 513.44 }, @@ -363,7 +328,7 @@ await test('group by unique numbers', async (t) => { 'group by number', ) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIdint8').get(), + await db.query2('trip').sum('distance').groupBy('vendorIdint8').get(), { 13: { distance: { sum: 513.44 }, @@ -372,7 +337,7 @@ await test('group by unique numbers', async (t) => { 'group by number', ) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIduint16').get(), + await db.query2('trip').sum('distance').groupBy('vendorIduint16').get(), { 813: { distance: { sum: 513.44 }, @@ -381,7 +346,7 @@ await test('group by unique numbers', async (t) => { 'group by number', ) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIdint16').get(), + await db.query2('trip').sum('distance').groupBy('vendorIdint16').get(), { 813: { distance: { sum: 513.44 }, @@ -390,7 +355,7 @@ await test('group by unique numbers', async (t) => { 'group by number', ) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIduint32').get(), + await db.query2('trip').sum('distance').groupBy('vendorIduint32').get(), { 813: { distance: { sum: 513.44 }, @@ -399,7 +364,7 @@ await test('group by unique numbers', async (t) => { 'group by number', ) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIdint32').get(), + await db.query2('trip').sum('distance').groupBy('vendorIdint32').get(), { 813: { distance: { sum: 513.44 }, @@ -408,7 +373,7 @@ await test('group by unique numbers', async (t) => { 'group by number', ) deepEqual( - await db.query('trip').sum('distance').groupBy('vendorIdnumber').get(), + await db.query2('trip').sum('distance').groupBy('vendorIdnumber').get(), { 813.813: { distance: { sum: 513.44 }, @@ -444,5 +409,5 @@ await test.skip('groupBy ranges in numeric properties', async (t) => { }) } - // await db.query('trip').sum('distance').groupBy('tripId').get().inspect() + // await db.query2('trip').sum('distance').groupBy('tripId').get().inspect() }) diff --git a/test/aggregate/multiple.ts b/test/aggregate/multiple.ts index 117e95c8b4..adf99ff836 100644 --- a/test/aggregate/multiple.ts +++ b/test/aggregate/multiple.ts @@ -1,17 +1,9 @@ -import { BasedDb, groupBy } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('multiple functions', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -82,14 +74,14 @@ await test('multiple functions', async (t) => { const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) deepEqual( - await db.query('vote').sum('NL').sum('NO').max('NL').min('NL').get(), + await db.query2('vote').sum('NL').sum('NO').max('NL').min('NL').get(), { NL: { sum: 176, max: 50, min: 10 }, NO: { sum: -176 } }, 'multiple func main no groupBy', ) deepEqual( await db - .query('vote') + .query2('vote') .sum('NL') .sum('NO') .max('NL') @@ -110,7 +102,7 @@ await test('multiple functions', async (t) => { }) const multi = await db - .query('vote') + .query2('vote') .sum('NL') .max('PT') .cardinality('judges') @@ -143,7 +135,7 @@ await test('multiple functions', async (t) => { ) const multi2 = await db - .query('vote') + .query2('vote') .sum('NL') .max('PT') .cardinality('judges') @@ -211,7 +203,7 @@ await test('multiple functions', async (t) => { ) deepEqual( - await db.query('vote').sum('NL').count().sum('PT').stddev('NO').get(), + await db.query2('vote').sum('NL').count().sum('PT').stddev('NO').get(), { NL: { sum: 176, @@ -230,7 +222,7 @@ await test('multiple functions', async (t) => { ) deepEqual( await db - .query('vote') + .query2('vote') .sum('NL') .count() .sum('PT') @@ -279,7 +271,7 @@ await test('multiple functions', async (t) => { ) // const multiref = await db - // .query('sequence') + // .query2('sequence') // .include((q) => q('votes').sum('NL').count().cardinality('judges')) // .get() @@ -306,7 +298,7 @@ await test('multiple functions', async (t) => { // deepEqual( // await db - // .query('sequence') + // .query2('sequence') // .include((q) => q('votes').sum('NL').count().cardinality('judges')) // .get(), // [ @@ -324,7 +316,7 @@ await test('multiple functions', async (t) => { // deepEqual( // await db - // .query('sequence') + // .query2('sequence') // .include((q) => q('votes').count().sum('NL').cardinality('judges')) // .get(), // [ diff --git a/test/aggregate/overall.perf.ts b/test/aggregate/overall.perf.ts index 1922e3b3fd..ebb16515bf 100644 --- a/test/aggregate/overall.perf.ts +++ b/test/aggregate/overall.perf.ts @@ -5,16 +5,11 @@ import { deepEqual } from '../shared/assert.js' import { fastPrng } from '../../src/utils/index.js' import { equal } from 'node:assert' import { SchemaType } from '../../src/schema/index.js' +import { testDb } from '../shared/index.js' -await test.skip('overall performance', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - +test.skip('overall performance', async (t) => { const types = ['IPA', 'Lager', 'Ale', 'Stout', 'Wit', 'Dunkel', 'Tripel'] - await db.setSchema({ + const db = await testDb(t, { types: { beer: { props: { @@ -43,32 +38,24 @@ await test.skip('overall performance', async (t) => { await db.drain() await perf(async () => { - await db.query('beer').sum('price').get() + await db.query2('beer').sum('price').get() }, 'main agg') await perf(async () => { - await db.query('beer').groupBy('year').get() + await db.query2('beer').groupBy('year').get() }, 'group by year') await perf(async () => { - await db.query('beer').groupBy('type').get() + await db.query2('beer').groupBy('type').get() }, 'group by enum main') await perf(async () => { - await db.query('beer').max('price').groupBy('type').get() + await db.query2('beer').max('price').groupBy('type').get() }, 'agg + enum main group by') }) await test.skip('count top level bignumber', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { bla: 'uint32', @@ -82,17 +69,11 @@ await test.skip('count top level bignumber', async (t) => { await db.drain() - const q = await db.query('sequence').count().get() - equal(q.toObject().count, 1e6) + const q = await db.query2('sequence').count().get() + equal(q.count, 1e6) }) await test('many countries', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - const countrySchema: SchemaType = { props: { AF: 'uint8', @@ -291,13 +272,13 @@ await test('many countries', async (t) => { }, } - await db.setSchema({ + const db = await testDb(t, { types: { audience: countrySchema, }, }) - const countries = Object.keys(countrySchema.props) + const countries = Object.keys(countrySchema.props) as [any, ...any[]] // for (let i = 0; i < 1e8; i++) { // db.create( @@ -310,7 +291,7 @@ await test('many countries', async (t) => { // } // await perf(async () => { // await db - // .query('audience') + // .query2('audience') // .avg(...countries) // .get() // }, 'averaging 193 props x 100_000_000 nodes') @@ -331,7 +312,7 @@ await test('many countries', async (t) => { } await perf(async () => { await db - .query('audience') + .query2('audience') .avg(...countries) .get() }, 'averaging 193 props x 10_000_000 nodes') @@ -352,7 +333,7 @@ await test('many countries', async (t) => { } await perf(async () => { await db - .query('audience') + .query2('audience') .avg(...countries) .get() }, 'averaging 193 props x 1_000_000 nodes') @@ -364,7 +345,7 @@ await test('many countries', async (t) => { await perf(async () => { await db - .query('audience') + .query2('audience') .avg(...countries) .get() }, 'averaging 193 props x 100_000 nodes') @@ -376,7 +357,7 @@ await test('many countries', async (t) => { await perf(async () => { await db - .query('audience') + .query2('audience') .avg(...countries) .get() }, 'averaging 193 props x 10_000 nodes') @@ -388,7 +369,7 @@ await test('many countries', async (t) => { await perf(async () => { await db - .query('audience') + .query2('audience') .avg(...countries) .get() }, 'averaging 193 props x 1_000 nodes') diff --git a/test/aggregate/temporal.ts b/test/aggregate/temporal.ts index cbbaadd167..7dd4d647eb 100644 --- a/test/aggregate/temporal.ts +++ b/test/aggregate/temporal.ts @@ -1,18 +1,10 @@ -import { equal } from 'node:assert' import { BasedDb } from '../../src/index.js' -import { allCountryCodes } from '../shared/examples.js' import test from '../shared/test.js' import { throws, deepEqual } from '../shared/assert.js' -import { fastPrng } from '../../src/utils/index.js' +import { testDb } from '../shared/index.js' await test('group by datetime intervals', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { trip: { pickup: 'timestamp', @@ -37,7 +29,7 @@ await test('group by datetime intervals', async (t) => { }) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'day').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'day').get(), { 11: { distance: { sum: 1026.88 }, @@ -47,7 +39,7 @@ await test('group by datetime intervals', async (t) => { ) deepEqual( await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', { step: 'day' }) .get(), @@ -59,7 +51,7 @@ await test('group by datetime intervals', async (t) => { 'group timestamp by day, without shorthand', ) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'hour').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'hour').get(), { 11: { distance: { sum: 1026.88 }, @@ -68,7 +60,7 @@ await test('group by datetime intervals', async (t) => { 'group timestamp by hour', ) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'dow').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'dow').get(), { 3: { distance: { sum: 1026.88 }, @@ -77,7 +69,7 @@ await test('group by datetime intervals', async (t) => { 'group timestamp by day of week', ) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'isoDOW').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'isoDOW').get(), { 3: { distance: { sum: 1026.88 }, @@ -86,7 +78,7 @@ await test('group by datetime intervals', async (t) => { 'group timestamp by hour', ) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'doy').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'doy').get(), { 345: { distance: { sum: 1026.88 }, @@ -95,7 +87,7 @@ await test('group by datetime intervals', async (t) => { 'group timestamp by hour', ) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'month').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'month').get(), { 11: { distance: { sum: 1026.88 }, @@ -104,7 +96,7 @@ await test('group by datetime intervals', async (t) => { 'group timestamp by month[0-11]', ) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup', 'year').get(), + await db.query2('trip').sum('distance').groupBy('pickup', 'year').get(), { 2024: { distance: { sum: 1026.88 }, @@ -115,13 +107,7 @@ await test('group by datetime intervals', async (t) => { }) await test('group by datetime ranges', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { trip: { pickup: 'timestamp', @@ -154,11 +140,10 @@ await test('group by datetime ranges', async (t) => { let interval = 40 * 60 // 40 minutes let r = await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', interval) .get() - .toObject() let epoch = Number(Object.keys(r)[0]) let startDate = dtFormat.format(epoch) @@ -190,11 +175,10 @@ await test('group by datetime ranges', async (t) => { let interval2 = 60 * 60 * 24 * 12 + 2 * 60 * 60 // 12 days and 2h let r2 = await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', interval2) .get() - .toObject() let epoch2 = Number(Object.keys(r2)[0]) let startDate2 = dtFormat.format(epoch2) @@ -213,11 +197,11 @@ await test('group by datetime ranges', async (t) => { await throws( async () => { await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', 2 ** 32 + 1) .get() - .inspect() + // .inspect() }, false, `throw invalid step range error on validation`, @@ -225,13 +209,7 @@ await test('group by datetime ranges', async (t) => { }) await test('cardinality with dates', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { lunch: { day: 'timestamp', @@ -279,17 +257,16 @@ await test('cardinality with dates', async (t) => { ], }) - const total = await db.query('lunch').cardinality('eaters').get().toObject() + const total = await db.query2('lunch').cardinality('eaters').get() // console.log('Total Eaters: ', total.eaters) deepEqual(total.eaters.cardinality, 11, 'Total Eaters') const groupByDay = await db - .query('lunch') + .query2('lunch') .cardinality('eaters') .groupBy('day') .get() - .toObject() const meals = Object.entries(groupByDay) //@ts-ignore .map((m) => m[1].eaters.cardinality) @@ -314,11 +291,10 @@ await test('cardinality with dates', async (t) => { } const groupByMonth = await db - .query('lunch') + .query2('lunch') .cardinality('eaters') .groupBy('day', 'month') .get() - .toObject() const eatersByMonth = Object.entries(groupByMonth).map((e) => { //@ts-ignore @@ -334,13 +310,7 @@ await test('cardinality with dates', async (t) => { }) await test('formating timestamp', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { trip: { pickup: 'timestamp', @@ -372,7 +342,7 @@ await test('formating timestamp', async (t) => { }) deepEqual( - await db.query('trip').sum('distance').groupBy('pickup').get(), + await db.query2('trip').sum('distance').groupBy('pickup').get(), { 1733916600000: { distance: { sum: 513.44 }, @@ -386,7 +356,7 @@ await test('formating timestamp', async (t) => { deepEqual( await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', { step: 40 * 60, display: dtFormat }) .get(), @@ -400,7 +370,7 @@ await test('formating timestamp', async (t) => { deepEqual( await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', { display: dtFormat }) .get(), @@ -413,13 +383,7 @@ await test('formating timestamp', async (t) => { }) await test('timezone offsets', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { trip: { pickup: 'timestamp', @@ -452,7 +416,7 @@ await test('timezone offsets', async (t) => { deepEqual( await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', { step: 'day', timeZone: 'America/Sao_Paulo' }) .get(), @@ -469,7 +433,7 @@ await test('timezone offsets', async (t) => { ) deepEqual( await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('pickup', { step: 'hour', timeZone: 'America/Sao_Paulo' }) .get(), @@ -485,7 +449,7 @@ await test('timezone offsets', async (t) => { ) deepEqual( await db - .query('trip') + .query2('trip') .sum('distance') .groupBy('dropoff', { step: 'month', timeZone: 'America/Sao_Paulo' }) .get(), diff --git a/test/aggregate/validation.ts b/test/aggregate/validation.ts index cbb975f96e..fe8b873938 100644 --- a/test/aggregate/validation.ts +++ b/test/aggregate/validation.ts @@ -1,20 +1,10 @@ -import { equal } from 'node:assert' import { BasedDb } from '../../src/index.js' -import { allCountryCodes } from '../shared/examples.js' import test from '../shared/test.js' -import { throws, deepEqual } from '../shared/assert.js' -import { fastPrng } from '../../src/utils/index.js' +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('undefined numbers', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { vote: { props: { @@ -36,7 +26,7 @@ await test('undefined numbers', async (t) => { }) deepEqual( - await db.query('vote').max('AU', 'FI').groupBy('region').get().toObject(), + await db.query2('vote').max('AU', 'FI').groupBy('region').get(), { EU: { AU: { max: 23 }, @@ -46,7 +36,7 @@ await test('undefined numbers', async (t) => { 'number is initialized with zero', ) deepEqual( - await db.query('vote').avg('AU', 'FI').groupBy('region').get().toObject(), + await db.query2('vote').avg('AU', 'FI').groupBy('region').get(), { EU: { AU: { avg: 16.5 }, @@ -57,7 +47,7 @@ await test('undefined numbers', async (t) => { ) deepEqual( - await db.query('vote').hmean('AU', 'FI').groupBy('region').get().toObject(), + await db.query2('vote').hmean('AU', 'FI').groupBy('region').get(), { EU: { AU: { hmean: 13.93939393939394 }, @@ -69,13 +59,7 @@ await test('undefined numbers', async (t) => { }) await test('boundary cases for validation', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { movie: { name: 'string', @@ -134,7 +118,7 @@ await test('boundary cases for validation', async (t) => { }) deepEqual( - await db.query('movie').groupBy('year').count().get(), + await db.query2('movie').groupBy('year').count().get(), { 1994: { count: 1, @@ -147,7 +131,7 @@ await test('boundary cases for validation', async (t) => { ) deepEqual( - await db.query('movie').groupBy('genre').min('year').get(), + await db.query2('movie').groupBy('genre').min('year').get(), { undefined: { year: { min: 1994 }, diff --git a/test_type.ts b/test_type.ts new file mode 100644 index 0000000000..862cf76f8a --- /dev/null +++ b/test_type.ts @@ -0,0 +1,16 @@ +import { testDb } from './test/shared/index.js' + +async function run() { + const db = null as any as Awaited> + const a = await db.query2('vote').include('country', 'AU').get() + + const b = await db + .query2('vote') + .include((q) => q('sequence')) + .get() + + const c = await db + .query2('vote') + .include('country', 'AU', (q) => q('sequence')) + .get() +} From 89b3f8068553355fcb28be08db2d3e4b51b46cc6 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 23 Feb 2026 18:08:43 +0100 Subject: [PATCH 385/449] port alias test --- src/db-client/query2/index.ts | 10 +- test/alias/alias.ts | 469 ++++++++++------------------------ test/alias/aliasOps.perf.ts | 13 +- test/alias/aliasOps.ts | 16 +- test/alias/create.perf.ts | 10 +- test/alias/doubleAlias.ts | 15 +- test/alias/filter.ts | 17 +- test/alias/insert.ts | 63 +++-- test/alias/upsert.ts | 50 ++-- 9 files changed, 230 insertions(+), 433 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index ca5af610d3..f93ae06e29 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -17,8 +17,9 @@ import type { ResolvedProps, SchemaOut } from '../../schema/index.js' import { astToQueryCtx } from '../../db-query/ast/toCtx.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import type { DbClient } from '../../sdk.js' -import { proxyResult } from './result.js' +import { $buffer, proxyResult } from './result.js' import type { StepInput, aggFnOptions } from '../query/aggregates/types.js' +import { readUint32 } from '../../utils/uint8.js' class Query< S extends { types: any } = { types: any }, @@ -43,6 +44,7 @@ class Query< include< F extends [ ( + | 'id' | (keyof (ResolvedProps & EdgeProps) & string) | Path | '*' @@ -50,6 +52,7 @@ class Query< | ((q: SelectFn) => AnyQuery) ), ...( + | 'id' | (keyof (ResolvedProps & EdgeProps) & string) | Path | '*' @@ -891,3 +894,8 @@ function traverse(target: any, prop: string) { } return target } + +export const checksum = (res: any): number => { + const buf = res?.[$buffer] + return buf ? readUint32(buf, buf.byteLength - 4) : 0 +} diff --git a/test/alias/alias.ts b/test/alias/alias.ts index a140037fc9..f7840d47e2 100644 --- a/test/alias/alias.ts +++ b/test/alias/alias.ts @@ -2,16 +2,11 @@ import { notEqual } from 'assert' import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' +import { checksum } from '../../src/db-client/query2/index.js' await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -31,7 +26,7 @@ await test('simple', async (t) => { }) deepEqual( - await db.query('user', await user1).get(), + await db.query2('user', await user1).get(), { id: 1, externalId: 'cool', @@ -40,13 +35,13 @@ await test('simple', async (t) => { 'One alias', ) - deepEqual(await db.query('user', user2).get(), { + deepEqual(await db.query2('user', user2).get(), { id: 2, externalId: 'cool2', potato: '', }) - deepEqual(await db.query('user').filter('externalId', '=', 'cool').get(), [ + deepEqual(await db.query2('user').filter('externalId', '=', 'cool').get(), [ { id: 1, externalId: 'cool', @@ -55,7 +50,7 @@ await test('simple', async (t) => { ]) deepEqual( - await db.query('user').filter('externalId', 'includes', 'cool').get(), + await db.query2('user').filter('externalId', 'includes', 'cool').get(), [ { id: 1, @@ -69,39 +64,49 @@ await test('simple', async (t) => { }, ], ) - const res1 = await db.upsert('user', { - externalId: 'potato', - potato: 'success', - }) + const res1 = await db.upsert( + 'user', + { + externalId: 'potato', + }, + { + potato: 'success', + }, + ) - deepEqual(await db.query('user', res1).get(), { + deepEqual(await db.query2('user', res1).get(), { id: 3, externalId: 'potato', potato: 'success', }) - const res2 = await db.upsert('user', { - externalId: 'potato', - potato: 'wrong', - }) - deepEqual(await db.query('user', res2).get(), { + const res2 = await db.upsert( + 'user', + { + externalId: 'potato', + }, + { + potato: 'wrong', + }, + ) + deepEqual(await db.query2('user', res2).get(), { id: 3, externalId: 'potato', potato: 'wrong', }) deepEqual( - await db.query('user', { externalId: 'i-dont-exists-haha!' }).get(), + await db.query2('user', { externalId: 'i-dont-exists-haha!' }).get(), null, 'Get non existing alias', ) - deepEqual(await db.query('user', 123).get(), null, 'Get non existing id') + deepEqual(await db.query2('user', 123).get(), null, 'Get non existing id') await db.create('user', { potato: 'power', externalId: 'cool', }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, externalId: '', potato: '' }, { id: 2, externalId: 'cool2', potato: '' }, { id: 3, externalId: 'potato', potato: 'wrong' }, @@ -110,14 +115,7 @@ await test('simple', async (t) => { }) await test('alias - references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -138,17 +136,22 @@ await test('alias - references', async (t) => { }, }) - await db.upsert('user', { - name: '2', - email: '2@saulx.com', - bestFriend: db.upsert('user', { email: 'jim@saulx.com' }), - friends: { - add: [db.upsert('user', { email: 'jim@saulx.com' })], + await db.upsert( + 'user', + { + email: '2@saulx.com', }, - }) + { + name: '2', + bestFriend: db.upsert('user', { email: 'jim@saulx.com' }, {}), + friends: { + add: [db.upsert('user', { email: 'jim@saulx.com' }, {})], + }, + }, + ) deepEqual( - await db.query('user').include('email', 'friends').get(), + await db.query2('user').include('email', 'friends').get(), [ { id: 1, @@ -164,17 +167,26 @@ await test('alias - references', async (t) => { 'simple', ) - await db.upsert('user', { - name: '2', - email: '2@saulx.com', - bestFriend: db.upsert('user', { email: 'jim@saulx.com', name: 'jim' }), - friends: { - add: [db.upsert('user', { email: 'jim@saulx.com', name: 'jim' })], + await db.upsert( + 'user', + { + email: '2@saulx.com', }, - }) + { + name: '2', + bestFriend: db.upsert( + 'user', + { email: 'jim@saulx.com' }, + { name: 'jim' }, + ), + friends: { + add: [db.upsert('user', { email: 'jim@saulx.com' }, { name: 'jim' })], + }, + }, + ) deepEqual( - await db.query('user').include('friends', 'email').get(), + await db.query2('user').include('friends', 'email').get(), [ { id: 1, @@ -192,7 +204,7 @@ await test('alias - references', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('email', 'includes', '2', { lowerCase: true }) .get(), [{ id: 2, name: '2', email: '2@saulx.com' }], @@ -201,14 +213,7 @@ await test('alias - references', async (t) => { }) await test('Get single node by alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -219,14 +224,19 @@ await test('Get single node by alias', async (t) => { }, }) - await db.upsert('user', { - name: '2', - email: '2@saulx.com', - }) + await db.upsert( + 'user', + { + email: '2@saulx.com', + }, + { + name: '2', + }, + ) deepEqual( await db - .query('user', { + .query2('user', { email: '2@saulx.com', }) .get(), @@ -239,14 +249,7 @@ await test('Get single node by alias', async (t) => { }) await test('Update existing alias field', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -260,25 +263,30 @@ await test('Update existing alias field', async (t) => { }) const email = 'nuno@saulx.com' - await db.upsert('user', { - name: 'nuno', - email, - currentToken: - // INFO: Works if this field is undefined or an empty string - 'aff1ffc48253ffe063005ecce308996da1ab01c864276faaa88bd94fab4a092d604bbd916470ff1def223bc9e8b662b7', - }) + await db.upsert( + 'user', + { + email, + currentToken: + // INFO: Works if this field is undefined or an empty string + 'aff1ffc48253ffe063005ecce308996da1ab01c864276faaa88bd94fab4a092d604bbd916470ff1def223bc9e8b662b7', + }, + { + name: 'nuno', + }, + ) - const existingUser = await db.query('user', { email }).get().toObject() + const existingUser = await db.query2('user', { email }).get() let newToken = 'e2d88cf5d303972f2eb0c381e093afb8728eaebc8114a322418403eeaf30eb767d3d7dfaef784e9c2059d6cfa78cea87' - await db.update('user', existingUser.id, { + await db.update('user', existingUser!.id, { currentToken: newToken, status: 'login', }) await db.drain() - deepEqual(await db.query('user', { email }).get(), { + deepEqual(await db.query2('user', { email }).get(), { id: 1, name: 'nuno', email: 'nuno@saulx.com', @@ -288,7 +296,7 @@ await test('Update existing alias field', async (t) => { newToken = '6093127416cbc7ff8126cda605a2239a2e061a5c65a77cc38b23034441832d2c40afdaa91f83285c52edccc5dd8d18d5' - await db.update('user', existingUser.id, { + await db.update('user', existingUser!.id, { currentToken: newToken, status: 'login', }) @@ -296,7 +304,7 @@ await test('Update existing alias field', async (t) => { deepEqual( await db - .query('user', { + .query2('user', { email, }) .get(), @@ -309,7 +317,7 @@ await test('Update existing alias field', async (t) => { }, ) - await db.update('user', existingUser.id, { + await db.update('user', existingUser!.id, { currentToken: null, status: 'clear', }) @@ -318,7 +326,7 @@ await test('Update existing alias field', async (t) => { deepEqual( await db - .query('user', { + .query2('user', { email, }) .get(), @@ -333,7 +341,7 @@ await test('Update existing alias field', async (t) => { newToken = '1e6d1b9baf291d0d3f581ca147eda5a62feba5f2e84039322d9b8e0999e5d9a8c9feae5c7707d63be670615675ad2381' - await db.update('user', existingUser.id, { + await db.update('user', existingUser!.id, { currentToken: newToken, status: 'login', }) @@ -341,7 +349,7 @@ await test('Update existing alias field', async (t) => { deepEqual( await db - .query('user', { + .query2('user', { email, }) .get(), @@ -356,14 +364,7 @@ await test('Update existing alias field', async (t) => { }) await test('same-name-alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { sequence: { props: { @@ -387,18 +388,18 @@ await test('same-name-alias', async (t) => { const rounds = [{ name: 'semi1' }, { name: 'semi2' }, { name: 'final' }] for (const sequence of sequences) { - db.upsert('sequence', sequence) + db.upsert('sequence', sequence, {}) } await db.drain() for (const round of rounds) { - await db.upsert('round', round) + await db.upsert('round', round, {}) } await db.drain() - deepEqual(await db.query('round').get(), [ + deepEqual(await db.query2('round').get(), [ { id: 1, name: 'semi1' }, { id: 2, name: 'semi2' }, { id: 3, name: 'final' }, @@ -406,15 +407,7 @@ await test('same-name-alias', async (t) => { }) await test('nested alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { thing: { obj: { @@ -427,32 +420,34 @@ await test('nested alias', async (t) => { }, }) - await db.upsert('thing', { - obj: { - a: 'jibber', + await db.upsert( + 'thing', + { + obj: { + a: 'jibber', + }, }, - }) + {}, + ) - await db.upsert('thing', { - obj: { - b: 'flurp', + await db.upsert( + 'thing', + { + obj: { + b: 'flurp', + }, }, - }) + {}, + ) - deepEqual(await db.query('thing').get(), [ + deepEqual(await db.query2('thing').get(), [ { id: 1, obj: { a: 'jibber', b: '' } }, { id: 2, obj: { b: 'flurp', a: '' } }, ]) }) await test('json and crc32', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { article: { @@ -466,26 +461,19 @@ await test('json and crc32', async (t) => { article: 'a', }) - const checksum = (await db.query('user', user1).get()).checksum + const checksum1 = checksum(await db.query2('user', user1).get()) await db.update('user', user1, { article: 'b', }) - const checksum2 = (await db.query('user', user1).get()).checksum + const checksum2 = checksum(await db.query2('user', user1).get()) - notEqual(checksum, checksum2, 'Checksum is not the same') + notEqual(checksum1, checksum2, 'Checksum is not the same') }) await test('Get single node by alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -496,205 +484,30 @@ await test('Get single node by alias', async (t) => { }, }) - await db.upsert('user', { - name: '2', - email: '2@saulx.com', - }) - - deepEqual( - await db - .query('user', { - email: '2@saulx.com', - }) - .get(), + await db.upsert( + 'user', { - id: 1, - name: '2', email: '2@saulx.com', }, + { name: '2' }, ) -}) - -await test('Update existing alias field', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ - types: { - user: { - props: { - name: 'string', - email: 'alias', - status: ['login', 'clear'], - currentToken: 'alias', - }, - }, - }, - }) - - const email = 'nuno@saulx.com' - await db.upsert('user', { - name: 'nuno', - email, - currentToken: - // INFO: Works if this field is undefined or an empty string - 'aff1ffc48253ffe063005ecce308996da1ab01c864276faaa88bd94fab4a092d604bbd916470ff1def223bc9e8b662b7', - }) - - const existingUser = await db.query('user', { email }).get().toObject() - - let newToken = - 'e2d88cf5d303972f2eb0c381e093afb8728eaebc8114a322418403eeaf30eb767d3d7dfaef784e9c2059d6cfa78cea87' - await db.update('user', existingUser.id, { - currentToken: newToken, - status: 'login', - }) - await db.drain() - - deepEqual( - await db - .query('user', { - email, - }) - .get(), - { - id: 1, - name: 'nuno', - email: 'nuno@saulx.com', - status: 'login', - currentToken: newToken, - }, - ) - - newToken = - '6093127416cbc7ff8126cda605a2239a2e061a5c65a77cc38b23034441832d2c40afdaa91f83285c52edccc5dd8d18d5' - await db.update('user', existingUser.id, { - currentToken: newToken, - status: 'login', - }) - await db.drain() - - deepEqual( - await db - .query('user', { - email, - }) - .get(), - { - id: 1, - name: 'nuno', - email: 'nuno@saulx.com', - status: 'login', - currentToken: newToken, - }, - ) - - await db.update('user', existingUser.id, { - currentToken: null, - status: 'clear', - }) - - await db.drain() - - deepEqual( - await db - .query('user', { - email, - }) - .get(), - { - id: 1, - name: 'nuno', - email: 'nuno@saulx.com', - status: 'clear', - currentToken: '', - }, - ) - - newToken = - '1e6d1b9baf291d0d3f581ca147eda5a62feba5f2e84039322d9b8e0999e5d9a8c9feae5c7707d63be670615675ad2381' - await db.update('user', existingUser.id, { - currentToken: newToken, - status: 'login', - }) - await db.drain() deepEqual( await db - .query('user', { - email, + .query2('user', { + email: '2@saulx.com', }) .get(), { id: 1, - name: 'nuno', - email: 'nuno@saulx.com', - status: 'login', - currentToken: newToken, + name: '2', + email: '2@saulx.com', }, ) }) -await test('same-name-alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ - types: { - sequence: { - props: { - name: 'alias', - }, - }, - round: { - props: { - name: 'alias', - }, - }, - }, - }) - - const sequences = [ - { name: 'semi1' }, - { name: 'semi1-othershit' }, - { name: 'semi2' }, - { name: 'semi2-othershit' }, - ] - const rounds = [{ name: 'semi1' }, { name: 'semi2' }, { name: 'final' }] - - for (const sequence of sequences) { - db.upsert('sequence', sequence) - } - for (const round of rounds) { - await db.upsert('round', round) - } - - await db.drain() - - deepEqual(await db.query('round').get(), [ - { id: 1, name: 'semi1' }, - { id: 2, name: 'semi2' }, - { id: 3, name: 'final' }, - ]) -}) - await test('alias and ref', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -722,12 +535,12 @@ await test('alias and ref', async (t) => { const user1 = await db.create('user', { name: 'Mario' }) //await db.update('user', user, { role: { alias: 'admin' }}) - await db.upsert('role', { alias: 'admin', users: { add: [user1] } }) + await db.upsert('role', { alias: 'admin' }, { users: { add: [user1] } }) const user2 = await db.create('user', { name: 'Luigi' }) - await db.upsert('role', { alias: 'admin', users: { add: [user2] } }) + await db.upsert('role', { alias: 'admin' }, { users: { add: [user2] } }) - deepEqual(await db.query('role', adminRole).include('name', 'users').get(), { + deepEqual(await db.query2('role', adminRole).include('name', 'users').get(), { id: 1, name: 'Admin Role', users: [ @@ -744,14 +557,7 @@ await test('alias and ref', async (t) => { }) await test('alias and edge ref', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -793,18 +599,17 @@ await test('alias and edge ref', async (t) => { const user2 = await db.create('user', { name: 'Luigi' }) const adminRole = await db - .query('role', { alias: 'admin' }) + .query2('role', { alias: 'admin' }) .include('id') .get() - .toObject() await db.update('project', prj, { - users: { add: [{ id: user1, $role: adminRole }] }, + users: { add: [{ id: user1, $role: adminRole!.id }] }, }) deepEqual( await db - .query('project', prj) + .query2('project', prj) .include('name', 'users', 'users.$role') .get(), { diff --git a/test/alias/aliasOps.perf.ts b/test/alias/aliasOps.perf.ts index 11e6c1ea83..4f9be4f9b4 100644 --- a/test/alias/aliasOps.perf.ts +++ b/test/alias/aliasOps.perf.ts @@ -1,17 +1,10 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { deepEqual, equal, perf } from '../shared/assert.js' +import { perf } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('await updates', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const status = ['a', 'b', 'c', 'd', 'e', 'f'] - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/alias/aliasOps.ts b/test/alias/aliasOps.ts index 458b4d4760..5d09cda1a3 100644 --- a/test/alias/aliasOps.ts +++ b/test/alias/aliasOps.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { deepEqual, equal } from '../shared/assert.js' +import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('upsert', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -25,7 +19,7 @@ await test('upsert', async (t) => { status: 'a', }) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, status: 'a', externalId: 'cool', @@ -36,7 +30,7 @@ await test('upsert', async (t) => { status: 'b', }) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, status: 'b', externalId: '', diff --git a/test/alias/create.perf.ts b/test/alias/create.perf.ts index e32e6f674c..b3b3ff4553 100644 --- a/test/alias/create.perf.ts +++ b/test/alias/create.perf.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { perf } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('create 1m items with an alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { alias: 'alias', diff --git a/test/alias/doubleAlias.ts b/test/alias/doubleAlias.ts index fa4fe72746..84f35974ff 100644 --- a/test/alias/doubleAlias.ts +++ b/test/alias/doubleAlias.ts @@ -1,16 +1,9 @@ -import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('aliasDouble', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { plot: { props: { @@ -37,7 +30,7 @@ await test('aliasDouble', async (t) => { deepEqual( await db - .query('plot', { + .query2('plot', { slug: 'test-plot-2', }) .get(), @@ -46,7 +39,7 @@ await test('aliasDouble', async (t) => { deepEqual( await db - .query('plot', { + .query2('plot', { uuid: 'flap2', }) .get(), diff --git a/test/alias/filter.ts b/test/alias/filter.ts index 68b7439294..89adecf0f6 100644 --- a/test/alias/filter.ts +++ b/test/alias/filter.ts @@ -1,16 +1,9 @@ -import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('aliasFilter', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { plot: { props: { @@ -31,10 +24,12 @@ await test('aliasFilter', async (t) => { age: 20, }) - const a = await db.query('plot', { slug: 'kavel-omval-naast-de-poort' }).get() + const a = await db + .query2('plot', { slug: 'kavel-omval-naast-de-poort' }) + .get() const b = await db - .query('plot', { slug: 'kavel-omval-naast-de-poort' }) + .query2('plot', { slug: 'kavel-omval-naast-de-poort' }) .filter('age', '>', 10) .get() diff --git a/test/alias/insert.ts b/test/alias/insert.ts index d7205a1494..bd86eb566a 100644 --- a/test/alias/insert.ts +++ b/test/alias/insert.ts @@ -1,17 +1,9 @@ -import { BasedDb } from '../../src/index.js' import { equal } from '../shared/assert.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('alias insert', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -23,13 +15,18 @@ await test('alias insert', async (t) => { }, }) - await db.insert('user', { - uuid: 'xx', - one: 1, - two: 2, - }) + await db.insert( + 'user', + { + uuid: 'xx', + }, + { + one: 1, + two: 2, + }, + ) - equal(await db.query('user').get(), [ + equal(await db.query2('user').get(), [ { id: 1, uuid: 'xx', @@ -38,13 +35,18 @@ await test('alias insert', async (t) => { }, ]) - await db.insert('user', { - uuid: 'xx', - one: 5, - two: 6, - }) + await db.insert( + 'user', + { + uuid: 'xx', + }, + { + one: 5, + two: 6, + }, + ) - equal(await db.query('user').get(), [ + equal(await db.query2('user').get(), [ { id: 1, uuid: 'xx', @@ -53,13 +55,18 @@ await test('alias insert', async (t) => { }, ]) - await db.insert('user', { - uuid: 'yy', - one: 5, - two: 6, - }) + await db.insert( + 'user', + { + uuid: 'yy', + }, + { + one: 5, + two: 6, + }, + ) - equal(await db.query('user').get(), [ + equal(await db.query2('user').get(), [ { id: 1, uuid: 'xx', diff --git a/test/alias/upsert.ts b/test/alias/upsert.ts index 65446b6774..f2c512b6d1 100644 --- a/test/alias/upsert.ts +++ b/test/alias/upsert.ts @@ -1,16 +1,11 @@ +import { DbServer } from '../../dist/index.js' import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' import { equal } from '../shared/assert.js' import test from '../shared/test.js' await test('alias upsert', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const server = new DbServer({ path: t.tmp }) + const schema = { types: { user: { props: { @@ -20,37 +15,50 @@ await test('alias upsert', async (t) => { }, }, }, + } as const + const client1 = new DbClient({ + hooks: getDefaultHooks(server), }) - - const client1 = db.client - const client2 = new DbClient({ - hooks: getDefaultHooks(db.server), + const client2 = new DbClient({ + hooks: getDefaultHooks(server), }) + await client1.setSchema(schema) + const ids = await Promise.all([ client1.create('user', { uuid: 'a', }), - client1.upsert('user', { - uuid: 'x', - one: 1, - }), + client1.upsert( + 'user', + { + uuid: 'x', + }, + { + one: 1, + }, + ), client1.create('user', { uuid: 'b', }), client2.create('user', { uuid: 'c', }), - client2.upsert('user', { - uuid: 'x', - two: 2, - }), + client2.upsert( + 'user', + { + uuid: 'x', + }, + { + two: 2, + }, + ), client2.create('user', { uuid: 'd', }), ]) - const results = await db.query('user').get() + const results = await client1.query2('user').get() equal( results, From aa1ad90fbd517ac316a1f2e791ddac6e401da381 Mon Sep 17 00:00:00 2001 From: youzi Date: Mon, 23 Feb 2026 18:41:43 +0100 Subject: [PATCH 386/449] update --- src/db-client/index.ts | 9 +++++++-- test/alias/alias.ts | 6 ++++-- test_type.ts | 16 ---------------- 3 files changed, 11 insertions(+), 20 deletions(-) delete mode 100644 test_type.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index bc55a6c1a8..aa1582e795 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -101,13 +101,18 @@ export class DbClientClass< query2( type: T, ): BasedQuery2 + query2( type: T, - id: number | Partial>, + id: + | number + | (Partial> & { [Symbol.toStringTag]?: never }), ): BasedQuery2 query2( type: T, - id?: number | Partial>, + id?: + | number + | (Partial> & { [Symbol.toStringTag]?: never }), ): BasedQuery2 { return new BasedQuery2(this, type, id) } diff --git a/test/alias/alias.ts b/test/alias/alias.ts index f7840d47e2..ea96e1760b 100644 --- a/test/alias/alias.ts +++ b/test/alias/alias.ts @@ -1,5 +1,4 @@ import { notEqual } from 'assert' -import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' import test from '../shared/test.js' import { testDb } from '../shared/index.js' @@ -35,7 +34,7 @@ await test('simple', async (t) => { 'One alias', ) - deepEqual(await db.query2('user', user2).get(), { + deepEqual(await db.query2('user', await user2).get(), { id: 2, externalId: 'cool2', potato: '', @@ -79,6 +78,7 @@ await test('simple', async (t) => { externalId: 'potato', potato: 'success', }) + const res2 = await db.upsert( 'user', { @@ -88,11 +88,13 @@ await test('simple', async (t) => { potato: 'wrong', }, ) + deepEqual(await db.query2('user', res2).get(), { id: 3, externalId: 'potato', potato: 'wrong', }) + deepEqual( await db.query2('user', { externalId: 'i-dont-exists-haha!' }).get(), null, diff --git a/test_type.ts b/test_type.ts deleted file mode 100644 index 862cf76f8a..0000000000 --- a/test_type.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { testDb } from './test/shared/index.js' - -async function run() { - const db = null as any as Awaited> - const a = await db.query2('vote').include('country', 'AU').get() - - const b = await db - .query2('vote') - .include((q) => q('sequence')) - .get() - - const c = await db - .query2('vote') - .include('country', 'AU', (q) => q('sequence')) - .get() -} From 8225226a46e6aa32d0831bce2af46dacb4b4e736 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 23 Feb 2026 17:29:02 -0300 Subject: [PATCH 387/449] fix top level count selection --- src/db-query/ast/aggregates.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index 49a3491306..e14419e0f1 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -71,7 +71,9 @@ const isRootCountOnly = (ast: QueryAst) => { !ast.stddev && !ast.variance && !ast.hmean && - !ast.cardinality + !ast.cardinality && + !ast.filter && + !ast.groupBy ) } From 206942efd3cab72db9edcd2fb1aa79cb164960c1 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 23 Feb 2026 17:43:04 -0300 Subject: [PATCH 388/449] fix top level count test --- test/aggregate/basic.ts | 1104 +++++++++++++++++++-------------------- 1 file changed, 548 insertions(+), 556 deletions(-) diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 6c7daffd14..3f2176c9d7 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -119,10 +119,6 @@ await test('top level count', async (t) => { }) const s = db.create('sequence', { votes: [nl1, nl2, au1] }) - db.drain() - db.create('sequence', { votes: [nl1] }) - db.create('sequence', { votes: [nl2] }) - db.create('sequence', { votes: [au1] }) // top level ---------------------------------- @@ -135,10 +131,9 @@ await test('top level count', async (t) => { // deepEqual( // await db // .query2('vote') - // .filter('country', '=', 'aa') // string filter not implemented yet + // .filter('country', '=', 'aa') // string filter not implemented yet // .count() - // .get() - // , + // .get(), // { count: 2 }, // 'count, top level, with filter', // ) @@ -154,17 +149,11 @@ await test('top level count', async (t) => { // .query2('vote') // .filter('country', '=', 'zz') // string filter not implemented yet // .count() - // .get() - // , + // .get(), // { count: 0 }, // 'count, with no match filtering, string value', // ) - console.log(await db.query2('vote').filter('NL', '=', 20).get()) // correct (1 item) - console.dir(await db.query2('vote').filter('NL', '=', 20).count().ast, { - depth: null, - }) // not correct (count: 3) - deepEqual( await db.query2('vote').filter('NL', '=', 20).count().get(), { count: 1 }, @@ -284,47 +273,49 @@ await test('two phase accumulation', async (t) => { 'stddev, top level, groupBy', ) - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').stddev('NL', { mode: 'population' })) - .get(), - [ - { - id: 1, - votes: { - NL: { stddev: 13.922643427165687 }, - }, - }, - ], - 'stddev, branched References, no groupBy', - ) + // branched References not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').stddev('NL', { mode: 'population' })) + // .get(), + // [ + // { + // id: 1, + // votes: { + // NL: { stddev: 13.922643427165687 }, + // }, + // }, + // ], + // 'stddev, branched References, no groupBy', + // ) - deepEqual( - await db - .query2('sequence') - .include((q) => - q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), - ) - .get(), - [ - { - id: 1, - votes: { - Brazil: { - NL: { stddev: 0 }, - }, - bb: { - NL: { stddev: 6.5 }, - }, - aa: { - NL: { stddev: 2.5 }, - }, - }, - }, - ], - 'stddev, branched References, groupBy', - ) + // branched References not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').stddev('NL', { mode: 'population' }).groupBy('country'), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // Brazil: { + // NL: { stddev: 0 }, + // }, + // bb: { + // NL: { stddev: 6.5 }, + // }, + // aa: { + // NL: { stddev: 2.5 }, + // }, + // }, + // }, + // ], + // 'stddev, branched References, groupBy', + // ) }) await test('numeric types', async (t) => { @@ -397,136 +388,136 @@ await test('numeric types', async (t) => { }) const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) - deepEqual( - await db.query2('vote').groupBy('region').get(), - { - bb: {}, - aa: {}, - Great: {}, - }, - 'empty aggregation function, group by', - ) + // deepEqual( + // await db.query2('vote').groupBy('region').get(), + // { + // bb: {}, + // aa: {}, + // Great: {}, + // }, + // 'empty aggregation function, group by', + // ) - deepEqual( - await db.query2('vote').sum('NL', 'FI').groupBy('region').get(), - { - bb: { - NL: { sum: 33 }, - FI: { sum: -1000000.3 }, - }, - aa: { - NL: { sum: 93 }, - FI: { sum: 0 }, - }, - Great: { - NL: { sum: 50 }, - FI: { sum: -50.999 }, - }, - }, - 'sum, main, group by', - ) + // deepEqual( + // await db.query2('vote').sum('NL', 'FI').groupBy('region').get(), + // { + // bb: { + // NL: { sum: 33 }, + // FI: { sum: -1000000.3 }, + // }, + // aa: { + // NL: { sum: 93 }, + // FI: { sum: 0 }, + // }, + // Great: { + // NL: { sum: 50 }, + // FI: { sum: -50.999 }, + // }, + // }, + // 'sum, main, group by', + // ) - deepEqual( - await db.query2('vote').count().groupBy('region').get(), - { - bb: { - count: 2, - }, - aa: { - count: 2, - }, - Great: { - count: 1, - }, - }, - 'count, main, group by', - ) + // deepEqual( + // await db.query2('vote').count().groupBy('region').get(), + // { + // bb: { + // count: 2, + // }, + // aa: { + // count: 2, + // }, + // Great: { + // count: 1, + // }, + // }, + // 'count, main, group by', + // ) - deepEqual( - await db.query2('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), - { - bb: { - NL: { avg: 16.5 }, - PT: { avg: 21.5 }, - FI: { avg: -500000.15 }, - }, - aa: { - NL: { avg: 46.5 }, - PT: { avg: 46.5 }, - FI: { avg: 0 }, - }, - Great: { - NL: { avg: 50 }, - PT: { avg: 50 }, - FI: { avg: -50.999 }, - }, - }, - 'avg, main, group by', - ) + // deepEqual( + // await db.query2('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), + // { + // bb: { + // NL: { avg: 16.5 }, + // PT: { avg: 21.5 }, + // FI: { avg: -500000.15 }, + // }, + // aa: { + // NL: { avg: 46.5 }, + // PT: { avg: 46.5 }, + // FI: { avg: 0 }, + // }, + // Great: { + // NL: { avg: 50 }, + // PT: { avg: 50 }, + // FI: { avg: -50.999 }, + // }, + // }, + // 'avg, main, group by', + // ) - deepEqual( - await db.query2('vote').hmean('NL', 'PT', 'FI').groupBy('region').get(), - { - bb: { - NL: { hmean: 13.93939393939394 }, - PT: { hmean: 15.348837209302324 }, - FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition - }, - aa: { - NL: { hmean: 46.236559139784944 }, - PT: { hmean: 46.236559139784944 }, - FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition - }, - Great: { - NL: { hmean: 50 }, - PT: { hmean: 50 }, - FI: { hmean: -50.99900000000001 }, // harmonic mean is not designed for negative numbers but possible - }, - }, - 'hmean, main, group by', - ) + // deepEqual( + // await db.query2('vote').hmean('NL', 'PT', 'FI').groupBy('region').get(), + // { + // bb: { + // NL: { hmean: 13.93939393939394 }, + // PT: { hmean: 15.348837209302324 }, + // FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition + // }, + // aa: { + // NL: { hmean: 46.236559139784944 }, + // PT: { hmean: 46.236559139784944 }, + // FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition + // }, + // Great: { + // NL: { hmean: 50 }, + // PT: { hmean: 50 }, + // FI: { hmean: -50.99900000000001 }, // harmonic mean is not designed for negative numbers but possible + // }, + // }, + // 'hmean, main, group by', + // ) - deepEqual( - await db - .query2('vote') - .stddev('NL', 'PL', { mode: 'population' }) - .groupBy('region') - .get(), - { - bb: { - NL: { stddev: 6.5 }, - PL: { stddev: 11.5 }, - }, - aa: { - NL: { stddev: 3.5 }, - PL: { stddev: 11.5 }, - }, - Great: { - NL: { stddev: 0 }, - PL: { stddev: 0 }, - }, - }, - 'stddev, main, group by, pop', - ) + // deepEqual( + // await db + // .query2('vote') + // .stddev('NL', 'PL', { mode: 'population' }) + // .groupBy('region') + // .get(), + // { + // bb: { + // NL: { stddev: 6.5 }, + // PL: { stddev: 11.5 }, + // }, + // aa: { + // NL: { stddev: 3.5 }, + // PL: { stddev: 11.5 }, + // }, + // Great: { + // NL: { stddev: 0 }, + // PL: { stddev: 0 }, + // }, + // }, + // 'stddev, main, group by, pop', + // ) - deepEqual( - await db.query2('vote').stddev('NL', 'PL').groupBy('region').get(), - { - bb: { - NL: { stddev: 9.192388155425117 }, - PL: { stddev: 16.263455967290593 }, - }, - aa: { - NL: { stddev: 4.949747468305833 }, - PL: { stddev: 16.263455967290593 }, - }, - Great: { - NL: { stddev: 0 }, - PL: { stddev: 0 }, - }, - }, - 'stddev, main, group by, default=sample', - ) + // deepEqual( + // await db.query2('vote').stddev('NL', 'PL').groupBy('region').get(), + // { + // bb: { + // NL: { stddev: 9.192388155425117 }, + // PL: { stddev: 16.263455967290593 }, + // }, + // aa: { + // NL: { stddev: 4.949747468305833 }, + // PL: { stddev: 16.263455967290593 }, + // }, + // Great: { + // NL: { stddev: 0 }, + // PL: { stddev: 0 }, + // }, + // }, + // 'stddev, main, group by, default=sample', + // ) deepEqual( await db @@ -550,385 +541,386 @@ await test('numeric types', async (t) => { }, 'variance, main, group by, population', ) - deepEqual( - await db - .query2('vote') - .var('NL', 'PL', { mode: 'sample' }) - .groupBy('region') - .get(), - { - bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, - aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, - Great: { NL: { variance: 0 }, PL: { variance: 0 } }, - }, - 'variance, main, group by, sample', - ) - - deepEqual( - await db.query2('vote').var('NL', 'PL').groupBy('region').get(), - { - bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, - aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, - Great: { NL: { variance: 0 }, PL: { variance: 0 } }, - }, - 'variance, main, group by, default (sample)', - ) - deepEqual( - await db.query2('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), - { - bb: { - NL: { max: 23 }, - NO: { max: -10 }, - PT: { max: 33 }, - FI: { max: 0 }, - }, - aa: { - NL: { max: 50 }, - NO: { max: -43 }, - PT: { max: 50 }, - FI: { max: 0 }, - }, - Great: { - NL: { max: 50 }, - NO: { max: -50 }, - PT: { max: 50 }, - FI: { max: -50.999 }, - }, - }, - 'max, main, group by', - ) - - deepEqual( - await db.query2('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), - { - bb: { - NL: { min: 10 }, - NO: { min: -23 }, - PT: { min: 10 }, - FI: { min: -1000000.3 }, - }, - aa: { - NL: { min: 43 }, - NO: { min: -50 }, - PT: { min: 43 }, - FI: { min: 0 }, - }, - Great: { - NL: { min: 50 }, - NO: { min: -50 }, - PT: { min: 50 }, - FI: { min: -50.999 }, - }, - }, - 'min, main, group by', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').sum('NL')) - .get(), - [ - { - id: 1, - votes: { - NL: { sum: 176 }, - }, - }, - ], - 'references, not grouped', - ) - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').avg('NL')) - .get(), - [ - { - id: 1, - votes: { - NL: { avg: 35.2 }, - }, - }, - ], - 'avg, references, not grouped', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').hmean('NL')) - .get(), - [ - { - id: 1, - votes: { - NL: { hmean: 24.18565978675536 }, - }, - }, - ], - 'hmean, references, not grouped', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').groupBy('region').sum('NL')) - .get(), - [ - { - id: 1, - votes: { - bb: { - NL: { sum: 33 }, - }, - aa: { - NL: { sum: 93 }, - }, - Great: { - NL: { sum: 50 }, - }, - }, - }, - ], - 'sum, references, group by', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').groupBy('region').count()) - .get(), - [ - { - id: 1, - votes: { - bb: { count: 2 }, - aa: { count: 2 }, - Great: { count: 1 }, - }, - }, - ], - 'count, references, group by', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => - q('votes').groupBy('region').stddev('NL', { mode: 'population' }), - ) - .get(), - [ - { - id: 1, - votes: { - bb: { - NL: { stddev: 6.5 }, - }, - aa: { - NL: { stddev: 3.5 }, - }, - Great: { - NL: { stddev: 0 }, - }, - }, - }, - ], - 'stddev, references, group by', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').groupBy('region').stddev('NL')) - .get(), - [ - { - id: 1, - votes: { - bb: { NL: { stddev: 9.192388155425117 } }, - aa: { NL: { stddev: 4.949747468305833 } }, - Great: { NL: { stddev: 0 } }, - }, - }, - ], - 'stddev, references, group by', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => - q('votes').groupBy('region').var('NL', { mode: 'population' }), - ) - .get(), - [ - { - id: 1, - votes: { - bb: { - NL: { variance: 42.25 }, - }, - aa: { - NL: { variance: 12.25 }, - }, - Great: { - NL: { variance: 0 }, - }, - }, - }, - ], - 'variance, references, group by, pop', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => - q('votes').groupBy('region').var('NL', { mode: 'sample' }), - ) - .get(), - [ - { - id: 1, - votes: { - bb: { NL: { variance: 84.5 } }, - aa: { NL: { variance: 24.5 } }, - Great: { NL: { variance: 0 } }, - }, - }, - ], - 'variance, references, group by, sample', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').groupBy('region').var('NL')) - .get(), - [ - { - id: 1, - votes: { - bb: { NL: { variance: 84.5 } }, - aa: { NL: { variance: 24.5 } }, - Great: { NL: { variance: 0 } }, - }, - }, - ], - 'variance, references, group by, defaul (sample)', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').groupBy('region').avg('NL')) - .get(), - [ - { - id: 1, - votes: { - bb: { - NL: { avg: 16.5 }, - }, - aa: { - NL: { avg: 46.5 }, - }, - Great: { - NL: { avg: 50 }, - }, - }, - }, - ], - 'avg, references, group by', - ) - - deepEqual( - await db - .query2('sequence') - .include((q) => q('votes').groupBy('region').hmean('NL')) - .get(), - [ - { - id: 1, - votes: { - bb: { - NL: { hmean: 13.93939393939394 }, - }, - aa: { - NL: { hmean: 46.236559139784944 }, - }, - Great: { - NL: { hmean: 50 }, - }, - }, - }, - ], - 'hmean, references, group by', - ) -}) - -await test('fixed length strings', async (t) => { - const db = await testDb(t, { - types: { - product: { - name: { type: 'string', maxBytes: 10 }, - flap: 'number', - }, - shelve: { - code: { type: 'string', maxBytes: 4 }, - products: { - items: { - ref: 'product', - prop: 'product', - }, - }, - }, - }, - }) - - const rnd = fastPrng() - for (let i = 0; i < 100; i++) { - let p = db.create('product', { - name: `lala ${rnd(0, 10)}`, - flap: Math.random() * 100, - }) - db.drain() - db.create('shelve', { - code: `S${rnd(0, 10)}`, - products: [p], - }) - } - - equal( - Number( - Object.keys( - await db - .query2('product') - .include('*') - .avg('flap') - .groupBy('name') - .get(), - )[0].substring(4, 6), - ) < 100, - true, - 'fixed length strings on main', - ) - - equal( - Number( - Object.keys( - await db - .query2('shelve') - .include((q) => q('products').avg('flap').groupBy('name')) - .get(), - )[0].substring(4, 6), - ) < 100, - true, - 'fixed length strings on references', - ) + // deepEqual( + // await db + // .query2('vote') + // .var('NL', 'PL', { mode: 'sample' }) + // .groupBy('region') + // .get(), + // { + // bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, + // aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, + // Great: { NL: { variance: 0 }, PL: { variance: 0 } }, + // }, + // 'variance, main, group by, sample', + // ) + + // deepEqual( + // await db.query2('vote').var('NL', 'PL').groupBy('region').get(), + // { + // bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, + // aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, + // Great: { NL: { variance: 0 }, PL: { variance: 0 } }, + // }, + // 'variance, main, group by, default (sample)', + // ) + + // deepEqual( + // await db.query2('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + // { + // bb: { + // NL: { max: 23 }, + // NO: { max: -10 }, + // PT: { max: 33 }, + // FI: { max: 0 }, + // }, + // aa: { + // NL: { max: 50 }, + // NO: { max: -43 }, + // PT: { max: 50 }, + // FI: { max: 0 }, + // }, + // Great: { + // NL: { max: 50 }, + // NO: { max: -50 }, + // PT: { max: 50 }, + // FI: { max: -50.999 }, + // }, + // }, + // 'max, main, group by', + // ) + + // deepEqual( + // await db.query2('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + // { + // bb: { + // NL: { min: 10 }, + // NO: { min: -23 }, + // PT: { min: 10 }, + // FI: { min: -1000000.3 }, + // }, + // aa: { + // NL: { min: 43 }, + // NO: { min: -50 }, + // PT: { min: 43 }, + // FI: { min: 0 }, + // }, + // Great: { + // NL: { min: 50 }, + // NO: { min: -50 }, + // PT: { min: 50 }, + // FI: { min: -50.999 }, + // }, + // }, + // 'min, main, group by', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').sum('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // NL: { sum: 176 }, + // }, + // }, + // ], + // 'references, not grouped', + // ) + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').avg('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // NL: { avg: 35.2 }, + // }, + // }, + // ], + // 'avg, references, not grouped', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').hmean('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // NL: { hmean: 24.18565978675536 }, + // }, + // }, + // ], + // 'hmean, references, not grouped', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').sum('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { sum: 33 }, + // }, + // aa: { + // NL: { sum: 93 }, + // }, + // Great: { + // NL: { sum: 50 }, + // }, + // }, + // }, + // ], + // 'sum, references, group by', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').count()) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { count: 2 }, + // aa: { count: 2 }, + // Great: { count: 1 }, + // }, + // }, + // ], + // 'count, references, group by', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').groupBy('region').stddev('NL', { mode: 'population' }), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { stddev: 6.5 }, + // }, + // aa: { + // NL: { stddev: 3.5 }, + // }, + // Great: { + // NL: { stddev: 0 }, + // }, + // }, + // }, + // ], + // 'stddev, references, group by', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').stddev('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { stddev: 9.192388155425117 } }, + // aa: { NL: { stddev: 4.949747468305833 } }, + // Great: { NL: { stddev: 0 } }, + // }, + // }, + // ], + // 'stddev, references, group by', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').groupBy('region').var('NL', { mode: 'population' }), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { variance: 42.25 }, + // }, + // aa: { + // NL: { variance: 12.25 }, + // }, + // Great: { + // NL: { variance: 0 }, + // }, + // }, + // }, + // ], + // 'variance, references, group by, pop', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').groupBy('region').var('NL', { mode: 'sample' }), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { variance: 84.5 } }, + // aa: { NL: { variance: 24.5 } }, + // Great: { NL: { variance: 0 } }, + // }, + // }, + // ], + // 'variance, references, group by, sample', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').var('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { variance: 84.5 } }, + // aa: { NL: { variance: 24.5 } }, + // Great: { NL: { variance: 0 } }, + // }, + // }, + // ], + // 'variance, references, group by, defaul (sample)', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').avg('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { avg: 16.5 }, + // }, + // aa: { + // NL: { avg: 46.5 }, + // }, + // Great: { + // NL: { avg: 50 }, + // }, + // }, + // }, + // ], + // 'avg, references, group by', + // ) + + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').hmean('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { hmean: 13.93939393939394 }, + // }, + // aa: { + // NL: { hmean: 46.236559139784944 }, + // }, + // Great: { + // NL: { hmean: 50 }, + // }, + // }, + // }, + // ], + // 'hmean, references, group by', + // ) + // }) + + // await test('fixed length strings', async (t) => { + // const db = await testDb(t, { + // types: { + // product: { + // name: { type: 'string', maxBytes: 10 }, + // flap: 'number', + // }, + // shelve: { + // code: { type: 'string', maxBytes: 4 }, + // products: { + // items: { + // ref: 'product', + // prop: 'product', + // }, + // }, + // }, + // }, + // }) + + // const rnd = fastPrng() + // for (let i = 0; i < 100; i++) { + // let p = db.create('product', { + // name: `lala ${rnd(0, 10)}`, + // flap: Math.random() * 100, + // }) + // db.drain() + // db.create('shelve', { + // code: `S${rnd(0, 10)}`, + // products: [p], + // }) + // } + + // equal( + // Number( + // Object.keys( + // await db + // .query2('product') + // .include('*') + // .avg('flap') + // .groupBy('name') + // .get(), + // )[0].substring(4, 6), + // ) < 100, + // true, + // 'fixed length strings on main', + // ) + + // equal( + // Number( + // Object.keys( + // await db + // .query2('shelve') + // .include((q) => q('products').avg('flap').groupBy('name')) + // .get(), + // )[0].substring(4, 6), + // ) < 100, + // true, + // 'fixed length strings on references', + // ) }) /* await test('range', async (t) => { From 98277c039b3e02445806648d45e4f85d8319feb7 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 23 Feb 2026 17:48:42 -0300 Subject: [PATCH 389/449] fix variance population in AST --- src/db-query/ast/aggregates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db-query/ast/aggregates.ts b/src/db-query/ast/aggregates.ts index e14419e0f1..411b50073b 100644 --- a/src/db-query/ast/aggregates.ts +++ b/src/db-query/ast/aggregates.ts @@ -223,7 +223,7 @@ export const isAggregateAst = (ast: QueryAst) => { const checkSamplingMode = (ast: QueryAst): boolean => { if ( ast['stddev']?.samplingMode === 'population' || - ast['var']?.samplingMode === 'population' + ast['variance']?.samplingMode === 'population' ) return false else return true From f77c9502a532c509daf8772675149c4ddb0988a4 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Mon, 23 Feb 2026 18:33:54 -0300 Subject: [PATCH 390/449] fix numeric types agg test --- package-lock.json | 4 - test/aggregate/basic.ts | 962 ++++++++++++++++++++-------------------- 2 files changed, 491 insertions(+), 475 deletions(-) diff --git a/package-lock.json b/package-lock.json index 20b4d7c1f2..71baa502af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,10 +174,6 @@ "@based/locale-x86-64-gnu": "*" } }, - "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { - "dev": true, - "optional": true - }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 3f2176c9d7..92a7327174 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -388,136 +388,136 @@ await test('numeric types', async (t) => { }) const s = db.create('sequence', { votes: [nl1, nl2, au1, au2, br1] }) - // deepEqual( - // await db.query2('vote').groupBy('region').get(), - // { - // bb: {}, - // aa: {}, - // Great: {}, - // }, - // 'empty aggregation function, group by', - // ) + deepEqual( + await db.query2('vote').groupBy('region').get(), + { + bb: {}, + aa: {}, + Great: {}, + }, + 'empty aggregation function, group by', + ) - // deepEqual( - // await db.query2('vote').sum('NL', 'FI').groupBy('region').get(), - // { - // bb: { - // NL: { sum: 33 }, - // FI: { sum: -1000000.3 }, - // }, - // aa: { - // NL: { sum: 93 }, - // FI: { sum: 0 }, - // }, - // Great: { - // NL: { sum: 50 }, - // FI: { sum: -50.999 }, - // }, - // }, - // 'sum, main, group by', - // ) + deepEqual( + await db.query2('vote').sum('NL', 'FI').groupBy('region').get(), + { + bb: { + NL: { sum: 33 }, + FI: { sum: -1000000.3 }, + }, + aa: { + NL: { sum: 93 }, + FI: { sum: 0 }, + }, + Great: { + NL: { sum: 50 }, + FI: { sum: -50.999 }, + }, + }, + 'sum, main, group by', + ) - // deepEqual( - // await db.query2('vote').count().groupBy('region').get(), - // { - // bb: { - // count: 2, - // }, - // aa: { - // count: 2, - // }, - // Great: { - // count: 1, - // }, - // }, - // 'count, main, group by', - // ) + deepEqual( + await db.query2('vote').count().groupBy('region').get(), + { + bb: { + count: 2, + }, + aa: { + count: 2, + }, + Great: { + count: 1, + }, + }, + 'count, main, group by', + ) - // deepEqual( - // await db.query2('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), - // { - // bb: { - // NL: { avg: 16.5 }, - // PT: { avg: 21.5 }, - // FI: { avg: -500000.15 }, - // }, - // aa: { - // NL: { avg: 46.5 }, - // PT: { avg: 46.5 }, - // FI: { avg: 0 }, - // }, - // Great: { - // NL: { avg: 50 }, - // PT: { avg: 50 }, - // FI: { avg: -50.999 }, - // }, - // }, - // 'avg, main, group by', - // ) + deepEqual( + await db.query2('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), + { + bb: { + NL: { avg: 16.5 }, + PT: { avg: 21.5 }, + FI: { avg: -500000.15 }, + }, + aa: { + NL: { avg: 46.5 }, + PT: { avg: 46.5 }, + FI: { avg: 0 }, + }, + Great: { + NL: { avg: 50 }, + PT: { avg: 50 }, + FI: { avg: -50.999 }, + }, + }, + 'avg, main, group by', + ) - // deepEqual( - // await db.query2('vote').hmean('NL', 'PT', 'FI').groupBy('region').get(), - // { - // bb: { - // NL: { hmean: 13.93939393939394 }, - // PT: { hmean: 15.348837209302324 }, - // FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition - // }, - // aa: { - // NL: { hmean: 46.236559139784944 }, - // PT: { hmean: 46.236559139784944 }, - // FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition - // }, - // Great: { - // NL: { hmean: 50 }, - // PT: { hmean: 50 }, - // FI: { hmean: -50.99900000000001 }, // harmonic mean is not designed for negative numbers but possible - // }, - // }, - // 'hmean, main, group by', - // ) + deepEqual( + await db.query2('vote').hmean('NL', 'PT', 'FI').groupBy('region').get(), + { + bb: { + NL: { hmean: 13.93939393939394 }, + PT: { hmean: 15.348837209302324 }, + FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition + }, + aa: { + NL: { hmean: 46.236559139784944 }, + PT: { hmean: 46.236559139784944 }, + FI: { hmean: 0 }, // harmonic mean when any of the values is 0 is 0 by definition + }, + Great: { + NL: { hmean: 50 }, + PT: { hmean: 50 }, + FI: { hmean: -50.99900000000001 }, // harmonic mean is not designed for negative numbers but possible + }, + }, + 'hmean, main, group by', + ) - // deepEqual( - // await db - // .query2('vote') - // .stddev('NL', 'PL', { mode: 'population' }) - // .groupBy('region') - // .get(), - // { - // bb: { - // NL: { stddev: 6.5 }, - // PL: { stddev: 11.5 }, - // }, - // aa: { - // NL: { stddev: 3.5 }, - // PL: { stddev: 11.5 }, - // }, - // Great: { - // NL: { stddev: 0 }, - // PL: { stddev: 0 }, - // }, - // }, - // 'stddev, main, group by, pop', - // ) + deepEqual( + await db + .query2('vote') + .stddev('NL', 'PL', { mode: 'population' }) + .groupBy('region') + .get(), + { + bb: { + NL: { stddev: 6.5 }, + PL: { stddev: 11.5 }, + }, + aa: { + NL: { stddev: 3.5 }, + PL: { stddev: 11.5 }, + }, + Great: { + NL: { stddev: 0 }, + PL: { stddev: 0 }, + }, + }, + 'stddev, main, group by, pop', + ) - // deepEqual( - // await db.query2('vote').stddev('NL', 'PL').groupBy('region').get(), - // { - // bb: { - // NL: { stddev: 9.192388155425117 }, - // PL: { stddev: 16.263455967290593 }, - // }, - // aa: { - // NL: { stddev: 4.949747468305833 }, - // PL: { stddev: 16.263455967290593 }, - // }, - // Great: { - // NL: { stddev: 0 }, - // PL: { stddev: 0 }, - // }, - // }, - // 'stddev, main, group by, default=sample', - // ) + deepEqual( + await db.query2('vote').stddev('NL', 'PL').groupBy('region').get(), + { + bb: { + NL: { stddev: 9.192388155425117 }, + PL: { stddev: 16.263455967290593 }, + }, + aa: { + NL: { stddev: 4.949747468305833 }, + PL: { stddev: 16.263455967290593 }, + }, + Great: { + NL: { stddev: 0 }, + PL: { stddev: 0 }, + }, + }, + 'stddev, main, group by, default=sample', + ) deepEqual( await db @@ -542,386 +542,406 @@ await test('numeric types', async (t) => { 'variance, main, group by, population', ) - // deepEqual( - // await db - // .query2('vote') - // .var('NL', 'PL', { mode: 'sample' }) - // .groupBy('region') - // .get(), + deepEqual( + await db + .query2('vote') + .var('NL', 'PL', { mode: 'sample' }) + .groupBy('region') + .get(), + { + bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, + aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, + Great: { NL: { variance: 0 }, PL: { variance: 0 } }, + }, + 'variance, main, group by, sample', + ) + + deepEqual( + await db.query2('vote').var('NL', 'PL').groupBy('region').get(), + { + bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, + aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, + Great: { NL: { variance: 0 }, PL: { variance: 0 } }, + }, + 'variance, main, group by, default (sample)', + ) + + deepEqual( + await db.query2('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + { + bb: { + NL: { max: 23 }, + NO: { max: -10 }, + PT: { max: 33 }, + FI: { max: 0 }, + }, + aa: { + NL: { max: 50 }, + NO: { max: -43 }, + PT: { max: 50 }, + FI: { max: 0 }, + }, + Great: { + NL: { max: 50 }, + NO: { max: -50 }, + PT: { max: 50 }, + FI: { max: -50.999 }, + }, + }, + 'max, main, group by', + ) + + deepEqual( + await db.query2('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + { + bb: { + NL: { min: 10 }, + NO: { min: -23 }, + PT: { min: 10 }, + FI: { min: -1000000.3 }, + }, + aa: { + NL: { min: 43 }, + NO: { min: -50 }, + PT: { min: 43 }, + FI: { min: 0 }, + }, + Great: { + NL: { min: 50 }, + NO: { min: -50 }, + PT: { min: 50 }, + FI: { min: -50.999 }, + }, + }, + 'min, main, group by', + ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').sum('NL')) + // .get(), + // [ // { - // bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, - // aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, - // Great: { NL: { variance: 0 }, PL: { variance: 0 } }, + // id: 1, + // votes: { + // NL: { sum: 176 }, + // }, // }, - // 'variance, main, group by, sample', - // ) + // ], + // 'references, not grouped', + // ) - // deepEqual( - // await db.query2('vote').var('NL', 'PL').groupBy('region').get(), + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').avg('NL')) + // .get(), + // [ // { - // bb: { NL: { variance: 84.5 }, PL: { variance: 264.5 } }, - // aa: { NL: { variance: 24.5 }, PL: { variance: 264.5 } }, - // Great: { NL: { variance: 0 }, PL: { variance: 0 } }, + // id: 1, + // votes: { + // NL: { avg: 35.2 }, + // }, // }, - // 'variance, main, group by, default (sample)', - // ) + // ], + // 'avg, references, not grouped', + // ) - // deepEqual( - // await db.query2('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').hmean('NL')) + // .get(), + // [ // { - // bb: { - // NL: { max: 23 }, - // NO: { max: -10 }, - // PT: { max: 33 }, - // FI: { max: 0 }, - // }, - // aa: { - // NL: { max: 50 }, - // NO: { max: -43 }, - // PT: { max: 50 }, - // FI: { max: 0 }, - // }, - // Great: { - // NL: { max: 50 }, - // NO: { max: -50 }, - // PT: { max: 50 }, - // FI: { max: -50.999 }, + // id: 1, + // votes: { + // NL: { hmean: 24.18565978675536 }, // }, // }, - // 'max, main, group by', - // ) + // ], + // 'hmean, references, not grouped', + // ) - // deepEqual( - // await db.query2('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').sum('NL')) + // .get(), + // [ // { - // bb: { - // NL: { min: 10 }, - // NO: { min: -23 }, - // PT: { min: 10 }, - // FI: { min: -1000000.3 }, - // }, - // aa: { - // NL: { min: 43 }, - // NO: { min: -50 }, - // PT: { min: 43 }, - // FI: { min: 0 }, + // id: 1, + // votes: { + // bb: { + // NL: { sum: 33 }, + // }, + // aa: { + // NL: { sum: 93 }, + // }, + // Great: { + // NL: { sum: 50 }, + // }, // }, - // Great: { - // NL: { min: 50 }, - // NO: { min: -50 }, - // PT: { min: 50 }, - // FI: { min: -50.999 }, + // }, + // ], + // 'sum, references, group by', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').count()) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { count: 2 }, + // aa: { count: 2 }, + // Great: { count: 1 }, // }, // }, - // 'min, main, group by', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').sum('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // NL: { sum: 176 }, + // ], + // 'count, references, group by', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').groupBy('region').stddev('NL', { mode: 'population' }), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { stddev: 6.5 }, // }, - // }, - // ], - // 'references, not grouped', - // ) - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').avg('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // NL: { avg: 35.2 }, + // aa: { + // NL: { stddev: 3.5 }, // }, - // }, - // ], - // 'avg, references, not grouped', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').hmean('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // NL: { hmean: 24.18565978675536 }, + // Great: { + // NL: { stddev: 0 }, // }, // }, - // ], - // 'hmean, references, not grouped', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').groupBy('region').sum('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { sum: 33 }, - // }, - // aa: { - // NL: { sum: 93 }, - // }, - // Great: { - // NL: { sum: 50 }, - // }, - // }, + // }, + // ], + // 'stddev, references, group by', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').stddev('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { stddev: 9.192388155425117 } }, + // aa: { NL: { stddev: 4.949747468305833 } }, + // Great: { NL: { stddev: 0 } }, // }, - // ], - // 'sum, references, group by', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').groupBy('region').count()) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { count: 2 }, - // aa: { count: 2 }, - // Great: { count: 1 }, + // }, + // ], + // 'stddev, references, group by', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').groupBy('region').var('NL', { mode: 'population' }), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { variance: 42.25 }, // }, - // }, - // ], - // 'count, references, group by', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => - // q('votes').groupBy('region').stddev('NL', { mode: 'population' }), - // ) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { stddev: 6.5 }, - // }, - // aa: { - // NL: { stddev: 3.5 }, - // }, - // Great: { - // NL: { stddev: 0 }, - // }, + // aa: { + // NL: { variance: 12.25 }, // }, - // }, - // ], - // 'stddev, references, group by', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').groupBy('region').stddev('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { stddev: 9.192388155425117 } }, - // aa: { NL: { stddev: 4.949747468305833 } }, - // Great: { NL: { stddev: 0 } }, + // Great: { + // NL: { variance: 0 }, // }, // }, - // ], - // 'stddev, references, group by', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => - // q('votes').groupBy('region').var('NL', { mode: 'population' }), - // ) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { variance: 42.25 }, - // }, - // aa: { - // NL: { variance: 12.25 }, - // }, - // Great: { - // NL: { variance: 0 }, - // }, - // }, + // }, + // ], + // 'variance, references, group by, pop', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => + // q('votes').groupBy('region').var('NL', { mode: 'sample' }), + // ) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { variance: 84.5 } }, + // aa: { NL: { variance: 24.5 } }, + // Great: { NL: { variance: 0 } }, // }, - // ], - // 'variance, references, group by, pop', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => - // q('votes').groupBy('region').var('NL', { mode: 'sample' }), - // ) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { variance: 84.5 } }, - // aa: { NL: { variance: 24.5 } }, - // Great: { NL: { variance: 0 } }, - // }, + // }, + // ], + // 'variance, references, group by, sample', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').var('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { NL: { variance: 84.5 } }, + // aa: { NL: { variance: 24.5 } }, + // Great: { NL: { variance: 0 } }, // }, - // ], - // 'variance, references, group by, sample', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').groupBy('region').var('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { NL: { variance: 84.5 } }, - // aa: { NL: { variance: 24.5 } }, - // Great: { NL: { variance: 0 } }, + // }, + // ], + // 'variance, references, group by, defaul (sample)', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').avg('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { avg: 16.5 }, // }, - // }, - // ], - // 'variance, references, group by, defaul (sample)', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').groupBy('region').avg('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { avg: 16.5 }, - // }, - // aa: { - // NL: { avg: 46.5 }, - // }, - // Great: { - // NL: { avg: 50 }, - // }, + // aa: { + // NL: { avg: 46.5 }, // }, - // }, - // ], - // 'avg, references, group by', - // ) - - // deepEqual( - // await db - // .query2('sequence') - // .include((q) => q('votes').groupBy('region').hmean('NL')) - // .get(), - // [ - // { - // id: 1, - // votes: { - // bb: { - // NL: { hmean: 13.93939393939394 }, - // }, - // aa: { - // NL: { hmean: 46.236559139784944 }, - // }, - // Great: { - // NL: { hmean: 50 }, - // }, + // Great: { + // NL: { avg: 50 }, // }, // }, - // ], - // 'hmean, references, group by', - // ) - // }) - - // await test('fixed length strings', async (t) => { - // const db = await testDb(t, { - // types: { - // product: { - // name: { type: 'string', maxBytes: 10 }, - // flap: 'number', - // }, - // shelve: { - // code: { type: 'string', maxBytes: 4 }, - // products: { - // items: { - // ref: 'product', - // prop: 'product', - // }, + // }, + // ], + // 'avg, references, group by', + // ) + + // Branched queries not implemented yet in AST + // deepEqual( + // await db + // .query2('sequence') + // .include((q) => q('votes').groupBy('region').hmean('NL')) + // .get(), + // [ + // { + // id: 1, + // votes: { + // bb: { + // NL: { hmean: 13.93939393939394 }, + // }, + // aa: { + // NL: { hmean: 46.236559139784944 }, + // }, + // Great: { + // NL: { hmean: 50 }, // }, // }, // }, - // }) - - // const rnd = fastPrng() - // for (let i = 0; i < 100; i++) { - // let p = db.create('product', { - // name: `lala ${rnd(0, 10)}`, - // flap: Math.random() * 100, - // }) - // db.drain() - // db.create('shelve', { - // code: `S${rnd(0, 10)}`, - // products: [p], - // }) - // } - - // equal( - // Number( - // Object.keys( - // await db - // .query2('product') - // .include('*') - // .avg('flap') - // .groupBy('name') - // .get(), - // )[0].substring(4, 6), - // ) < 100, - // true, - // 'fixed length strings on main', - // ) - - // equal( - // Number( - // Object.keys( - // await db - // .query2('shelve') - // .include((q) => q('products').avg('flap').groupBy('name')) - // .get(), - // )[0].substring(4, 6), - // ) < 100, - // true, - // 'fixed length strings on references', - // ) + // ], + // 'hmean, references, group by', + // ) }) + +await test('fixed length strings', async (t) => { + const db = await testDb(t, { + types: { + product: { + name: { type: 'string', maxBytes: 10 }, + flap: 'number', + }, + shelve: { + code: { type: 'string', maxBytes: 4 }, + products: { + items: { + ref: 'product', + prop: 'product', + }, + }, + }, + }, + }) + + const rnd = fastPrng() + for (let i = 0; i < 100; i++) { + let p = db.create('product', { + name: `lala ${rnd(0, 10)}`, + flap: Math.random() * 100, + }) + db.drain() + db.create('shelve', { + code: `S${rnd(0, 10)}`, + products: [p], + }) + } + + console.dir( + await db.query2('product').include('*').avg('flap').groupBy('name').get(), + { maxDepth: null }, + ) + + equal( + Number( + Object.keys( + await db + .query2('product') + .include('*') + .avg('flap') + .groupBy('name') + .get(), + )[0].substring(4, 6), + ) < 100, + true, + 'fixed length strings on main', + ) + + // Branched queries not implemented yet in AST + // equal( + // Number( + // Object.keys( + // await db + // .query2('shelve') + // .include((q) => q('products').avg('flap').groupBy('name')) + // .get(), + // )[0].substring(4, 6), + // ) < 100, + // true, + // 'fixed length strings on references', + // ) +}) + /* await test('range', async (t) => { const db = await testDb(t, { From 5a1a1011deb670e74a7686c95eef0d49a3cd837f Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 09:27:17 +0100 Subject: [PATCH 391/449] Incorrect typing for TypedArray --- src/db-client/modify/types.ts | 3 +-- src/db-client/query2/types.ts | 7 ++++--- src/schema/infer.ts | 3 +-- src/schema/schema/payload.ts | 3 +-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 0f9fcd8cfe..df4bc1fb54 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -3,8 +3,7 @@ import { type SchemaTypes } from '../../schema.js' import type { BasedModify } from './index.js' type TypedArray = - | Uint8Array - | Float32Array + | Int8Array | Uint8Array | Int16Array | Uint16Array diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index ae6a9ff225..05f07e274b 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -1,13 +1,14 @@ +import type { ResolvedProps } from '../../schema/index.js' + type TypedArray = - | Uint8Array - | Float32Array | Int8Array + | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array + | Float32Array | Float64Array -import type { ResolvedProps } from '../../schema/index.js' export type InferSchemaOutput< S extends { types: any; locales?: any }, diff --git a/src/schema/infer.ts b/src/schema/infer.ts index 05d718e91c..5c69a4b5bb 100644 --- a/src/schema/infer.ts +++ b/src/schema/infer.ts @@ -1,8 +1,7 @@ import type { Schema } from './schema/schema.js' type TypedArray = - | Uint8Array - | Float32Array + | Int8Array | Uint8Array | Int16Array | Uint16Array diff --git a/src/schema/schema/payload.ts b/src/schema/schema/payload.ts index 0340b5b2f2..87afe3a28f 100644 --- a/src/schema/schema/payload.ts +++ b/src/schema/schema/payload.ts @@ -4,8 +4,7 @@ import type { SchemaOut } from './schema.js' type BasedModify = any // Mock BasedModify to avoid circular dependency type TypedArray = - | Uint8Array - | Float32Array + | Int8Array | Uint8Array | Int16Array | Uint16Array From 50f80b3d81578b9283f16f0cdddfaafe9e5ef961 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 09:32:44 +0100 Subject: [PATCH 392/449] Remove unused imports --- src/db-client/modify/types.ts | 2 -- src/schema/schema/payload.ts | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index df4bc1fb54..7620679806 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -1,5 +1,3 @@ -import { type SchemaTypes } from '../../schema.js' - import type { BasedModify } from './index.js' type TypedArray = diff --git a/src/schema/schema/payload.ts b/src/schema/schema/payload.ts index 87afe3a28f..2dc070692f 100644 --- a/src/schema/schema/payload.ts +++ b/src/schema/schema/payload.ts @@ -1,6 +1,3 @@ -import type { SchemaOut } from './schema.js' - -// import type { BasedModify } from '../../db-client/modify/index.js' type BasedModify = any // Mock BasedModify to avoid circular dependency type TypedArray = From cbbde9ac06b117261b8481d467b4551b06a32612 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 09:54:00 +0100 Subject: [PATCH 393/449] Only one type for TypedArray --- src/db-client/modify/types.ts | 11 +---------- src/db-client/query2/types.ts | 11 +---------- src/protocol/db-read/types.ts | 11 +---------- src/schema/defs/props/vector.ts | 2 +- src/schema/index.ts | 10 ++++++++++ src/schema/infer.ts | 11 +---------- src/schema/schema/payload.ts | 12 ++---------- 7 files changed, 17 insertions(+), 51 deletions(-) diff --git a/src/db-client/modify/types.ts b/src/db-client/modify/types.ts index 7620679806..38ee81b4a9 100644 --- a/src/db-client/modify/types.ts +++ b/src/db-client/modify/types.ts @@ -1,14 +1,5 @@ import type { BasedModify } from './index.js' - -type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array +import type { TypedArray } from '../../schema/index.js' type NumInc = number | { increment: number } diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 05f07e274b..9ce575cb89 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -1,14 +1,5 @@ import type { ResolvedProps } from '../../schema/index.js' - -type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array +import type { TypedArray } from '../../schema/index.js' export type InferSchemaOutput< S extends { types: any; locales?: any }, diff --git a/src/protocol/db-read/types.ts b/src/protocol/db-read/types.ts index ca5a21ddc4..dd264fad17 100644 --- a/src/protocol/db-read/types.ts +++ b/src/protocol/db-read/types.ts @@ -1,5 +1,6 @@ import type { SchemaHooks } from '../../schema/index.js' import type { PropTypeEnum, VectorBaseTypeEnum } from '../../zigTsExports.js' +import type { TypedArray } from '../../schema.js' export type Item = { id: number @@ -17,16 +18,6 @@ export type Meta = { export type AggItem = Partial -export type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array - export enum ReaderSchemaEnum { edge = 1, default = 2, diff --git a/src/schema/defs/props/vector.ts b/src/schema/defs/props/vector.ts index 26edba7e13..198e80975c 100644 --- a/src/schema/defs/props/vector.ts +++ b/src/schema/defs/props/vector.ts @@ -16,7 +16,7 @@ import { import type { AutoSizedUint8Array } from '../../../utils/AutoSizedUint8Array.js' import { BasePropDef } from './base.js' import type { TypeDef } from '../index.js' -import { TypedArray } from '../../../protocol/index.js' +import { TypedArray } from '../../../schema/index.js' export const vector = class Vector extends BasePropDef { constructor(schema: SchemaVector, path: string[], typeDef: TypeDef) { diff --git a/src/schema/index.ts b/src/schema/index.ts index e7efab8a5a..88fe38d063 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -45,6 +45,16 @@ export * from './serialize.js' export * from './infer.js' export * as semver from './semver/mod.js' +export type TypedArray = + | Int8Array + | Uint8Array + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + // eslint-disable-next-line export const parse = ( schema: StrictSchema, diff --git a/src/schema/infer.ts b/src/schema/infer.ts index 5c69a4b5bb..1a052ab7bc 100644 --- a/src/schema/infer.ts +++ b/src/schema/infer.ts @@ -1,15 +1,6 @@ +import type { TypedArray } from './index.js' import type { Schema } from './schema/schema.js' -type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array - // Map schema types to TypeScript types type TypeMap = { string: string diff --git a/src/schema/schema/payload.ts b/src/schema/schema/payload.ts index 2dc070692f..bec3d70ce5 100644 --- a/src/schema/schema/payload.ts +++ b/src/schema/schema/payload.ts @@ -1,14 +1,6 @@ -type BasedModify = any // Mock BasedModify to avoid circular dependency +import type { TypedArray } from '../index.js' -type TypedArray = - | Int8Array - | Uint8Array - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - | Float32Array - | Float64Array +type BasedModify = any // Mock BasedModify to avoid circular dependency type NumInc = number | { increment: number } From d27f8e91347169c8261d10c9cf9a2666928c44af Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 10:03:38 +0100 Subject: [PATCH 394/449] wip --- src/db-query/ast/iteratorType.ts | 2 -- test/clientServer/index.ts | 7 +------ test/db-schema/schema.ts | 9 ++------- test/shared/index.ts | 23 ++++++++++++++++++++--- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/db-query/ast/iteratorType.ts b/src/db-query/ast/iteratorType.ts index 4fe4ca67ef..c083506aa1 100644 --- a/src/db-query/ast/iteratorType.ts +++ b/src/db-query/ast/iteratorType.ts @@ -131,7 +131,5 @@ export const getIteratorType = ( } } - console.log('----->', QueryIteratorTypeInverse[base]) - return base as QueryIteratorTypeEnum } diff --git a/test/clientServer/index.ts b/test/clientServer/index.ts index 27903fa927..23b7222fdd 100644 --- a/test/clientServer/index.ts +++ b/test/clientServer/index.ts @@ -1,9 +1,4 @@ -import { - BasedDb, - DbClient, - DbServer, - getDefaultHooks, -} from '../../src/index.js' +import { DbClient, DbServer, getDefaultHooks } from '../../src/index.js' import { testDb } from '../shared/index.js' import test from '../shared/test.js' diff --git a/test/db-schema/schema.ts b/test/db-schema/schema.ts index 6812162e4d..1c6892f2a2 100644 --- a/test/db-schema/schema.ts +++ b/test/db-schema/schema.ts @@ -2,20 +2,15 @@ import test from '../shared/test.js' import { BasedDb } from '../../src/index.js' import { setTimeout } from 'node:timers/promises' import { deepEqual, throws } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('support many fields on type', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.destroy()) - const props = {} for (let i = 0; i < 248; i++) { props['myProp' + i] = 'string' } - await db.setSchema({ + await testDb(t, { types: { flurp: props, }, diff --git a/test/shared/index.ts b/test/shared/index.ts index 21f24ae143..d3c22c86fb 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,7 +1,7 @@ import { createHash } from 'node:crypto' import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' import type { ResolveSchema, SchemaIn, StrictSchema } from '../../src/schema.js' -import { BasedDb, DbServer, type DbClient } from '../../src/sdk.js' +import { BasedDb, DbClient, DbServer, getDefaultHooks } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' export * from './examples.js' @@ -39,10 +39,27 @@ export const testDb = async ( t: Parameters[1]>[0], schema: StrictSchema, ): Promise>> => { - const db = new BasedDb({ path: t.tmp }) + const server = await testDbServer(t) + return testDbClient(server, schema) +} + +export const testDbClient = ( + server: DbServer, + schema: StrictSchema, +): Promise>> => { + const client = new DbClient({ + hooks: getDefaultHooks(server), + }) + return client.setSchema(schema) +} + +export const testDbServer = async ( + t: Parameters[1]>[0], +): Promise => { + const db = new DbServer({ path: t.tmp }) await db.start({ clean: true }) t.after(() => db.destroy()) - return db.setSchema(schema) + return db } export async function countDirtyBlocks(server: DbServer) { From 67aef2ce1a88a8e66cf1ed207d694047db581ce5 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 10:07:31 +0100 Subject: [PATCH 395/449] covec test fixes --- test/colvec.ts | 60 ++++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/test/colvec.ts b/test/colvec.ts index 3c88b88cb3..8ef352c956 100644 --- a/test/colvec.ts +++ b/test/colvec.ts @@ -2,14 +2,14 @@ import test from './shared/test.js' import { BasedDb } from '../src/index.js' import { deepEqual, perf } from './shared/assert.js' -await test.skip('colvec', async (t) => { +await test.skip('basic', async (t) => { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { row: { props: { @@ -43,18 +43,18 @@ await test.skip('colvec', async (t) => { await perf(async () => { for (let i = 0; i < N; i++) { genVec() - db.create('row', { vec }) + client.create('row', { vec }) } - await db.drain() + await client.drain() }, 'row') reset() await perf(async () => { for (let i = 0; i < N; i++) { genVec() - db.create('col', { vec }) + client.create('col', { vec }) } - await db.drain() + await client.drain() }, 'col') vec[0] = 2311.0 @@ -67,49 +67,21 @@ await test.skip('colvec', async (t) => { vec[7] = 7261.0 await perf(async () => { await db - .query('row') + .query2('row') .include('*') .filter('vec', 'like', vec, { fn: 'euclideanDistance', score: 1 }) .get() }, 'QUERY row') - - await perf(async () => { - global.__basedDb__native__.colvecTest( - db.server.dbCtxExternal, - 3, - 1, - 1, - N + 1, - ) - }, 'QUERY col') - - const res = await db.query('col').include('vec').range(0, 2).get().toObject() - deepEqual(res, [ - { - id: 1, - vec: new Float32Array([ - 2311, 5054, 1.5612034346858506e-39, 1.007378107771942e-37, - 3.76158192263132e-37, 1.6815581571897805e-44, 0, 5391, - ]), - }, - { - id: 2, - vec: new Float32Array([ - 5391, 5094, 1.5612034346858506e-39, 4.029512431087768e-37, - 3.76158192263132e-37, 1.6815581571897805e-44, 0, 6071, - ]), - }, - ]) }) -await test('colvec int8', async (t) => { +await test('int8 vector', async (t) => { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { col: { blockCapacity: 10_000, @@ -122,13 +94,13 @@ await test('colvec int8', async (t) => { }) for (let i = 0; i < 5; i++) { - db.create('col', { + client.create('col', { str: Int8Array.from([i + 1, i + 2, i + 3, i + 4]), }) } - // await db.drain() + await client.drain() - deepEqual(await db.query('col').include('str').get(), [ + deepEqual(await client.query2('col').include('str').get(), [ { id: 1, str: new Int8Array([1, 2, 3, 4]) }, { id: 2, str: new Int8Array([2, 3, 4, 5]) }, { id: 3, str: new Int8Array([3, 4, 5, 6]) }, @@ -137,14 +109,14 @@ await test('colvec int8', async (t) => { ]) }) -await test('colvec float32', async (t) => { +await test('float32 vector', async (t) => { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { col: { blockCapacity: 10_000, @@ -157,11 +129,11 @@ await test('colvec float32', async (t) => { }) for (let i = 0; i < 1; i++) { - db.create('col', { + client.create('col', { str: Float32Array.from([1.23123, 1.3]), }) } - deepEqual(await db.query('col').include('str').get(), [ + deepEqual(await client.query2('col').include('str').get(), [ { id: 1, str: new Float32Array([1.23123, 1.3]) }, ]) }) From a4ad927aadf7f2cf14894e2d61bd8e0dd6727511 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 10:19:34 +0100 Subject: [PATCH 396/449] backup test with testDb --- test/shared/index.ts | 10 ++++++++-- test/shared/test.ts | 12 ++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/test/shared/index.ts b/test/shared/index.ts index d3c22c86fb..d1460ae91a 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -38,8 +38,9 @@ export function logMemoryUsage() { export const testDb = async ( t: Parameters[1]>[0], schema: StrictSchema, + opts: { noBackup?: boolean } = {}, ): Promise>> => { - const server = await testDbServer(t) + const server = await testDbServer(t, opts) return testDbClient(server, schema) } @@ -55,10 +56,15 @@ export const testDbClient = ( export const testDbServer = async ( t: Parameters[1]>[0], + opts: { noBackup?: boolean } = {}, ): Promise => { const db = new DbServer({ path: t.tmp }) await db.start({ clean: true }) - t.after(() => db.destroy()) + if (opts.noBackup) { + t.after(() => db.destroy()) + } else { + t.after(() => t.backup(db)) + } return db } diff --git a/test/shared/test.ts b/test/shared/test.ts index c6dd6ff432..bbb1dd83de 100644 --- a/test/shared/test.ts +++ b/test/shared/test.ts @@ -1,7 +1,7 @@ import { styleText } from 'node:util' import { fileURLToPath } from 'url' import { join, dirname, resolve } from 'path' -import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' +import { BasedDb, DbClient, DbServer, getDefaultHooks } from '../../src/index.js' import { deepEqual } from './assert.js' import { wait, bufToHex } from '../../src/utils/index.js' import fs from 'node:fs/promises' @@ -29,7 +29,7 @@ const errors = new Set() export type T = { after: (fn: () => Promise | void, push?: boolean) => void - backup: (db: BasedDb) => Promise + backup: (db: DbServer) => Promise tmp: string } @@ -61,7 +61,7 @@ const test: { afters.unshift(fn) } }, - backup: async (db: BasedDb) => { + backup: async (db: DbServer) => { afters.push(async () => { try { await db.destroy() @@ -72,15 +72,15 @@ const test: { return } - const make = async (db: BasedDb) => { + const make = async (db: DbServer) => { const client = new DbClient({ - hooks: getDefaultHooks(db.server), + hooks: getDefaultHooks(db), }) const checksums: any[] = [] const data: any[] = [] const counts: any[] = [] - for (const type in db.server.schema?.types) { + for (const type in db.schema?.types) { let x = await client.query2(type).include('*', '**').get() checksums.push(x['checksum']) data.push(x) From 6c3d23662563b6ea9a473f0c3ca723a2b54c3c99 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 10:24:36 +0100 Subject: [PATCH 397/449] use testDb --- test/colvec.ts | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/test/colvec.ts b/test/colvec.ts index 8ef352c956..7fef5e9ca0 100644 --- a/test/colvec.ts +++ b/test/colvec.ts @@ -1,15 +1,9 @@ import test from './shared/test.js' -import { BasedDb } from '../src/index.js' import { deepEqual, perf } from './shared/assert.js' +import { testDb } from './shared/index.js' await test.skip('basic', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { row: { props: { @@ -26,7 +20,7 @@ await test.skip('basic', async (t) => { }, }) - deepEqual(db.server.schemaTypesParsed.col.blockCapacity, 10_000) + deepEqual(client.schemaTypesParsed.col.blockCapacity, 10_000) let seed = 100 const next = () => (seed = (214013 * seed + 2531011) % 10e3) @@ -75,13 +69,7 @@ await test.skip('basic', async (t) => { }) await test('int8 vector', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { col: { blockCapacity: 10_000, @@ -110,13 +98,7 @@ await test('int8 vector', async (t) => { }) await test('float32 vector', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { col: { blockCapacity: 10_000, From 167136ac6c17c139d3473b864341cc857d91addd Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 10:26:16 +0100 Subject: [PATCH 398/449] wip --- test/db-schema/schema.ts | 70 +++++++++++++++++++--------------------- test/shared/index.ts | 9 ++++-- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/test/db-schema/schema.ts b/test/db-schema/schema.ts index 1c6892f2a2..9a7c1461a6 100644 --- a/test/db-schema/schema.ts +++ b/test/db-schema/schema.ts @@ -1,11 +1,16 @@ import test from '../shared/test.js' -import { BasedDb } from '../../src/index.js' +import { + BasedDb, + DbClient, + DbServer, + getDefaultHooks, +} from '../../src/index.js' import { setTimeout } from 'node:timers/promises' import { deepEqual, throws } from '../shared/assert.js' -import { testDb } from '../shared/index.js' +import { testDb, testDbClient, testDbServer } from '../shared/index.js' await test('support many fields on type', async (t) => { - const props = {} + const props: Record = {} for (let i = 0; i < 248; i++) { props['myProp' + i] = 'string' } @@ -18,13 +23,8 @@ await test('support many fields on type', async (t) => { }) await test('schema hash', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const server = await testDbServer(t) + const client = await testDbClient(server, { types: { flurp: { name: 'string', @@ -32,9 +32,9 @@ await test('schema hash', async (t) => { }, }) - const hash1 = db.server.schema!.hash + const hash1 = server.schema!.hash - await db.setSchema({ + await client.setSchema({ types: { flurp: { name: 'string', @@ -43,7 +43,7 @@ await test('schema hash', async (t) => { }, }) - const hash2 = db.server.schema!.hash + const hash2 = server.schema!.hash if (!hash1 || !hash2 || hash1 === hash2) { throw new Error('Incorrect hash') @@ -51,31 +51,30 @@ await test('schema hash', async (t) => { }) await test('dont accept modify with mismatch schema', async (t) => { - const db = new BasedDb({ - path: t.tmp, + const server = await testDbServer(t) + const client = new DbClient({ + hooks: Object.assign(getDefaultHooks(server), { + async flushModify(buf: Uint8Array) { + buf = new Uint8Array(buf) + await setTimeout(100) + return server.modify(buf) + }, + }), }) - await db.start({ clean: true }) - t.after(() => db.destroy()) - db.client.hooks.flushModify = async (buf) => { - buf = new Uint8Array(buf) - await setTimeout(100) - return db.server.modify(buf) - } - - await db.setSchema({ + await client.setSchema({ types: { flurp: { name: 'string', }, }, }) - await db.create('flurp', { + await client.create('flurp', { name: 'xxx', }) - const q1 = db.query('flurp') - const setSchemaPromise = db.setSchema({ + const q1 = client.query2('flurp') + const setSchemaPromise = client.setSchema({ types: { flurp: { title: 'string', @@ -83,17 +82,17 @@ await test('dont accept modify with mismatch schema', async (t) => { }, }) - db.create('flurp', { + client.create('flurp', { name: 'yyy', }) await setSchemaPromise throws(() => { - return db.create('flurp', { + return client.create('flurp', { name: 'zzz', }) }) - const res = await db.query('flurp').get().toObject() + const res = await client.query2('flurp').get() deepEqual(res, [ { id: 1, title: '' }, @@ -102,12 +101,11 @@ await test('dont accept modify with mismatch schema', async (t) => { }) await test('set schema before start', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) + const server = new DbServer({ path: t.tmp }) + const client = await testDbClient(server) await throws(() => - db.setSchema({ + client.setSchema({ types: { flurp: { props: { @@ -118,6 +116,6 @@ await test('set schema before start', async (t) => { }), ) - await db.start({ clean: true }) - t.after(() => db.destroy()) + await server.start({ clean: true }) + t.after(() => server.destroy()) }) diff --git a/test/shared/index.ts b/test/shared/index.ts index d3c22c86fb..9185249131 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -43,14 +43,17 @@ export const testDb = async ( return testDbClient(server, schema) } -export const testDbClient = ( +export const testDbClient = async ( server: DbServer, - schema: StrictSchema, + schema?: StrictSchema, ): Promise>> => { const client = new DbClient({ hooks: getDefaultHooks(server), }) - return client.setSchema(schema) + if (schema) { + await client.setSchema(schema) + } + return client as unknown as DbClient> } export const testDbServer = async ( From bf362d37df439761ec108a802873356754b902d6 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 10:31:46 +0100 Subject: [PATCH 399/449] new testDb and query2 --- test/vector.ts | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/test/vector.ts b/test/vector.ts index eabde79768..4e866dd980 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -1,7 +1,8 @@ -import { BasedDb, DbClient } from '../src/index.js' +import { DbClient } from '../src/index.js' import test from './shared/test.js' import { deepEqual, equal } from './shared/assert.js' import { equals } from '../src/utils/index.js' +import {testDb} from './shared/index.js' const data = { cat: [1.5, -0.4, 7.2, 19.6, 20.2], @@ -13,13 +14,7 @@ const data = { } async function initDb(t: Parameters[1]>[0]): Promise { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { data: { props: { @@ -50,7 +45,7 @@ async function initDb(t: Parameters[1]>[0]): Promise { const db = await initDb(t) - const res = (await db.query('data').include('name', 'a').get()).toObject() + const res = await db.query2('data').include('name', 'a').get() for (const r of res) { const a = new Uint8Array(r.a.buffer, 0, r.a.byteLength) const b = new Uint8Array(new Float32Array(data[r.name]).buffer) @@ -86,19 +81,17 @@ await test('query by vector', async (t) => { const db = await initDb(t) const r1 = await db - .query('data') + .query2('data') .include('name') .filter('a', '=', new Float32Array(data['car'].slice(0, 5))) .get() - .toObject() deepEqual(r1[0].name, 'car') const r2 = await db - .query('data') + .query2('data') .include('name') .filter('a', '=', new Float32Array(data['car'])) .get() - .toObject() deepEqual(r2.length, 1) }) @@ -108,11 +101,10 @@ await test.skip('vector like', async (t) => { const fruit = new Float32Array([-5.1, 2.9, 0.8, 7.9, 3.1]) const res = await db - .query('data') + .query2('data') .include('name') .filter('a', 'like', fruit, { fn: 'euclideanDistance', score: 1 }) .get() - .toObject() deepEqual(res, [ { id: 3, name: 'apple' }, @@ -131,12 +123,11 @@ await test.skip('vector like', async (t) => { deepEqual( await db - .query('data') + .query2('data') .include('name') .range(0, 1e6) .filter('a', 'like', fruit, { fn: 'euclideanDistance', score: 1 }) - .get() - .toObject(), + .get(), [ { id: 3, @@ -167,7 +158,7 @@ await test('search', async (t) => { deepEqual( await db - .query('data') + .query2('data') .include('id', 'name') .range(0, 3) .search(fruit, 'a', { fn: 'euclideanDistance', score: 1 }) From b7a7827f68484a88d7401901304c7bc258967cac Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 10:32:02 +0100 Subject: [PATCH 400/449] Too long vectors are no longer truncated --- test/vector.ts | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/vector.ts b/test/vector.ts index 4e866dd980..f1c614e736 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -53,30 +53,6 @@ await test('vector set/get', async (t) => { } }) -await test('vector set wrong size', async (t) => { - const db = await initDb(t) - - const a = db.create('data', { - a: new Float32Array([1, 2, 3]), - name: 'hehe', - }) - const b = db.create('data', { - a: new Float32Array([1, 2, 3, 4, 5, 6]), - name: 'hehe', - }) - await db.drain() - - const [ra, rb] = await db - .query('data') - .filter('id', '=', [await a, await b]) - .include('a') - .get() - - // RFE is truncation right? - deepEqual(ra.a.length, 5) - deepEqual(rb.a.length, 5) -}) - await test('query by vector', async (t) => { const db = await initDb(t) From ecf71d5f041083a824805bd2d886784dc28782b9 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 12:09:29 +0100 Subject: [PATCH 401/449] Handle interrupted type creat properly --- clibs/lib/selva/db.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clibs/lib/selva/db.c b/clibs/lib/selva/db.c index 350dd87577..b5364e7e42 100644 --- a/clibs/lib/selva/db.c +++ b/clibs/lib/selva/db.c @@ -355,10 +355,14 @@ void selva_del_block(struct SelvaDb *db, struct SelvaTypeEntry *te, block_id_t b static void del_all_nodes(struct SelvaDb *db, struct SelvaTypeEntry *te) { struct SelvaTypeBlocks *blocks = te->blocks; - block_id_t blocks_len = blocks->len; - for (block_id_t block_i = 0; block_i < blocks_len; block_i++) { - selva_del_block_unsafe(db, te, block_i, false); + /* blocks could be uninitialized on early startup fail. */ + if (blocks) { + block_id_t blocks_len = blocks->len; + + for (block_id_t block_i = 0; block_i < blocks_len; block_i++) { + selva_del_block_unsafe(db, te, block_i, false); + } } } From f466c214a7d18c52f8713418d2f04e535033f3e6 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 13:23:39 +0100 Subject: [PATCH 402/449] Use testDb --- test/capped.ts | 28 ++++++++-------------------- test/dependent.ts | 47 +++++++++++++---------------------------------- 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/test/capped.ts b/test/capped.ts index b11ea96130..ce811465a8 100644 --- a/test/capped.ts +++ b/test/capped.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('capped type', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { meas: { capped: 3, @@ -36,7 +30,7 @@ await test('capped type', async (t) => { wind: 50, }) - deepEqual(await client.query('meas').get(), [ + deepEqual(await client.query2('meas').get(), [ { id: 1, temperature: 0, humidity: 99, wind: 10 }, { id: 2, temperature: 1, humidity: 98, wind: 10 }, { id: 3, temperature: 2, humidity: 97, wind: 50 }, @@ -47,7 +41,7 @@ await test('capped type', async (t) => { humidity: 1, }) - deepEqual(await client.query('meas').get(), [ + deepEqual(await client.query2('meas').get(), [ { id: 1, temperature: -100, humidity: 1, wind: 10 }, { id: 2, temperature: 1, humidity: 98, wind: 10 }, { id: 3, temperature: 2, humidity: 97, wind: 50 }, @@ -63,7 +57,7 @@ await test('capped type', async (t) => { humidity: 1, }) - deepEqual(await client.query('meas').get(), [ + deepEqual(await client.query2('meas').get(), [ { id: 1, temperature: -100, humidity: 1, wind: 10 }, { id: 2, temperature: -50, humidity: 1, wind: 5 }, { id: 3, temperature: -40, humidity: 1, wind: 10 }, @@ -95,7 +89,7 @@ await test('capped type', async (t) => { humidity: 1, }) - deepEqual(await client.query('meas').get(), [ + deepEqual(await client.query2('meas').get(), [ { id: 1, temperature: -40, humidity: 1, wind: 10 }, { id: 2, temperature: -50, humidity: 1, wind: 10 }, { id: 3, temperature: -40, humidity: 1, wind: 10 }, @@ -103,13 +97,7 @@ await test('capped type', async (t) => { }) await test('capped references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { user: { props: { @@ -140,7 +128,7 @@ await test('capped references', async (t) => { client.create('article', { latest: user }) } - deepEqual(await client.query('user', user).include('**').get(), { + deepEqual(await client.query2('user', user).include('**').get(), { id: 1, latestArticles: [{ id: 6 }, { id: 7 }, { id: 8 }, { id: 9 }, { id: 10 }], }) diff --git a/test/dependent.ts b/test/dependent.ts index 20341b0238..01b4dc5a1e 100644 --- a/test/dependent.ts +++ b/test/dependent.ts @@ -1,15 +1,8 @@ import { deepEqual, equal } from './shared/assert.js' -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' await test('dependent', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const schema = { types: { show: { @@ -50,7 +43,7 @@ await test('dependent', async (t) => { }, } as const - const client = await db.setSchema(schema) + const client = await testDb(t, schema) const createShowTree = async () => { const showId = await client.create('show', {}) @@ -70,7 +63,7 @@ await test('dependent', async (t) => { await client.drain() for (const type in schema.types) { - const len = (await client.query(type).get()).length + const len = (await client.query2(type).get()).length equal(len, 1) } return showId @@ -80,20 +73,13 @@ await test('dependent', async (t) => { await client.delete('show', showId) await client.drain() for (const type in schema.types) { - equal((await client.query(type).get()).length, 0) + equal((await client.query2(type).get()).length, 0) } await createShowTree() }) await test('del children', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { parent: { children: { @@ -119,7 +105,7 @@ await test('del children', async (t) => { for (let i = 0; i < n; i++) { children.push(client.create('child', { parent: head })) } - deepEqual(await client.query('parent', head).include('**').get(), { + deepEqual(await client.query2('parent', head).include('**').get(), { id: await head, children: (await Promise.all(children)).map((id: number) => ({ id })), }) @@ -128,7 +114,7 @@ await test('del children', async (t) => { client.delete('child', child) } await client.drain() - deepEqual(await client.query('parent', head).include('**').get(), { + deepEqual(await client.query2('parent', head).include('**').get(), { id: await head, children: [], }) @@ -137,20 +123,13 @@ await test('del children', async (t) => { children.push(client.create('child', { parent: head })) } await client.delete('parent', head) - deepEqual(await client.query('parent').get(), []) - deepEqual(await client.query('child').get(), []) + deepEqual(await client.query2('parent').get(), []) + deepEqual(await client.query2('child').get(), []) } }) await test('circle of friends', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { human: { name: { type: 'string', maxBytes: 8 }, @@ -176,7 +155,7 @@ await test('circle of friends', async (t) => { // friends: { add: [h2, h1] }, //}) - deepEqual(await client.query('human').include('**').get(), [ + deepEqual(await client.query2('human').include('**').get(), [ { id: 1, friends: [ @@ -219,7 +198,7 @@ await test('circle of friends', async (t) => { ]) client.delete('human', 1) - deepEqual(await client.query('human').include('**').get(), [ + deepEqual(await client.query2('human').include('**').get(), [ { id: 2, friends: [ @@ -241,5 +220,5 @@ await test('circle of friends', async (t) => { ]) client.delete('human', 2) - deepEqual(await client.query('human').include('**').get(), []) + deepEqual(await client.query2('human').include('**').get(), []) }) From b04a50251639905768157a818e6933d793f61cba Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 24 Feb 2026 09:31:33 -0300 Subject: [PATCH 403/449] fix sized string check --- native/query/aggregates/group.zig | 18 +++++++----------- test/aggregate/basic.ts | 5 ----- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/native/query/aggregates/group.zig b/native/query/aggregates/group.zig index 64e382b408..af9d4a342b 100644 --- a/native/query/aggregates/group.zig +++ b/native/query/aggregates/group.zig @@ -54,17 +54,13 @@ inline fn getGrouByKeyValue( if (keyValue.len == 0) return emptyKey; - const key = if (propType == t.PropType.string) - if (propId == 0) - keyValue.ptr[start + 1 .. start + 1 + keyValue[start]] - else - keyValue.ptr[2 + start .. start + keyValue.len - propType.crcLen()] - else if (propType == t.PropType.timestamp) - @constCast(utils.datePart(keyValue.ptr[start .. start + keyValue.len], @enumFromInt(stepType), timezone)) - else if (propType == t.PropType.reference) - Node.getReferenceNodeId(@ptrCast(@alignCast(keyValue.ptr))) - else - keyValue.ptr[start .. start + propType.size()]; + const key = switch (propType) { + .string => if (propId == 0) keyValue.ptr[start + 1 .. start + 1 + keyValue[start]] else keyValue.ptr[2 + start .. start + keyValue.len - propType.crcLen()], + .stringFixed => if (propId == 0) keyValue.ptr[start + 1 .. start + 1 + keyValue[start]] else keyValue.ptr[2 + start .. start + keyValue.len - propType.crcLen()], + .timestamp => @constCast(utils.datePart(keyValue.ptr[start .. start + keyValue.len], @enumFromInt(stepType), timezone)), + .reference => Node.getReferenceNodeId(@ptrCast(@alignCast(keyValue.ptr))), + else => keyValue.ptr[start .. start + propType.size()], + }; return key; } diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index 92a7327174..a83115b412 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -907,11 +907,6 @@ await test('fixed length strings', async (t) => { }) } - console.dir( - await db.query2('product').include('*').avg('flap').groupBy('name').get(), - { maxDepth: null }, - ) - equal( Number( Object.keys( From cd3ee032c81b4db5e93fa7c0480a1819b6f7d8d7 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 24 Feb 2026 09:31:54 -0300 Subject: [PATCH 404/449] dev --- test/aggregate/dev.ts | 331 ++++++++++++++++++++++++++---------------- 1 file changed, 204 insertions(+), 127 deletions(-) diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index c2db462c23..b857e89f6b 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -1,7 +1,8 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { deepEqual } from '../shared/assert.js' +import { deepEqual, equal } from '../shared/assert.js' import { testDb } from '../shared/index.js' +import { fastPrng } from '../../src/utils/fastPrng.js' // await test('kev', async (t) => { // const db = new BasedDb({ @@ -128,144 +129,220 @@ import { testDb } from '../shared/index.js' // lala.inspect(10) // }) +// await test('yyy', async (t) => { +// const db = await testDb(t, { +// types: { +// team: { +// props: { +// teamName: { type: 'string' }, +// city: { type: 'string' }, +// players: { +// items: { +// ref: 'player', +// prop: 'team', +// }, +// }, +// }, +// }, +// player: { +// props: { +// playerName: { type: 'string' }, +// position: { type: 'string' }, +// goalsScored: 'uint16', +// gamesPlayed: 'uint16', +// team: { +// ref: 'team', +// prop: 'players', +// }, +// }, +// }, +// }, +// }) + +// const p1 = db.create('player', { +// playerName: 'Martin', +// position: 'Forward', +// goalsScored: 10, +// gamesPlayed: 5, +// }) +// const p2 = db.create('player', { +// playerName: 'Jemerson', +// position: 'Defender', +// goalsScored: 1, +// gamesPlayed: 10, +// }) +// const p3 = db.create('player', { +// playerName: 'Pavon', +// position: 'Forward', +// goalsScored: 12, +// gamesPlayed: 6, +// }) +// const p4 = db.create('player', { +// playerName: 'Wout', +// position: 'Forward', +// goalsScored: 8, +// gamesPlayed: 7, +// }) +// const p5 = db.create('player', { +// playerName: 'Jorrel', +// position: 'Defender', +// goalsScored: 2, +// gamesPlayed: 9, +// }) + +// const t1 = db.create('team', { +// teamName: 'Grêmio', +// city: 'Porto Alegre', +// players: [p1, p2, p3], +// }) +// const t2 = db.create('team', { +// teamName: 'Ajax', +// city: 'Amsterdam', +// players: [p4, p5], +// }) +// const t3 = db.create('team', { +// teamName: 'Boca Juniors', +// city: 'Buenos Aires', +// players: [], +// }) +// const t4 = db.create('team', { +// teamName: 'Barcelona', +// city: 'Barcelona', +// players: [ +// db.create('player', { +// playerName: 'Lewandowski', +// position: 'Forward', +// goalsScored: 5, +// gamesPlayed: 5, +// }), +// ], +// }) + +// const result = await db +// .query2('team') +// .include('teamName', 'city', (select) => +// select('players') +// .sum('goalsScored', 'gamesPlayed') +// .groupBy('position') +// .range(0, 10), +// ) +// .get() + +// result.debug() +// result.inspect() + +// deepEqual( +// result.toObject(), +// [ +// { +// id: 1, +// teamName: 'Grêmio', +// city: 'Porto Alegre', +// players: { +// Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) +// Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) +// }, +// }, +// { +// id: 2, +// teamName: 'Ajax', +// city: 'Amsterdam', +// players: { +// Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) +// Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) +// }, +// }, +// { +// id: 3, +// teamName: 'Boca Juniors', +// city: 'Buenos Aires', +// players: {}, // does anybody wants to play for Boca? +// }, +// { +// id: 4, +// teamName: 'Barcelona', +// city: 'Barcelona', +// players: { +// Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski +// }, +// }, +// ], +// 'Include parent props, with referenced items grouped by their own prop, and aggregations', +// ) +// }) + await test('yyy', async (t) => { - const db = await testDb(t, { + const db = new BasedDb({ + path: t.tmp, + }) + await db.start({ clean: true }) + t.after(() => db.stop()) + + await db.setSchema({ types: { - team: { - props: { - teamName: { type: 'string' }, - city: { type: 'string' }, - players: { - items: { - ref: 'player', - prop: 'team', - }, - }, - }, + product: { + name: { type: 'string', maxBytes: 10 }, + flap: 'number', }, - player: { - props: { - playerName: { type: 'string' }, - position: { type: 'string' }, - goalsScored: 'uint16', - gamesPlayed: 'uint16', - team: { - ref: 'team', - prop: 'players', + shelve: { + code: { type: 'string', maxBytes: 4 }, + products: { + items: { + ref: 'product', + prop: 'product', }, }, }, }, }) - const p1 = db.create('player', { - playerName: 'Martin', - position: 'Forward', - goalsScored: 10, - gamesPlayed: 5, - }) - const p2 = db.create('player', { - playerName: 'Jemerson', - position: 'Defender', - goalsScored: 1, - gamesPlayed: 10, - }) - const p3 = db.create('player', { - playerName: 'Pavon', - position: 'Forward', - goalsScored: 12, - gamesPlayed: 6, - }) - const p4 = db.create('player', { - playerName: 'Wout', - position: 'Forward', - goalsScored: 8, - gamesPlayed: 7, - }) - const p5 = db.create('player', { - playerName: 'Jorrel', - position: 'Defender', - goalsScored: 2, - gamesPlayed: 9, - }) + const rnd = fastPrng() + for (let i = 0; i < 100; i++) { + let p = db.create('product', { + name: `lala ${rnd(0, 10)}`, + flap: Math.random() * 100, + }) + db.create('shelve', { + code: `S${rnd(0, 10)}`, + products: [p], + }) + } - const t1 = db.create('team', { - teamName: 'Grêmio', - city: 'Porto Alegre', - players: [p1, p2, p3], - }) - const t2 = db.create('team', { - teamName: 'Ajax', - city: 'Amsterdam', - players: [p4, p5], - }) - const t3 = db.create('team', { - teamName: 'Boca Juniors', - city: 'Buenos Aires', - players: [], - }) - const t4 = db.create('team', { - teamName: 'Barcelona', - city: 'Barcelona', - players: [ - db.create('player', { - playerName: 'Lewandowski', - position: 'Forward', - goalsScored: 5, - gamesPlayed: 5, - }), - ], - }) - - const result = await db - .query2('team') - .include('teamName', 'city', (select) => - select('players') - .sum('goalsScored', 'gamesPlayed') - .groupBy('position') - .range(0, 10), - ) + await db + .query('product') + // .include('*') + .avg('flap') + .groupBy('name') .get() + .inspect() - // result.debug() - // result.inspect() + // equal( + // Number( + // Object.keys( + // await db + // .query('product') + // .include('*') + // .avg('flap') + // .groupBy('name') + // .get() + // .toObject(), + // )[0].substring(4, 6), + // ) < 100, + // true, + // 'fixed length strings on main', + // ) - // deepEqual( - // result.toObject(), - // [ - // { - // id: 1, - // teamName: 'Grêmio', - // city: 'Porto Alegre', - // players: { - // Forward: { goalsScored: { sum: 22 }, gamesPlayed: { sum: 11 } }, // Martin (10,5) + Pavon (12,6) - // Defender: { goalsScored: { sum: 1 }, gamesPlayed: { sum: 10 } }, // Jemerson (1,10) - // }, - // }, - // { - // id: 2, - // teamName: 'Ajax', - // city: 'Amsterdam', - // players: { - // Forward: { goalsScored: { sum: 8 }, gamesPlayed: { sum: 7 } }, // Wout (8,7) - // Defender: { goalsScored: { sum: 2 }, gamesPlayed: { sum: 9 } }, // Jorrel (2,9) - // }, - // }, - // { - // id: 3, - // teamName: 'Boca Juniors', - // city: 'Buenos Aires', - // players: {}, // does anybody wants to play for Boca? - // }, - // { - // id: 4, - // teamName: 'Barcelona', - // city: 'Barcelona', - // players: { - // Forward: { goalsScored: { sum: 5 }, gamesPlayed: { sum: 5 } }, // Lewandowski - // }, - // }, - // ], - // 'Include parent props, with referenced items grouped by their own prop, and aggregations', + // equal( + // Number( + // Object.keys( + // await db + // .query('shelve') + // .include((q) => q('products').avg('flap').groupBy('name')) + // .get() + // .toObject(), + // )[0].substring(4, 6), + // ) < 100, + // true, + // 'fixed length strings on references', // ) }) From 1e20cd7e3b8c9b5ea656f5bdd687b61a3cab300c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 13:33:12 +0100 Subject: [PATCH 405/449] testDb and query2 --- test/default.ts | 72 +++++++++++++++---------------------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/test/default.ts b/test/default.ts index fc4a9bffe1..651d9c541a 100644 --- a/test/default.ts +++ b/test/default.ts @@ -1,7 +1,7 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' import { convertToTimestamp } from '../src/utils/index.js' +import { testDb } from './shared/index.js' const derp = new Set([ '$nice', @@ -25,14 +25,8 @@ const defaultBinary = new Uint8Array([1, 2, 3]) const defaultText = { en: 'Default Label' } await test('edges', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - // Add all supported data types as edge properties (no date/text) - await db.setSchema({ + const client = await testDb(t, { types: { user: { props: { @@ -61,11 +55,11 @@ await test('edges', async (t) => { }, }) - const userId = await db.create('user', { - friends: [db.create('user')], + const userId = await client.create('user', { + friends: [client.create('user', {})], }) - deepEqual(await db.query('user', userId).include('friends.**').get(), { + deepEqual(await client.query2('user', userId).include('friends.**').get(), { id: 2, friends: [ { @@ -92,13 +86,13 @@ await test('edges', async (t) => { ], }) - await db.update('user', userId, { + await client.update('user', userId, { friends: { update: [{ id: 1, $role: 'derp' }], }, }) - deepEqual(await db.query('user', userId).include('friends.**').get(), { + deepEqual(await client.query2('user', userId).include('friends.**').get(), { id: 2, friends: [ { @@ -125,13 +119,13 @@ await test('edges', async (t) => { ], }) - await db.update('user', userId, { + await clint.update('user', userId, { friends: { update: [{ id: 1, $nice: false }], }, }) - deepEqual(await db.query('user', userId).include('friends.**').get(), { + deepEqual(await client.query2('user', userId).include('friends.**').get(), { id: 2, friends: [ { @@ -160,13 +154,7 @@ await test('edges', async (t) => { }) await test('separate', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, fi: {}, @@ -199,7 +187,7 @@ await test('separate', async (t) => { const userId = await db.create('user', {}) deepEqual( - await db.query('user', userId).include('*', '**').get(), + await db.query2('user', userId).include('*', '**').get(), { id: userId, flap: { @@ -220,7 +208,7 @@ await test('separate', async (t) => { }) deepEqual( - await db.query('user', userId2).include('*', '**').get(), + await db.query2('user', userId2).include('*', '**').get(), { id: userId2, flap: { @@ -236,13 +224,7 @@ await test('separate', async (t) => { }) await test('default values for all props in user type', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, }, @@ -313,7 +295,7 @@ await test('default values for all props in user type', async (t) => { const userId = await db.create('user', {}) deepEqual( - await db.query('user', userId).include('*', '**').get(), + await db.query2('user', userId).include('*', '**').get(), { id: userId, isNice: true, @@ -343,7 +325,7 @@ await test('default values for all props in user type', async (t) => { }) deepEqual( - await db.query('user', userNullId).get(), + await db.query2('user', userNullId).get(), { id: 2, label: { en: 'Default Label' }, @@ -362,13 +344,7 @@ await test('default values for all props in user type', async (t) => { }) await test('negative default values for numeric types', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db =await testDb(t, { types: { user: { props: { @@ -394,7 +370,7 @@ await test('negative default values for numeric types', async (t) => { const userId = await db.create('user', {}) deepEqual( - await db.query('user', userId).get(), + await db.query2('user', userId).get(), { id: userId, negativeNumber: -42, @@ -411,7 +387,7 @@ await test('negative default values for numeric types', async (t) => { }) deepEqual( - await db.query('user', userOverrideId).get(), + await db.query2('user', userOverrideId).get(), { id: 2, negativeNumber: -100, @@ -423,13 +399,7 @@ await test('negative default values for numeric types', async (t) => { }) await test('object', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { snurp: { preferences: { @@ -448,7 +418,7 @@ await test('object', async (t) => { const snurpId = await db.create('snurp', {}) deepEqual( - await db.query('snurp', snurpId).get(), + await db.query2('snurp', snurpId).get(), { id: snurpId, preferences: { @@ -470,7 +440,7 @@ await test('object', async (t) => { }, }) - deepEqual(await db.query('snurp', snurpCustomId).get(), { + deepEqual(await db.query2('snurp', snurpCustomId).get(), { id: 2, preferences: { units: 'imperial', From 8120e174252d00d7402c1eb44fcadc9d435a2e44 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 14:16:57 +0100 Subject: [PATCH 406/449] Unused var --- test/default.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/default.ts b/test/default.ts index 651d9c541a..d13c61d2c5 100644 --- a/test/default.ts +++ b/test/default.ts @@ -3,22 +3,6 @@ import { deepEqual } from './shared/assert.js' import { convertToTimestamp } from '../src/utils/index.js' import { testDb } from './shared/index.js' -const derp = new Set([ - '$nice', - '$role', - '$count', - '$score', - '$flag', - '$amount', - '$big', - '$huge', - '$max', - '$label', - '$bin', - '$timestamp', - '$enum', -]) - const defaultTimestamp = '2023-03-15T12:00:00.000Z' const defaultJson = { enabled: true, value: 10 } const defaultBinary = new Uint8Array([1, 2, 3]) From 775d7f9a5610f8ce6e9a111be8f3c69a837a1b72 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 14:33:15 +0100 Subject: [PATCH 407/449] More explicit schema is required now --- test/default.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/default.ts b/test/default.ts index d13c61d2c5..6f167d04ec 100644 --- a/test/default.ts +++ b/test/default.ts @@ -232,6 +232,7 @@ await test('default values for all props in user type', async (t) => { default: defaultTimestamp, }, level: { + type: 'enum', enum: ['low', 'medium', 'high'], default: 'medium', }, @@ -260,6 +261,7 @@ await test('default values for all props in user type', async (t) => { // default: [], // something in there }, meta: { + type: 'object', props: { rating: { type: 'uint8', From 1d5f26bf9f6f0a15993ac96bceb6af5567643e17 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 14:49:19 +0100 Subject: [PATCH 408/449] Add noClean opt to testDb --- test/shared/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/shared/index.ts b/test/shared/index.ts index 25af021451..58ccd6b287 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -38,7 +38,7 @@ export function logMemoryUsage() { export const testDb = async ( t: Parameters[1]>[0], schema: StrictSchema, - opts: { noBackup?: boolean } = {}, + opts: { noBackup?: boolean, noClean?: boolean } = {}, ): Promise>> => { const server = await testDbServer(t, opts) return testDbClient(server, schema) @@ -59,10 +59,10 @@ export const testDbClient = async ( export const testDbServer = async ( t: Parameters[1]>[0], - opts: { noBackup?: boolean } = {}, + opts: { noBackup?: boolean, noClean?: boolean } = {}, ): Promise => { const db = new DbServer({ path: t.tmp }) - await db.start({ clean: true }) + await db.start({ clean: !opts.noClean }) if (opts.noBackup) { t.after(() => db.destroy()) } else { From 5c08c6d36c8199725a673bdd998b650d42d8e8a8 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 14:49:28 +0100 Subject: [PATCH 409/449] Test cleanup --- test/exists.ts | 42 +++++++++++++++--------------------------- test/expire.ts | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/test/exists.ts b/test/exists.ts index 97244b8557..4ca2e1fe91 100644 --- a/test/exists.ts +++ b/test/exists.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('exists', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -35,21 +29,21 @@ await test('exists', async (t) => { const id2 = await db.create('user', {}) - deepEqual(await db.query('user').filter('name', 'exists').get(), [ + deepEqual(await db.query2('user').filter('name', 'exists').get(), [ { id: 1, name: 'mr derp', }, ]) - deepEqual(await db.query('user').filter('name', '!exists').get(), [ + deepEqual(await db.query2('user').filter('name', '!exists').get(), [ { id: 2, name: '', }, ]) - deepEqual(await db.query('user').filter('friend', '!exists').get(), [ + deepEqual(await db.query2('user').filter('friend', '!exists').get(), [ { id: 1, name: 'mr derp', @@ -60,7 +54,7 @@ await test('exists', async (t) => { }, ]) - deepEqual(await db.query('user').filter('friends', '!exists').get(), [ + deepEqual(await db.query2('user').filter('friends', '!exists').get(), [ { id: 1, name: 'mr derp', @@ -73,7 +67,7 @@ await test('exists', async (t) => { await db.update('user', id1, { friends: [id2] }) - deepEqual(await db.query('user').filter('friends', 'exists').get(), [ + deepEqual(await db.query2('user').filter('friends', 'exists').get(), [ { id: 1, name: 'mr derp', @@ -86,7 +80,7 @@ await test('exists', async (t) => { await db.update('user', id1, { friends: null }) - deepEqual(await db.query('user').filter('friends', 'exists').get(), []) + deepEqual(await db.query2('user').filter('friends', 'exists').get(), []) const friends: any[] = [] for (let i = 0; i < 1e6; i++) { @@ -97,19 +91,13 @@ await test('exists', async (t) => { await db.update('user', id1, { friends }) - deepEqual(await db.query('user').filter('friends', '!exists').get(), [ + deepEqual(await db.query2('user').filter('friends', '!exists').get(), [ { id: 2, name: '' }, ]) }) await test('with other filters', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -131,7 +119,7 @@ await test('with other filters', async (t) => { name: 'dude', start: Date.now() + 10000, }) - const id2 = await db.create('user', { + await db.create('user', { name: 'cool guy has friends', friends: [id1], }) @@ -142,7 +130,7 @@ await test('with other filters', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('name') .filter('start', '>', 'now') .filter('derp', 'exists') @@ -153,7 +141,7 @@ await test('with other filters', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('name') .filter('name', '!exists') .filter('start', '>', 'now') @@ -163,8 +151,8 @@ await test('with other filters', async (t) => { ) deepEqual( - await db.query('user').include('name').filter('friends', '!exists').get(), - [{ id: 3, name: 'sad guy has no friends' }], + await db.query2('user').include('name').filter('friends', '!exists').get(), + [{ id: id3, name: 'sad guy has no friends' }], '!exists refs', ) }) diff --git a/test/expire.ts b/test/expire.ts index 10ed4a8ea5..52b0a6c519 100644 --- a/test/expire.ts +++ b/test/expire.ts @@ -1,4 +1,4 @@ -import { BasedDb } from '../src/index.js' +import { BasedDb, DbClient, getDefaultHooks } from '../src/index.js' import { equal } from './shared/assert.js' import { deepEqual } from '../src/utils/index.js' import test from './shared/test.js' @@ -11,7 +11,7 @@ await test('expire', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const schema = { types: { token: { name: 'string', @@ -28,29 +28,30 @@ await test('expire', async (t) => { }, }, }, - }) + } as const + const client = await db.setSchema(schema) - const user1 = await db.create('user') - const token1 = await db.create('token', { + const user1 = await client.create('user', {}) + const token1 = await client.create('token', { name: 'my token', user: user1, }) - db.expire('token', token1, 1) - await db.drain() - equal((await db.query('token').get().toObject()).length, 1) + client.expire('token', token1, 1) + await client.drain() + equal((await client.query2('token').get()).length, 1) await setTimeout(2e3) - equal((await db.query('token').get().toObject()).length, 0) + equal((await client.query2('token').get()).length, 0) - const token2 = await db.create('token', { + const token2 = await client.create('token', { name: 'my new token', user: user1, }) - await db.expire('token', token2, 1) - await db.drain() + await client.expire('token', token2, 1) + await client.drain() await db.save() equal( - (await db.query('token').get().toObject()).length, + (await client.query2('token').get()).length, 1, '1 token before save', ) @@ -59,15 +60,18 @@ await test('expire', async (t) => { }) t.after(() => db2.destroy(), true) await db2.start() + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) equal( - (await db2.query('token').get().toObject()).length, + (await client2.query2('token').get()).length, 1, '1 token after load', ) await setTimeout(3e3) equal( - (await db2.query('token').get().toObject()).length, + (await client2.query2('token').get()).length, 0, '0 tokens after expiry', ) @@ -80,7 +84,7 @@ await test('refresh', async (t) => { await db.start({ clean: true }) t.after(() => t.backup(db)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -93,10 +97,10 @@ await test('refresh', async (t) => { const id1 = await db.create('user', { name: 'dude', }) - await db.expire('user', id1, 1) - await db.drain() - await db.expire('user', id1, 3) + await client.expire('user', id1, 1) + await client.drain() + await client.expire('user', id1, 3) await db.drain() await setTimeout(1100) - deepEqual(await db.query('user', id1).get(), { id: 1, name: 'dude' }) + deepEqual(await client.query2('user', id1).get(), { id: 1, name: 'dude' }) }) From cbdd4bf1cdb3a1548c023e9333954bd4744f9d3a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 14:59:21 +0100 Subject: [PATCH 410/449] Test cleanup --- test/insertOnly.ts | 10 ++-------- test/number.ts | 22 ++++++++-------------- test/timestamp.ts | 45 ++++++++++++++------------------------------- 3 files changed, 24 insertions(+), 53 deletions(-) diff --git a/test/insertOnly.ts b/test/insertOnly.ts index 4f9322d8b3..fab28dc6b4 100644 --- a/test/insertOnly.ts +++ b/test/insertOnly.ts @@ -1,16 +1,10 @@ import { BasedDb } from '../src/index.js' import { deepEqual, throws } from './shared/assert.js' +import { testDb } from './shared/index.js' import test from './shared/test.js' await test('insert only => no delete', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(async () => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { audit: { insertOnly: true, diff --git a/test/number.ts b/test/number.ts index 1195c97204..b323d9897e 100644 --- a/test/number.ts +++ b/test/number.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' +import { testDb } from './shared/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' await test('basic', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -70,7 +64,7 @@ await test('basic', async (t) => { await db.drain() // will become async deepEqual( - await db.query('user').get(), + await db.query2('user').get(), payloads.map((payload, index) => { return { id: index + 1, @@ -102,7 +96,7 @@ await test('basic', async (t) => { }, }) - deepEqual(await db.query('user', newThing).get(), { + deepEqual(await db.query2('user', newThing).get(), { id: newThing, number: 12, int8: 12, @@ -137,7 +131,7 @@ await test('basic', async (t) => { }, }) - deepEqual(await db.query('user', newThing).get(), { + deepEqual(await db.query2('user', newThing).get(), { id: newThing, number: 13, int8: 14, @@ -154,7 +148,7 @@ await test('basic', async (t) => { }, }) - deepEqual(await db.query('user', newThing).get(), { + deepEqual(await db.query2('user', newThing).get(), { id: newThing, number: 13, int8: 14, @@ -171,7 +165,7 @@ await test('basic', async (t) => { }, }) - deepEqual(await db.query('user', newThing).get(), { + deepEqual(await db.query2('user', newThing).get(), { id: newThing, number: 13, int8: 14, @@ -186,7 +180,7 @@ await test('basic', async (t) => { uint16: 100, }) - deepEqual(await db.query('user', newThing).get(), { + deepEqual(await db.query2('user', newThing).get(), { id: newThing, number: 13, int8: 14, diff --git a/test/timestamp.ts b/test/timestamp.ts index 54a17ffafa..0835f9dc84 100644 --- a/test/timestamp.ts +++ b/test/timestamp.ts @@ -1,18 +1,10 @@ import { wait } from '../src/utils/index.js' -import { BasedDb } from '../src/index.js' import { deepEqual, equal } from './shared/assert.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' await test('timestamp', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -36,7 +28,7 @@ await test('timestamp', async (t) => { name: 'youzi', }) - let res = (await db.query('user').get()).toObject() + let res = await db.query2('user').get() if (typeof res[0].createdAt !== 'number') { throw 'should be number' @@ -55,7 +47,7 @@ await test('timestamp', async (t) => { name: 'youzi1', }) - res = (await db.query('user').get()).toObject() + res = await db.query2('user').get() if (!(res[0].updatedAt > res[0].createdAt)) { throw 'updatedAt should be updated after update' @@ -63,7 +55,7 @@ await test('timestamp', async (t) => { const measure = async (v: number) => { deepEqual( - Math.floor((await db.query('user', youzi).get().toObject()).mrDerp / 10), + Math.floor(((await db.query2('user', youzi).get())?.mrDerp ?? 0) / 10), Math.floor(v / 10), ) } @@ -102,25 +94,17 @@ await test('timestamp', async (t) => { updatedAt: overwriteUpdatedAt, }) - const newUser = await db.query('user', jamex).get().toObject() - const updatedUser = await db.query('user', youzi).get().toObject() + const newUser = await db.query2('user', jamex).get() + const updatedUser = await db.query2('user', youzi).get() - equal(newUser.createdAt, overwriteCreatedAt) - equal(newUser.updatedAt, overwriteUpdatedAt) - equal(updatedUser.createdAt, overwriteCreatedAt) - equal(updatedUser.updatedAt, overwriteUpdatedAt) + equal(newUser?.createdAt, overwriteCreatedAt) + equal(newUser?.updatedAt, overwriteUpdatedAt) + equal(updatedUser?.createdAt, overwriteCreatedAt) + equal(updatedUser?.updatedAt, overwriteUpdatedAt) }) await test('timestamp before 1970', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -137,7 +121,6 @@ await test('timestamp before 1970', async (t) => { bday: d, }) - const res = await db.query('user', user).get().toObject() - - equal(res.bday, d.valueOf()) + const res = await db.query2('user', user).get() + equal(res?.bday, d.valueOf()) }) From ce68b300958c14530c4597b7d2edee20644baa71 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 14:59:51 +0100 Subject: [PATCH 411/449] prettier --- test/default.ts | 2 +- test/expire.ts | 12 ++---------- test/flush.ts | 4 +++- test/locales.ts | 4 +++- test/raw.ts | 2 +- test/stringUtils.perf.ts | 32 ++++++++++++++++++++------------ test/vector.ts | 6 ++++-- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/test/default.ts b/test/default.ts index 6f167d04ec..eb7006e3c3 100644 --- a/test/default.ts +++ b/test/default.ts @@ -330,7 +330,7 @@ await test('default values for all props in user type', async (t) => { }) await test('negative default values for numeric types', async (t) => { - const db =await testDb(t, { + const db = await testDb(t, { types: { user: { props: { diff --git a/test/expire.ts b/test/expire.ts index 52b0a6c519..010ca0715e 100644 --- a/test/expire.ts +++ b/test/expire.ts @@ -50,11 +50,7 @@ await test('expire', async (t) => { await client.expire('token', token2, 1) await client.drain() await db.save() - equal( - (await client.query2('token').get()).length, - 1, - '1 token before save', - ) + equal((await client.query2('token').get()).length, 1, '1 token before save') const db2 = new BasedDb({ path: t.tmp, }) @@ -64,11 +60,7 @@ await test('expire', async (t) => { hooks: getDefaultHooks(db2.server), }) - equal( - (await client2.query2('token').get()).length, - 1, - '1 token after load', - ) + equal((await client2.query2('token').get()).length, 1, '1 token after load') await setTimeout(3e3) equal( (await client2.query2('token').get()).length, diff --git a/test/flush.ts b/test/flush.ts index a1ee58722b..f605a53487 100644 --- a/test/flush.ts +++ b/test/flush.ts @@ -22,7 +22,9 @@ await test('too large payload should throw, correct size should not', async (t) let error: Error | null = null try { client.create('user', { - name: 'cool string but too long for the max size unfortunately wow what the hell'.repeat(4), + name: 'cool string but too long for the max size unfortunately wow what the hell'.repeat( + 4, + ), }) } catch (e) { error = e diff --git a/test/locales.ts b/test/locales.ts index bded1b9f6f..4829625959 100644 --- a/test/locales.ts +++ b/test/locales.ts @@ -3,7 +3,9 @@ import test from './shared/test.js' import { LangCode } from '../src/zigTsExports.js' const langs = [...Object.keys(LangCode)].filter((val) => val !== 'none') -const locales = Object.fromEntries(langs.map((l: keyof typeof LangCode) => [l, {}])) +const locales = Object.fromEntries( + langs.map((l: keyof typeof LangCode) => [l, {}]), +) await test('locales', async (t) => { const db = new BasedDb({ diff --git a/test/raw.ts b/test/raw.ts index 86ffbc2939..58cfe1d1ad 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -1,6 +1,6 @@ import { BasedDb } from '../src/index.js' import { deepEqual } from './shared/assert.js' -import {italy} from './shared/examples.js' +import { italy } from './shared/examples.js' import test from './shared/test.js' await test.skip('cardinality', async (t) => { diff --git a/test/stringUtils.perf.ts b/test/stringUtils.perf.ts index 1a6dfaa220..75c42ea069 100644 --- a/test/stringUtils.perf.ts +++ b/test/stringUtils.perf.ts @@ -1,36 +1,44 @@ import test from './shared/test.js' import { perf } from './shared/assert.js' -import { fastPrng } from '../src/utils/fastPrng.js'; -import { DECODER, ENCODER } from '../src/utils/uint8.js'; +import { fastPrng } from '../src/utils/fastPrng.js' +import { DECODER, ENCODER } from '../src/utils/uint8.js' import native from '../src/native.js' -const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' let rnd: ReturnType function createRandomString(length: number): string { const result = new Uint8Array(length) for (let i = 0; i < length; i++) { - result[i] = chars.charCodeAt(rnd(0, chars.length - 1)); + result[i] = chars.charCodeAt(rnd(0, chars.length - 1)) } return DECODER.decode(result) } -let i = 0; -let j = 0; +let i = 0 +let j = 0 await test('string len', async (t) => { const opts = { repeat: 100_000 } as const for (const len of [10, 100, 512, 1024]) { rnd = fastPrng() - await perf(async () => { - i += ENCODER.encode(createRandomString(1024)).byteLength - }, `ENCODER ${len}`, opts) + await perf( + async () => { + i += ENCODER.encode(createRandomString(1024)).byteLength + }, + `ENCODER ${len}`, + opts, + ) rnd = fastPrng() - await perf(async () => { - j += native.stringByteLength(createRandomString(1024)) - }, `stringByteLength ${len}`, opts) + await perf( + async () => { + j += native.stringByteLength(createRandomString(1024)) + }, + `stringByteLength ${len}`, + opts, + ) console.log(i, j) } }) diff --git a/test/vector.ts b/test/vector.ts index f1c614e736..328bf00068 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -2,7 +2,7 @@ import { DbClient } from '../src/index.js' import test from './shared/test.js' import { deepEqual, equal } from './shared/assert.js' import { equals } from '../src/utils/index.js' -import {testDb} from './shared/index.js' +import { testDb } from './shared/index.js' const data = { cat: [1.5, -0.4, 7.2, 19.6, 20.2], @@ -13,7 +13,9 @@ const data = { car: [81.6, -72.1, 16, -20.2, 102], } -async function initDb(t: Parameters[1]>[0]): Promise { +async function initDb( + t: Parameters[1]>[0], +): Promise { const client = await testDb(t, { types: { data: { From d2836e4b114600a34e8c23e83d92d11db8d7c105 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 15:05:36 +0100 Subject: [PATCH 412/449] Test cleanup --- test/upsert.ts | 70 ++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/test/upsert.ts b/test/upsert.ts index 7e88c9c2b6..b5bced3cb1 100644 --- a/test/upsert.ts +++ b/test/upsert.ts @@ -1,7 +1,7 @@ import { deepEqual, equal } from './shared/assert.js' import test from './shared/test.js' import { start } from './shared/multi.js' -import { BasedDb } from '../src/index.js' +import { testDb } from './shared/index.js' await test('upsert', async (t) => { const { @@ -55,22 +55,35 @@ await test('upsert', async (t) => { }) } - await client2.upsert('article', { - externalId: 'flap', - name: 'flap', - contributors: [ - client2.upsert('user', { - email: 'james@flapmail.com', - name: 'James!', - }), - client2.upsert('user', { - email: 'derp@flapmail.com', - name: 'Derp!', - }), - ], - }) + await client2.upsert( + 'article', + { externalId: 'flap' }, + { + name: 'flap', + contributors: [ + client2.upsert( + 'user', + { + email: 'james@flapmail.com', + }, + { + name: 'James!', + }, + ), + client2.upsert( + 'user', + { + email: 'derp@flapmail.com', + }, + { + name: 'Derp!', + }, + ), + ], + }, + ) - deepEqual(await client1.query('article').include('*', '**').get(), [ + deepEqual(await client1.query2('article').include('*', '**').get(), [ { id: 1, externalId: 'flap', @@ -84,14 +97,7 @@ await test('upsert', async (t) => { }) await test('upsert no alias', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start() - t.after(() => db.destroy()) - // t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { lala: { props: { @@ -102,24 +108,14 @@ await test('upsert no alias', async (t) => { }, }) - // await db.drain() - - equal( - (await db.query('lala').include('*').get().toObject()).length, - 0, - 'before upsert', - ) + equal((await db.query2('lala').include('*').get()).length, 0, 'before upsert') await db.upsert('lala', { lele: 'lulu', lili: 813, }) - equal( - (await db.query('lala').include('*').get().toObject()).length, - 1, - 'after upsert', - ) + equal((await db.query2('lala').include('*').get()).length, 1, 'after upsert') await db.upsert('lala', { lele: 'lulu', @@ -127,7 +123,7 @@ await test('upsert no alias', async (t) => { }) equal( - (await db.query('lala').include('*').get().toObject()).length, + (await db.query2('lala').include('*').get()).length, 2, 'upsert no alias should insert', ) From 998c7951178d9097f06d063ec434457c8dca795e Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 15:11:47 +0100 Subject: [PATCH 413/449] Test cleanup --- test/update.ts | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/test/update.ts b/test/update.ts index 10d9216e8d..b8067a312b 100644 --- a/test/update.ts +++ b/test/update.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' -import { deepEqual, equal, throws, perf } from './shared/assert.js' +import { deepEqual, equal, throws } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('update with payload.id', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { body: 'string', @@ -17,13 +11,12 @@ await test('update with payload.id', async (t) => { }, }) - const article1 = await db.create('article') - await db.update('article', { - id: article1, + const article1 = await db.create('article', {}) + await db.update('article', article1, { body: 'xxx', }) - deepEqual(await db.query('article').get(), [ + deepEqual(await db.query2('article').get(), [ { id: 1, body: 'xxx', @@ -32,13 +25,7 @@ await test('update with payload.id', async (t) => { }) await test('update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { mep: { props: { @@ -82,7 +69,7 @@ await test('update', async (t) => { await db.drain() - deepEqual(await db.query('snurp').get(), [ + deepEqual(await db.query2('snurp').get(), [ { a: 1, b: 2, @@ -127,7 +114,7 @@ await test('update', async (t) => { await db.drain() - deepEqual(await db.query('snurp').get(), [ + deepEqual(await db.query2('snurp').get(), [ { a: 1, b: 2, @@ -156,7 +143,7 @@ await test('update', async (t) => { await db.drain() - deepEqual(await db.query('snurp', 2).get(), { + deepEqual(await db.query2('snurp', 2).get(), { a: 0, b: 0, c: 0, @@ -170,7 +157,7 @@ await test('update', async (t) => { }) // for individual queries combine them - deepEqual(await db.query('snurp', [2, 1]).get(), [ + deepEqual(await db.query2('snurp', [2, 1]).get(), [ { a: 1, b: 2, @@ -212,15 +199,15 @@ await test('update', async (t) => { await db.drain() - equal((await db.query('snurp', ids).get()).length, 1e6) + equal((await db.query2('snurp', ids).get())?.length, 1e6) - equal((await db.query('snurp', ids).range(0, 100).get()).length, 100) + equal((await db.query2('snurp', ids).range(0, 100).get()).length, 100) - equal((await db.query('snurp', ids).range(10, 110).get()).length, 100) + equal((await db.query2('snurp', ids).range(10, 110).get()).length, 100) deepEqual( await db - .query('snurp', ids) + .query2('snurp', ids) .range(1e5, 1e5 + 2) .sort('a', 'desc') .get(), @@ -251,7 +238,7 @@ await test('update', async (t) => { const promises: any[] = [] for (var j = 0; j < 1; j++) { for (var i = 0; i < 1e5; i++) { - promises.push(db.query('snurp', i).include('a').get()) + promises.push(db.query2('snurp', i).include('a').get()) } } From 0f944eb50867b772751c12e3a20861331b652c70 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 15:21:45 +0100 Subject: [PATCH 414/449] Test cleanup --- test/text/text.ts | 170 ++++++++++++++++------------------------------ 1 file changed, 58 insertions(+), 112 deletions(-) diff --git a/test/text/text.ts b/test/text/text.ts index 29ce5e1bb2..671c123463 100644 --- a/test/text/text.ts +++ b/test/text/text.ts @@ -1,17 +1,11 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { italy } from '../shared/examples.js' import { deepEqual } from '../shared/assert.js' import { notEqual } from 'node:assert' +import { testDb } from '../shared/index.js' await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: { fallback: ['en'] }, @@ -35,7 +29,7 @@ await test('simple', async (t) => { await db.drain() deepEqual( - await db.query('dialog').include('id', 'fun').get(), + await db.query2('dialog').include('id', 'fun').get(), [ { id: dialogId, @@ -50,7 +44,7 @@ await test('simple', async (t) => { ) deepEqual( - await db.query('dialog').include('id').get(), + await db.query2('dialog').include('id').get(), [ { id: dialogId, @@ -60,7 +54,7 @@ await test('simple', async (t) => { ) deepEqual( - await db.query('dialog').locale('it').include('id', 'fun').get(), + await db.query2('dialog').locale('it').include('id', 'fun').get(), [ { id: dialogId, @@ -72,7 +66,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('it') .include('id', 'fun') .filter('fun', 'includes', 'fliperdieflaperdiefloep', { lowerCase: true }) @@ -83,7 +77,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('id', 'fun') .filter('fun', 'includes', 'italy', { lowerCase: true }) .get(), @@ -102,7 +96,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('it') .include('id', 'fun') .filter('fun', 'includes', 'italy', { lowerCase: true }) @@ -118,7 +112,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('id', 'fun') .filter('fun.en', 'includes', 'italy', { lowerCase: true }) .get(), @@ -128,7 +122,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('id', 'fun') .filter('fun.it', 'includes', 'italy', { lowerCase: true }) .get(), @@ -147,7 +141,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('en') .include('id', 'fun') .filter('fun.it', 'includes', 'italy', { lowerCase: true }) @@ -170,7 +164,7 @@ await test('simple', async (t) => { ) deepEqual( - await db.query('dialog').include('id', 'fun').locale('fi').get(), + await db.query2('dialog').include('id', 'fun').locale('fi').get(), [ { id: dialogId, @@ -194,7 +188,7 @@ await test('simple', async (t) => { ) deepEqual( - await db.query('dialog').include('id', 'fun').locale('fi').get(), + await db.query2('dialog').include('id', 'fun').locale('fi').get(), [ { id: dialogId, @@ -211,7 +205,7 @@ await test('simple', async (t) => { const derpderp = await db.create('dialog', {}) deepEqual( - await db.query('dialog', mrSnurfInFinland).get(), + await db.query2('dialog', mrSnurfInFinland).get(), { id: mrSnurfInFinland, fun: { @@ -224,7 +218,7 @@ await test('simple', async (t) => { ) deepEqual( - await db.query('dialog', derpderp).get(), + await db.query2('dialog', derpderp).get(), { id: derpderp, fun: { @@ -238,7 +232,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('fi') .include('id', 'fun') .filter('fun', '=', '3', { lowerCase: true }) @@ -254,7 +248,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('fi') .include('id', 'fun') .filter('fun', '=', 'mr snurf in finland!', { lowerCase: true }) @@ -274,7 +268,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('fun.en') .filter('fun', '=', 'mr snurf in finland!', { lowerCase: true }) .get(), @@ -289,13 +283,7 @@ await test('simple', async (t) => { }) await test('search', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, fi: { fallback: ['en'] }, @@ -323,7 +311,7 @@ await test('search', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('id', 'fun') .search('finland', 'fun') .get(), @@ -342,7 +330,7 @@ await test('search', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('id', 'fun') .search('kingdom', 'fun') .get(), @@ -360,7 +348,7 @@ await test('search', async (t) => { ) deepEqual( - await db.query('dialog').include('id', 'fun').search('snurp', 'fun').get(), + await db.query2('dialog').include('id', 'fun').search('snurp', 'fun').get(), [ { id: 2, @@ -375,7 +363,7 @@ await test('search', async (t) => { ) deepEqual( - await db.query('dialog').include('id', 'fun').search('derp', 'fun').get(), + await db.query2('dialog').include('id', 'fun').search('derp', 'fun').get(), [ { id: 2, @@ -391,7 +379,7 @@ await test('search', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('fi') .include('id', 'fun') .search('derp', 'fun') @@ -402,7 +390,7 @@ await test('search', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('en') .include('id', 'fun') .search('derp', 'fun') @@ -419,7 +407,7 @@ await test('search', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('id', 'fun') .search('derp', 'fun.en') .get(), @@ -438,13 +426,7 @@ await test('search', async (t) => { }) await test('reference text', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: { required: true }, fr: { required: true }, @@ -461,14 +443,14 @@ await test('reference text', async (t) => { }, }) - const country1 = await db.create('country') + const country1 = await db.create('country', {}) await db.create('contestant', { name: 'New contestant', country: country1, }) - deepEqual(await db.query('country').include('*').get(), [ + deepEqual(await db.query2('country').include('*').get(), [ { id: 1, name: '', @@ -479,7 +461,7 @@ await test('reference text', async (t) => { }, ]) - deepEqual(await db.query('contestant').include('*', 'country').get(), [ + deepEqual(await db.query2('contestant').include('*', 'country').get(), [ { id: 1, name: 'New contestant', @@ -496,13 +478,7 @@ await test('reference text', async (t) => { }) await test('sort', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: { fallback: ['en'] }, @@ -518,7 +494,7 @@ await test('sort', async (t) => { }, }) - await db.query('dialog').locale('fi').sort('fun', 'desc').get() + await db.query2('dialog').locale('fi').sort('fun', 'desc').get() const id1 = await db.create('dialog', { fun: { en: '3 en', fi: '1' }, @@ -544,7 +520,7 @@ await test('sort', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .include('fun') .locale('fi') .sort('fun', 'desc') @@ -575,7 +551,7 @@ await test('sort', async (t) => { ) deepEqual( - await db.query('dialog').include('fun').sort('fun.fi', 'desc').get(), + await db.query2('dialog').include('fun').sort('fun.fi', 'desc').get(), [ { id: 3, fun: { en: '1 en', fi: '3', it: '' } }, { id: 2, fun: { en: '2 en', fi: '2', it: '' } }, @@ -587,7 +563,7 @@ await test('sort', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('en') .include('fun') .sort('fun', 'desc') @@ -607,7 +583,7 @@ await test('sort', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('fi') .include('fun') .sort('fun', 'desc') @@ -640,7 +616,7 @@ await test('sort', async (t) => { deepEqual( await db - .query('dialog') + .query2('dialog') .locale('fi') .include('fun') .sort('fun', 'desc') @@ -665,7 +641,7 @@ await test('sort', async (t) => { ], ) - deepEqual(await db.query('dialog').locale('fi').sort('snurf', 'desc').get(), [ + deepEqual(await db.query2('dialog').locale('fi').sort('snurf', 'desc').get(), [ { id: 3, fun: '3', snurf: '3' }, { id: 2, fun: '2', snurf: '2' }, { id: 1, fun: '1', snurf: '1' }, @@ -684,14 +660,14 @@ await test('sort', async (t) => { await db.drain() - deepEqual(await db.query('dialog').locale('fi').sort('snurf', 'desc').get(), [ + deepEqual(await db.query2('dialog').locale('fi').sort('snurf', 'desc').get(), [ { id: 3, fun: '3', snurf: '3' }, { id: 2, fun: '2', snurf: '2' }, { id: 1, fun: '', snurf: '' }, { id: 4, snurf: '', fun: '' }, ]) - deepEqual(await db.query('dialog').locale('fi').sort('fun').get(), [ + deepEqual(await db.query2('dialog').locale('fi').sort('fun').get(), [ { id: 4, snurf: '', fun: '' }, { id: 1, fun: '', snurf: '' }, { id: 2, fun: '2', snurf: '2' }, @@ -704,7 +680,7 @@ await test('sort', async (t) => { await db.drain() - deepEqual(await db.query('dialog').locale('fi').sort('fun').get(), [ + deepEqual(await db.query2('dialog').locale('fi').sort('fun').get(), [ { id: 4, snurf: '', fun: '' }, { id: 3, snurf: '3', fun: '' }, { id: 1, snurf: '', fun: '' }, @@ -721,7 +697,7 @@ await test('sort', async (t) => { ) await db.drain() - deepEqual(await db.query('dialog').locale('fi').sort('fun').get(), [ + deepEqual(await db.query2('dialog').locale('fi').sort('fun').get(), [ { id: 4, snurf: '', fun: '' }, { id: 1, snurf: '', fun: '' }, { id: 3, snurf: '3', fun: '0' }, @@ -739,7 +715,7 @@ await test('sort', async (t) => { await db.drain() deepEqual( - await db.query('dialog').locale('fi').sort('fun').get(), + await db.query2('dialog').locale('fi').sort('fun').get(), [ { id: 4, snurf: '', fun: '' }, { id: 3, snurf: '3', fun: '' }, @@ -757,7 +733,7 @@ await test('sort', async (t) => { await db.drain() deepEqual( - await db.query('dialog').locale('fi').sort('fun').get(), + await db.query2('dialog').locale('fi').sort('fun').get(), [ { id: 4, snurf: '', fun: '' }, { id: 1, snurf: '', fun: '' }, @@ -782,7 +758,7 @@ await test('sort', async (t) => { await db.drain() deepEqual( - await db.query('dialog').locale('fi').sort('fun').get(), + await db.query2('dialog').locale('fi').sort('fun').get(), [ { id: 4, snurf: '', fun: '' }, { id: 3, snurf: '3', fun: '' }, @@ -794,13 +770,7 @@ await test('sort', async (t) => { }) await test('in object only', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, @@ -825,20 +795,14 @@ await test('in object only', async (t) => { }, }) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, dict: { nice: { en: 'a', it: '' } }, }) }) await test('correct return from obj', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, @@ -864,7 +828,7 @@ await test('correct return from obj', async (t) => { }, }) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, dict: { nice: { en: 'cool guy', it: '' } }, name: { en: '', it: '' }, @@ -872,13 +836,7 @@ await test('correct return from obj', async (t) => { }) await test('clear field', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, @@ -894,7 +852,7 @@ await test('clear field', async (t) => { name: { en: 'coolnameEN', it: 'coolnameIT' }, }) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, name: { en: 'coolnameEN', it: 'coolnameIT' }, }) @@ -908,20 +866,14 @@ await test('clear field', async (t) => { { locale: 'en' }, ) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, name: { en: '', it: 'coolnameIT' }, }) }) await test('text and compression', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, @@ -937,7 +889,7 @@ await test('text and compression', async (t) => { article: { en: italy, it: 'cool' }, }) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, article: { en: italy, it: 'cool' }, }) @@ -951,20 +903,14 @@ await test('text and compression', async (t) => { { locale: 'en' }, ) - deepEqual(await db.query('user', user1).get(), { + deepEqual(await db.query2('user', user1).get(), { id: 1, article: { en: '', it: 'cool' }, }) }) await test('text and crc32', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, @@ -983,7 +929,7 @@ await test('text and crc32', async (t) => { }, }) - const checksum = (await db.query('user', user1).get()).checksum + const checksum = (await db.query2('user', user1).get()).checksum await db.update('user', user1, { article: { @@ -992,7 +938,7 @@ await test('text and crc32', async (t) => { }, }) - const checksum2 = (await db.query('user', user1).get()).checksum + const checksum2 = (await db.query2('user', user1).get()).checksum notEqual(checksum, checksum2, 'Checksum is not the same') }) From 0000e47b5874175d5bcb26ce0ef405438174d5da Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 15:22:52 +0100 Subject: [PATCH 415/449] Test cleanup --- test/text/textMany.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/text/textMany.ts b/test/text/textMany.ts index 4efa76a68b..fbbb1b08c2 100644 --- a/test/text/textMany.ts +++ b/test/text/textMany.ts @@ -1,6 +1,6 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { fastPrng } from '../../src/utils/fastPrng.js' +import { testDb } from '../shared/index.js' const N_PROPS = 248 @@ -53,13 +53,7 @@ function setTextProps(): { } await test('Many text props', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: makeMaxSchema(), types: { dialog: { From bb70f6eed666d4f94488dd31c8a20b345d99eff1 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 15:30:41 +0100 Subject: [PATCH 416/449] wip --- src/db-client/query2/index.ts | 15 ++- src/db-client/query2/types.ts | 24 ++--- test/HLLunion.ts | 2 +- test/aggregate/deep.ts | 6 +- test/aggregate/dev.ts | 4 +- test/aggregate/experimental.ts | 2 +- test/alignModify.ts | 2 +- test/bigNode.perf.ts | 8 +- test/binary.ts | 2 +- test/cardinality.ts | 20 ++-- test/clientServer.perf.ts | 2 +- test/copy.ts | 2 +- test/crc32c.ts | 4 +- test/db-schema/schemaChange.ts | 28 +++--- test/db-schema/schemaDebug.ts | 5 +- test/db-schema/schemaProblems.ts | 6 +- test/db-schema/schemaUpdates.ts | 94 +++++-------------- test/edges/edgeFilterNested.ts | 30 +++--- test/edges/edgeNumbers.ts | 12 +-- test/edges/edgeText.ts | 18 ++-- test/edges/edgeType.ts | 32 +++---- test/edges/edges.ts | 62 +++++------- test/edges/edgesMain.ts | 64 ++++--------- test/edges/edgesReference.ts | 29 ++---- test/edges/edgesReferences.ts | 92 +++++++----------- test/expire.ts | 10 +- test/filter/filter.ts | 66 ++++++------- test/filter/string.ts | 6 +- test/hooks.ts | 14 +-- test/idOffset.ts | 2 +- test/instantModify.ts | 4 +- test/isModified.perf.ts | 2 +- test/locales.ts | 4 +- test/mainAndEmptyStringFieldDelete.ts | 2 +- test/migration.ts | 6 +- test/modify/insert.ts | 8 +- test/modify/upsert.ts | 8 +- test/query-ast/aggregates.ts | 2 +- test/query/ast.ts | 87 +++++++++++++++-- test/query/db.ts | 53 ++++++----- test/range.ts | 2 +- test/raw.ts | 8 +- test/references/references.ts | 14 +-- test/references/referencesIndex.ts | 16 ++-- test/references/referencesModify.ts | 12 +-- test/save/save.ts | 12 +-- test/save/saveInterval.ts | 4 +- test/scenarios/northwind.ts | 18 ++-- test/scenarios/nycTaxi.ts | 14 +-- test/scenarios/vote.ts | 4 +- test/scenarios/voteLargeAmounts.perf.ts | 2 +- test/scenarios/voteStorage.ts | 4 +- test/serializeQueryDef.ts | 8 +- test/shared/assert.ts | 4 +- test/shared/index.ts | 2 +- test/shared/test.ts | 14 ++- test/simpleQuery.ts | 8 +- test/singleRef.ts | 38 ++++---- test/singleRefQuery.ts | 4 +- test/sort/sort.ts | 2 +- test/sort/sortHll.ts | 14 +-- test/sort/sortNodeId.ts | 2 +- test/sort/sortNumber.ts | 4 +- test/sort/sortString.ts | 4 +- test/string.ts | 2 +- test/subscription/subscription.ts | 2 +- .../subscription/subscriptionSchemaChanges.ts | 6 +- test/timestamp.ts | 12 +-- test/upsert.ts | 6 +- test/vector.ts | 10 +- 70 files changed, 510 insertions(+), 581 deletions(-) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index f93ae06e29..fc7f70abbf 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -1,4 +1,4 @@ -import type { FilterAst, FilterLeaf, QueryAst } from '../../db-query/ast/ast.js' +import type { FilterAst, QueryAst } from '../../db-query/ast/ast.js' import type { PickOutput, ResolveInclude, @@ -13,7 +13,7 @@ import type { ExpandDotPath, UnionToIntersection, } from './types.js' -import type { ResolvedProps, SchemaOut } from '../../schema/index.js' +import type { ResolvedProps } from '../../schema/index.js' import { astToQueryCtx } from '../../db-query/ast/toCtx.js' import { AutoSizedUint8Array } from '../../utils/AutoSizedUint8Array.js' import type { DbClient } from '../../sdk.js' @@ -98,7 +98,7 @@ class Query< >( prop: P, op: Operator, - val: InferPathType, + val: InferPathType, opts?: FilterOpts, ): FilterBranch filter(prop: any, op?: any, val?: any, opts?: any): FilterBranch { @@ -118,7 +118,7 @@ class Query< >( prop: P, op: Operator, - val: InferPathType, + val: InferPathType, opts?: FilterOpts, ): FilterBranch and(prop: any, op?: any, val?: any, opts?: any): FilterBranch { @@ -137,7 +137,7 @@ class Query< >( prop: P, op: Operator, - val: InferPathType, + val: InferPathType, opts?: FilterOpts, ): FilterBranch or(prop: any, op?: any, val?: any, opts?: any): FilterBranch { @@ -644,8 +644,7 @@ class Query< } } -type FilterBranch = Omit & - FilterMethods +type FilterBranch = T type FilterMethods = { and: T['filter'] @@ -774,7 +773,7 @@ type FilterSignature< >( prop: P, op: Operator, - val: InferPathType, + val: InferPathType, opts?: FilterOpts, ): Result } diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 9ce575cb89..17a4f6178f 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -62,12 +62,12 @@ export type PickOutputFromProps< > : never } & { - [Item in Extract as Item['field'] & + [Field in Extract['field'] & keyof Props]: InferProp< - Props[Item['field'] & keyof Props], + Props[Field], S['types'], S['locales'] extends Record ? S['locales'] : {}, - Item['select'] + Extract['select'] > } > @@ -80,7 +80,7 @@ export type InferProp< > = IsSelected extends false ? InferPropLogic - : Selection extends { _aggregate: infer Agg } + : [Selection] extends [{ _aggregate: infer Agg }] ? Agg : InferPropLogic @@ -136,11 +136,11 @@ type InferType< } // Helpers for include -type IsRefProp

= P extends { type: 'reference' } | { type: 'references' } +type IsRefProp

= [P] extends [{ type: 'reference' } | { type: 'references' }] ? true - : P extends { ref: any } + : [P] extends [{ ref: any }] ? true - : P extends { items: { ref: any } } + : [P] extends [{ items: { ref: any } }] ? true : false @@ -185,13 +185,12 @@ export type PickOutput< : InferSchemaOutput[P] : InferSchemaOutput[P] } & { - [Item in Extract as Item['field'] & + [Field in Extract['field'] & keyof ResolvedProps]: InferProp< - ResolvedProps[Item['field'] & - keyof ResolvedProps], + ResolvedProps[Field], S['types'], S['locales'] extends Record ? S['locales'] : {}, - Item['select'] + Extract['select'] > } > @@ -316,7 +315,8 @@ export type InferPathType< S extends { types: any; locales?: any }, T extends keyof S['types'], P, -> = InferPropsPathType, P> + EdgeProps extends Record = {}, +> = InferPropsPathType & EdgeProps, P> export type NumberPaths< S extends { types: any; locales?: any }, diff --git a/test/HLLunion.ts b/test/HLLunion.ts index 0cca03bbc7..04f3b72b5f 100644 --- a/test/HLLunion.ts +++ b/test/HLLunion.ts @@ -204,7 +204,7 @@ await test.skip('dev', async (t) => { // // .query('article') // // .include((q) => q('contributors').sum('flap'), 'name') // // .get() -// // .toObject(), +// // , // // [ // // { id: 1, name: 'The wonders of Strudel', contributors: { flap: 100 } }, // // { diff --git a/test/aggregate/deep.ts b/test/aggregate/deep.ts index 5b308a11c8..b9b899d16e 100644 --- a/test/aggregate/deep.ts +++ b/test/aggregate/deep.ts @@ -837,7 +837,7 @@ await test.skip('edges aggregation', async (t) => { // .query2('movie') // .include((q) => q('actors').max('$rating')) // .get() - // .toObject(), + // , // [ // { // id: 1, @@ -864,7 +864,7 @@ await test.skip('edges aggregation', async (t) => { // .query2('movie') // .include((q) => q('actors').max('$rating').sum('$hating')) // .get() - // .toObject(), + // , // [ // { // id: 1, @@ -897,7 +897,7 @@ await test.skip('edges aggregation', async (t) => { // .query2('movie') // .include((q) => q('actors').max('$rating', '$hating')) // .get() - // .toObject(), + // , // [ // { // id: 1, diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index c2db462c23..1e6fd02789 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -124,7 +124,7 @@ import { testDb } from '../shared/index.js' // ) // .get() -// // console.log(lala.toObject()) +// // console.log(lala) // lala.inspect(10) // }) @@ -231,7 +231,7 @@ await test('yyy', async (t) => { // result.inspect() // deepEqual( - // result.toObject(), + // result, // [ // { // id: 1, diff --git a/test/aggregate/experimental.ts b/test/aggregate/experimental.ts index 01a3d0827f..cc5670401b 100644 --- a/test/aggregate/experimental.ts +++ b/test/aggregate/experimental.ts @@ -73,7 +73,7 @@ await test('dev', async (t) => { // console.log( // 'Total meals from query: ', - // Object.entries(eaters.toObject()[0]) + // Object.entries(eaters[0]) // .filter(([key]) => days.includes(key)) // .reduce((sum, el: [string, number]) => sum + el[1], 0), // ) diff --git a/test/alignModify.ts b/test/alignModify.ts index c41c6dfaf6..a5f7dedb6f 100644 --- a/test/alignModify.ts +++ b/test/alignModify.ts @@ -42,7 +42,7 @@ await test('alignModify - putrefs', async (t) => { }) } await db.drain() - const res = await db.query('user').include('friends', 'str').get().toObject() + const res = await db.query('user').include('friends', 'str').get() deepEqual(res, [ { id: 1, diff --git a/test/bigNode.perf.ts b/test/bigNode.perf.ts index cbb7947d90..8a56f8b26b 100644 --- a/test/bigNode.perf.ts +++ b/test/bigNode.perf.ts @@ -54,8 +54,8 @@ await test('big nodes', async (t) => { }) await db.drain() - const mega = (await db.query('mega').get()).toObject() - const giga = (await db.query('giga').get()).toObject() + const mega = (await db.query('mega').get()) + const giga = (await db.query('giga').get()) deepEqual(mega[1].f4092, 1337) deepEqual(giga[1].f100, 1337) @@ -65,8 +65,8 @@ await test('big nodes', async (t) => { const megaRefQ = await db.query('mega').include('ref').get() - const megaRef = megaRefQ.toObject() - const gigaRef = (await db.query('giga').include('ref').get()).toObject() + const megaRef = megaRefQ + const gigaRef = (await db.query('giga').include('ref').get()) deepEqual(gigaRef[0].ref.id, 2) deepEqual(megaRef[1].ref.id, 1) diff --git a/test/binary.ts b/test/binary.ts index b6baddf487..059a6780ac 100644 --- a/test/binary.ts +++ b/test/binary.ts @@ -54,7 +54,7 @@ await test('simple', async (t) => { }) equal( - (await db.query('user', id2).get()).toObject().file.length, + (await db.query('user', id2).get()).file.length, italyBytes.byteLength, ) }) diff --git a/test/cardinality.ts b/test/cardinality.ts index fb4e14bc80..e55dc3bd62 100644 --- a/test/cardinality.ts +++ b/test/cardinality.ts @@ -51,7 +51,7 @@ await test('hll', async (t) => { .query('article') .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') .get() - ).toObject(), + ), [ { id: 1, @@ -69,7 +69,7 @@ await test('hll', async (t) => { .include('myUniqueValuesCount') .filter('myUniqueValuesCount', '!=', 0) .get() - ).toObject(), + ), [ { id: 1, @@ -106,7 +106,7 @@ await test('hll', async (t) => { .query('article') .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') .get() - ).toObject(), + ), [ { id: 1, myUniqueValuesCount: 1, myUniqueValuesCountFromArray: 0 }, { id: 2, myUniqueValuesCountFromArray: 7, myUniqueValuesCount: 0 }, @@ -120,7 +120,7 @@ await test('hll', async (t) => { .include('myUniqueValuesCountFromArray') .filter('myUniqueValuesCountFromArray', '=', 7) .get() - ).toObject(), + ), [ { id: 2, @@ -136,7 +136,7 @@ await test('hll', async (t) => { .include('myUniqueValuesCount') .filter('myUniqueValuesCount', '>', 1) .get() - ).toObject(), + ), [], ) @@ -169,7 +169,7 @@ await test('hll', async (t) => { .query('article') .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') .get() - ).toObject(), + ), [ { id: 1, myUniqueValuesCount: 7, myUniqueValuesCountFromArray: 0 }, { id: 2, myUniqueValuesCountFromArray: 7, myUniqueValuesCount: 0 }, @@ -202,7 +202,7 @@ await test('hll', async (t) => { .filter('myUniqueValuesCount', '=', 11) .or('myUniqueValuesCountFromArray', '>', 6) .get() - ).toObject(), + ), [ { id: 1, @@ -280,7 +280,7 @@ await test('hll', async (t) => { .filter('id', '>=', 3) .include('contributors.$tokens') .get() - ).toObject(), + ), [ { id: 3, @@ -301,7 +301,7 @@ await test('hll', async (t) => { .filter('id', '>=', 3) .include('contributors.$undeftokens') .get() - .toObject(), + , [ { id: 3, @@ -330,7 +330,7 @@ await test('hll', async (t) => { .filter('id', '>=', 3) .include('contributors.$undeftokens') .get() - .toObject(), + , [ { id: 3, diff --git a/test/clientServer.perf.ts b/test/clientServer.perf.ts index 0959f4d621..3af6c04be0 100644 --- a/test/clientServer.perf.ts +++ b/test/clientServer.perf.ts @@ -55,7 +55,7 @@ await test('client server rapid fire', async (t) => { .query('user') .range(0, 100_000) .get() - .toObject() + let id = 0 for (const user of allUsers1) { diff --git a/test/copy.ts b/test/copy.ts index 13534d15c7..4f721d0554 100644 --- a/test/copy.ts +++ b/test/copy.ts @@ -94,7 +94,7 @@ await test('copy', async (t) => { .query('edition') .include('*', 'versionOf', 'versions', 'sequences', 'sequences.pages') .get() - .toObject() + deepEqual(res, [ { diff --git a/test/crc32c.ts b/test/crc32c.ts index ca95a71ddb..b29741b0ed 100644 --- a/test/crc32c.ts +++ b/test/crc32c.ts @@ -119,7 +119,7 @@ await test('simple', async (t) => { }) equal( - await db.query('transaction').include('id', 'myHash').get().toObject(), + await db.query('transaction').include('id', 'myHash').get(), [ { id: 1, @@ -133,7 +133,7 @@ await test('simple', async (t) => { .query('transactionN') .include('id', 'myNativeMadeHash') .get() - .toObject(), + , [ { id: 1, diff --git a/test/db-schema/schemaChange.ts b/test/db-schema/schemaChange.ts index 94422c46d9..2b981be12c 100644 --- a/test/db-schema/schemaChange.ts +++ b/test/db-schema/schemaChange.ts @@ -3,15 +3,11 @@ import { BasedDb } from '../../src/index.js' import { deepCopy } from '../../src/utils/index.js' import type { SchemaIn } from '../../src/schema/index.js' import { deepEqual } from '../shared/assert.js' +import { testDbClient, testDbServer } from '../shared/index.js' await test('set schema dont migrate', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) + const server = await testDbServer(t) + const client = await testDbClient(server) let schema = { types: { @@ -24,18 +20,18 @@ await test('set schema dont migrate', async (t) => { } let updates = 0 - db.client.on('schema', () => { + client.on('schema', () => { updates++ }) - await db.setSchema(deepCopy(schema) as SchemaIn) - await db.setSchema(deepCopy(schema) as SchemaIn) - await db.setSchema(deepCopy(schema) as SchemaIn) + await client.setSchema(deepCopy(schema) as SchemaIn) + await client.setSchema(deepCopy(schema) as SchemaIn) + await client.setSchema(deepCopy(schema) as SchemaIn) deepEqual(updates, 1, '1 update') // deepEqual(migrates, 0, '0 migrates') - await db.setSchema({ + await client.setSchema({ types: { yes: { name: 'string', @@ -65,7 +61,7 @@ await test('set schema dont migrate', async (t) => { deepEqual(updates, 2, '2 update') // deepEqual(migrates, 1, '1 migrates') - await db.setSchema({ + await client.setSchema({ types: { nope: { name: 'string', @@ -80,16 +76,16 @@ await test('set schema dont migrate', async (t) => { deepEqual(updates, 3, '3 update') // deepEqual(migrates, 2, '2 migrates') - await db.create('nope', { + await client.create('nope', { name: 'abe', }) - await db.create('yes', { + await client.create('yes', { name: 'bill', success: true, }) - await db.setSchema({ + await client.setSchema({ types: { nope: { name: 'string', diff --git a/test/db-schema/schemaDebug.ts b/test/db-schema/schemaDebug.ts index 6be56d8b58..d8b6745f77 100644 --- a/test/db-schema/schemaDebug.ts +++ b/test/db-schema/schemaDebug.ts @@ -1,9 +1,8 @@ import { serverChildProcess } from '../shared/serverChildProcess.js' -import { DbClient, DbClientHooks, DbServer } from '../../src/index.js' +import { DbClient, DbClientHooks } from '../../src/index.js' import test from '../shared/test.js' import { deepCopy, deepMerge, wait } from '../../src/utils/index.js' import { copy, emptyDir } from 'fs-extra/esm' -import { deepEqual, equal } from '../shared/assert.js' import type { SchemaOut } from '../../src/schema/index.js' const cleanProps = (props) => { @@ -88,7 +87,7 @@ await test('schema debug', async (t) => { let i = 1 while (i--) { - const contestants = await client.query('contestant').get().toObject() + const contestants = await client.query('contestant').get() await client.setSchema( deepMerge(schema, { diff --git a/test/db-schema/schemaProblems.ts b/test/db-schema/schemaProblems.ts index 642ccc610c..b2e079ca49 100644 --- a/test/db-schema/schemaProblems.ts +++ b/test/db-schema/schemaProblems.ts @@ -214,10 +214,10 @@ await test('schema problems', async (t) => { await Promise.all(q) equal( - (await db.query('flap').count().get().inspect().toObject()).count, + (await db.query('flap').count().get().inspect()).count, 1_100_001, ) - equal((await db.query('seq').count().get().inspect().toObject()).count, 1) + equal((await db.query('seq').count().get().inspect()).count, 1) // to Object on nested refs does not work if combin count + sum equal( @@ -227,7 +227,7 @@ await test('schema problems', async (t) => { .include((s) => s('flap').count()) .get() .inspect() - .toObject() + )[0].flap.count, 1_100_000, ) diff --git a/test/db-schema/schemaUpdates.ts b/test/db-schema/schemaUpdates.ts index 9984f289e4..42fa496771 100644 --- a/test/db-schema/schemaUpdates.ts +++ b/test/db-schema/schemaUpdates.ts @@ -1,26 +1,14 @@ import { setTimeout } from 'node:timers/promises' -import { DbClient } from '../../src/db-client/index.js' -import { DbServer } from '../../src/db-server/index.js' import { deepEqual } from '../shared/assert.js' import test from '../shared/test.js' -import { BasedDb, getDefaultHooks } from '../../src/index.js' import { wait } from '../../src/utils/index.js' +import { testDb, testDbClient, testDbServer } from '../shared/index.js' await test('client server schema updates', async (t) => { - const server = new DbServer({ - path: t.tmp, - }) - - await server.start({ clean: true }) - t.after(() => server.destroy()) + const server = await testDbServer(t, { noBackup: true }) - const client1 = new DbClient({ - hooks: getDefaultHooks(server), - }) - - const client2 = new DbClient({ - hooks: getDefaultHooks(server), - }) + const client1 = await testDbClient(server) + const client2 = await testDbClient(server) await client1.setSchema({ types: { @@ -38,7 +26,7 @@ await test('client server schema updates', async (t) => { name: 'jamez', }) - deepEqual(await client1.query('user').get(), [ + deepEqual(await client1.query2('user').get(), [ { id: 1, name: 'youzi' }, { id: 2, name: 'jamez' }, ]) @@ -51,16 +39,12 @@ await test('client server schema updates', async (t) => { }, }) - deepEqual(await client1.query('user').get(), [ + deepEqual(await client1.query2('user').get(), [ { id: 1, age: 0 }, { id: 2, age: 0 }, ]) - const ageSorted = await client2 - .query('user') - .sort('age', 'asc') - .get() - .toObject() + const ageSorted = await client2.query2('user').sort('age', 'asc').get() await client1.setSchema({ types: { @@ -70,11 +54,7 @@ await test('client server schema updates', async (t) => { }, }) - const ageSorted2 = await client1 - .query('user') - .sort('age', 'asc') - .get() - .toObject() + const ageSorted2 = await client1.query2('user').sort('age', 'asc').get() deepEqual(ageSorted, ageSorted2) @@ -89,29 +69,16 @@ await test('client server schema updates', async (t) => { }, }) - const ageSorted3 = await client1 - .query('user') - .sort('age', 'asc') - .get() - .toObject() + const ageSorted3 = await client1.query2('user').sort('age', 'asc').get() deepEqual(ageSorted3, ageSorted2) }) await test('rapid schema updates', async (t) => { - const server = new DbServer({ - path: t.tmp, - }) - await server.start({ clean: true }) - t.after(() => server.destroy()) - - const client1 = new DbClient({ - hooks: getDefaultHooks(server), - }) + const server = await testDbServer(t, { noBackup: true }) - const client2 = new DbClient({ - hooks: getDefaultHooks(server), - }) + const client1 = await testDbClient(server) + const client2 = await testDbClient(server) await client1.setSchema({ types: { @@ -159,31 +126,22 @@ await test('rapid schema updates', async (t) => { await Promise.all(promises) - const res = await client1.query('user').get().toObject() + const res = await client1.query2('user').get() deepEqual( [ { id: 1, name: 'youzi', field0: '' }, { id: 2, name: 'jamez', field0: '' }, ], - res, + res as any, ) }) await test('rapid modifies during schema update', async (t) => { - const server = new DbServer({ - path: t.tmp, - }) - await server.start({ clean: true }) - t.after(() => server.destroy()) - - const client1 = new DbClient({ - hooks: getDefaultHooks(server), - }) + const server = await testDbServer(t, { noBackup: true }) - const client2 = new DbClient({ - hooks: getDefaultHooks(server), - }) + const client1 = await testDbClient(server) + const client2 = await testDbClient(server) // console.log('set schema 1') await client1.setSchema({ types: { @@ -217,28 +175,22 @@ await test('rapid modifies during schema update', async (t) => { while (b--) { const name = 'jamex' + b const id = await client2.create('user', { name }) - const res = await client2.query('user', id).get().toObject() + const res = (await client2.query2('user', id).get())! deepEqual(res.id, id) deepEqual(res.name, name) } - const all = await client2.query('user').range(0, 1000_000).get().toObject() + const all = (await client2.query2('user').range(0, 1000_000).get())! // await wait(1e3) // console.log(all.length, all.slice(0, 10), all.slice(-10)) - deepEqual(all[0], { id: 1, name: 'youzi499999', age: 0 }) - deepEqual(all.at(-1), { id: 501000, name: 'jamex0', age: 0 }) + deepEqual(all[0], { id: 1, name: 'youzi499999', age: 0 } as any) + deepEqual(all.at(-1), { id: 501000, name: 'jamex0', age: 0 } as any) deepEqual(all.length, youzies + jamesies) }) await test('tree after schema update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { nurp: { props: { @@ -281,6 +233,4 @@ await test('tree after schema update', async (t) => { await db.create('user', { name: 'dr youz', }) - - await db.save() }) diff --git a/test/edges/edgeFilterNested.ts b/test/edges/edgeFilterNested.ts index a1d2fb172a..a0b2ddcaf5 100644 --- a/test/edges/edgeFilterNested.ts +++ b/test/edges/edgeFilterNested.ts @@ -1,15 +1,9 @@ import { deepEqual } from '../shared/assert.js' -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('edge enum', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { initiative: { name: 'string', @@ -27,7 +21,7 @@ await test('edge enum', async (t) => { items: { ref: 'initiative', prop: 'users', - // $role: ['a', 'b'], + $role: ['a', 'b'], }, }, }, @@ -47,10 +41,10 @@ await test('edge enum', async (t) => { deepEqual( await db - .query('user') - .include('name', (q) => { - q('initiatives').filter('$role', '=', 'a').include('name') - }) + .query2('user') + .include('name', (q) => + q('initiatives').filter('$role', '=', 'a').include('name'), + ) .get(), [ { @@ -64,12 +58,14 @@ await test('edge enum', async (t) => { deepEqual( await db - .query('user') - .include('name', (q) => { + .query2('user') + .include('name', (q) => q('initiatives') .filter('$role', '=', 'a') - .include((q) => q('users').include('$role').filter('$role', '=', 'b')) - }) + .include((q) => + q('users').include('$role').filter('$role', '=', 'b'), + ), + ) .get(), [ { diff --git a/test/edges/edgeNumbers.ts b/test/edges/edgeNumbers.ts index c788887c78..6ef0153836 100644 --- a/test/edges/edgeNumbers.ts +++ b/test/edges/edgeNumbers.ts @@ -1,15 +1,9 @@ import { deepEqual } from '../shared/assert.js' -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('number', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -44,7 +38,7 @@ await test('number', async (t) => { }, }) - deepEqual(await db.query('user', user2).include('**').get(), { + deepEqual(await db.query2('user', user2).include('**').get(), { id: 2, bestFriend: { id: 1, diff --git a/test/edges/edgeText.ts b/test/edges/edgeText.ts index f65aeaedb5..ec74c0f947 100644 --- a/test/edges/edgeText.ts +++ b/test/edges/edgeText.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' // Text is currently not supported in edge props: FDN-1713 FDN-730 await test.skip('text in an edge prop', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: { fallback: ['en'] }, @@ -42,7 +36,7 @@ await test.skip('text in an edge prop', async (t) => { $x: { en: 'hello' }, }, }) - deepEqual(await db.query('user', user2).include('**').get(), { + deepEqual(await db.query2('user', user2).include('**').get(), { id: user2, bestFriend: { id: user1, @@ -58,12 +52,12 @@ await test.skip('text in an edge prop', async (t) => { { id: user2, $x: { en: 'hello' } }, ], }) - deepEqual(await db.query('user', user1).include('**').get(), { + deepEqual(await db.query2('user', user1).include('**').get(), { id: user1, bestFriend: null, friends: [{ id: user3, $x: { en: 'hello' } }], }) - deepEqual(await db.query('user', user3).include('**').get(), { + deepEqual(await db.query2('user', user3).include('**').get(), { id: user3, bestFriend: { id: user2, @@ -78,7 +72,7 @@ await test.skip('text in an edge prop', async (t) => { await db.update('user', user3, { friends: { update: [{ id: user2, $index: 0 }] }, }) - deepEqual(await db.query('user', user3).include('**').get(), { + deepEqual(await db.query2('user', user3).include('**').get(), { id: user3, bestFriend: { id: user2, $x: 0 }, friends: [ diff --git a/test/edges/edgeType.ts b/test/edges/edgeType.ts index 8172354034..cc8d148b8d 100644 --- a/test/edges/edgeType.ts +++ b/test/edges/edgeType.ts @@ -1,16 +1,15 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual, equal } from '../shared/assert.js' -import { countDirtyBlocks } from '../shared/index.js' +import { + countDirtyBlocks, + testDbServer, + testDbClient, + testDb, +} from '../shared/index.js' await test('single reference', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const server = await testDbServer(t) + const db = await testDbClient(server, { types: { user: { props: { @@ -51,18 +50,12 @@ await test('single reference', async (t) => { }, }) - deepEqual(await countDirtyBlocks(db.server), 3) + deepEqual(await countDirtyBlocks(server), 3) }) await test('json type edge', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const server = await testDbServer(t, { noBackup: true }) + const db = await testDbClient(server, { types: { workspace: { name: 'string', @@ -96,7 +89,7 @@ await test('json type edge', async (t) => { ], }) const retrieved = await db - .query('serviceAccount', serviceAccountId) + .query2('serviceAccount', serviceAccountId) .include( 'id', 'workspaces.id', @@ -104,7 +97,6 @@ await test('json type edge', async (t) => { 'workspaces.$permissionsJson', ) .get() - .toObject() equal(retrieved?.workspaces.length, 1, 'Expected to have length 1') deepEqual( diff --git a/test/edges/edges.ts b/test/edges/edges.ts index 5a272d51c2..4d306d418c 100644 --- a/test/edges/edges.ts +++ b/test/edges/edges.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' import { italy, sentence } from '../shared/examples.js' await test('multiple references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -107,7 +101,7 @@ await test('multiple references', async (t) => { // console.log( // 'derp', // await db - // .query('article') + // .query2('article') // .include('contributors.$role') // // .include('contributors.$role', 'contributors.$bigString') // .get(), @@ -115,7 +109,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.$role', 'contributors.$bigString') .get(), [ @@ -130,7 +124,7 @@ await test('multiple references', async (t) => { ], ) - deepEqual(await db.query('article').include('contributors.$rating').get(), [ + deepEqual(await db.query2('article').include('contributors.$rating').get(), [ { id: artStrudel, contributors: [{ id: 1, $rating: 5 }], @@ -141,7 +135,7 @@ await test('multiple references', async (t) => { }, ]) - deepEqual(await db.query('article').include('contributors.$lang').get(), [ + deepEqual(await db.query2('article').include('contributors.$lang').get(), [ { id: 1, contributors: [{ id: 1, $lang: 'en' }], @@ -152,7 +146,7 @@ await test('multiple references', async (t) => { }, ]) - deepEqual(await db.query('article').include('contributors.$on').get(), [ + deepEqual(await db.query2('article').include('contributors.$on').get(), [ { id: 1, contributors: [{ id: 1, $on: true }], @@ -164,7 +158,7 @@ await test('multiple references', async (t) => { ]) deepEqual( - await db.query('article').include('contributors.$file').get(), + await db.query2('article').include('contributors.$file').get(), [ { id: 1, @@ -192,7 +186,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((s) => s('contributors').filter('$role', '=', 'writer').include('$role'), ) @@ -216,7 +210,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((s) => s('contributors') .filter('$bigString', '=', italy) @@ -248,7 +242,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article', lastArticle) + .query2('article', lastArticle) .include('contributors.$rating') .get(), { @@ -258,7 +252,7 @@ await test('multiple references', async (t) => { ) deepEqual( - await db.query('article', 3).include('contributors.$countries.id').get(), + await db.query2('article', 3).include('contributors.$countries.id').get(), { id: 3, contributors: [ @@ -277,7 +271,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors') .range(lastArticle - 3, 1000) .get(), @@ -290,13 +284,7 @@ await test('multiple references', async (t) => { }) await test('single reference', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -335,7 +323,7 @@ await test('single reference', async (t) => { author: { id: mrDrol, $role: 'boss' }, }) - deepEqual(await db.query('article').include('author.$role', '*').get(), [ + deepEqual(await db.query2('article').include('author.$role', '*').get(), [ { id: 1, name: 'This is a nice article', @@ -353,7 +341,7 @@ await test('single reference', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('author.$role', '*') .filter('author.$role', '=', 'boss') .get(), @@ -374,7 +362,7 @@ await test('single reference', async (t) => { author: { id: mrDrol, $msg: sentence }, }) - deepEqual(await db.query('article').include('author.$msg', '*').get(), [ + deepEqual(await db.query2('article').include('author.$msg', '*').get(), [ { id: 1, name: 'This is a nice article', author: { id: 1 } }, { id: 2, @@ -393,13 +381,7 @@ await test('single reference', async (t) => { }) await test('preserve fields', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -427,7 +409,7 @@ await test('preserve fields', async (t) => { $x: 42, }, }) - deepEqual(await db.query('user', user2).include('**').get(), { + deepEqual(await db.query2('user', user2).include('**').get(), { id: user2, bestFriend: { id: user1, @@ -443,12 +425,12 @@ await test('preserve fields', async (t) => { { id: user2, $x: 20 }, ], }) - deepEqual(await db.query('user', user1).include('**').get(), { + deepEqual(await db.query2('user', user1).include('**').get(), { id: user1, bestFriend: null, friends: [{ id: user3, $x: 10 }], }) - deepEqual(await db.query('user', user3).include('**').get(), { + deepEqual(await db.query2('user', user3).include('**').get(), { id: user3, bestFriend: { id: user2, @@ -463,7 +445,7 @@ await test('preserve fields', async (t) => { await db.update('user', user3, { friends: { update: [{ id: user2, $index: 0 }] }, }) - deepEqual(await db.query('user', user3).include('**').get(), { + deepEqual(await db.query2('user', user3).include('**').get(), { id: user3, bestFriend: { id: user2, $x: 0 }, friends: [ diff --git a/test/edges/edgesMain.ts b/test/edges/edgesMain.ts index d5e5dc480b..4f5025f725 100644 --- a/test/edges/edgesMain.ts +++ b/test/edges/edgesMain.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' await test('multiple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -62,7 +56,7 @@ await test('multiple', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.$rdy') .include('contributors.$rating') .include('contributors.$derp') @@ -102,7 +96,7 @@ await test('multiple', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('name') .include('contributors.$rdy') .include('contributors.$rating') @@ -142,7 +136,7 @@ await test('multiple', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('name') .include('contributors.$rdy') .include('contributors.$rating') @@ -178,7 +172,7 @@ await test('multiple', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('name') .include('contributors.$rdy') .include('contributors.$rating') @@ -203,13 +197,7 @@ await test('multiple', async (t) => { }) await test('single', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -252,7 +240,7 @@ await test('single', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributor.$rdy') .include('contributor.$rating') .include('contributor.$derp') @@ -271,7 +259,7 @@ await test('single', async (t) => { ) deepEqual( - await db.query('article').include('contributor.$rdy').get().toObject(), + await db.query2('article').include('contributor.$rdy').get(), [ { id: 1, @@ -285,13 +273,7 @@ await test('single', async (t) => { }) await test('multi references update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -342,10 +324,10 @@ await test('multi references update', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.$age') .get() - .then((v) => v.toObject()), + .then((v) => v), [{ id: 1, contributors: [{ id: 1, $age: 66 }] }], 'age 66', ) @@ -364,33 +346,27 @@ await test('multi references update', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.$age') .get() - .then((v) => v.toObject()), + .then((v) => v), [{ id: 1, contributors: [{ id: 1, $age: 2 }] }], 'age 2', ) deepEqual( await db - .query('article') + .query2('article') .include('contributors.$plonki') .get() - .then((v) => v.toObject()), + .then((v) => v), [{ id: 1, contributors: [{ id: 1, $plonki: 100 }] }], 'plonki 100', ) }) await test('single ref update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: {}, article: { @@ -419,7 +395,7 @@ await test('single ref update', async (t) => { }) deepEqual( - await db.query('article').include('writer.$age').get(), + await db.query2('article').include('writer.$age').get(), [{ id: 1, writer: { id: 1, $age: 66 } }], 'age 66', ) @@ -433,13 +409,13 @@ await test('single ref update', async (t) => { }) deepEqual( - await db.query('article').include('writer.$age').get(), + await db.query2('article').include('writer.$age').get(), [{ id: 1, writer: { id: 1, $age: 202 } }], 'age 202', ) deepEqual( - await db.query('article').include('writer.$plonki').get(), + await db.query2('article').include('writer.$plonki').get(), [{ id: 1, writer: { id: 1, $plonki: 100 } }], 'plonki 100', ) diff --git a/test/edges/edgesReference.ts b/test/edges/edgesReference.ts index 30841ef769..1d31be4657 100644 --- a/test/edges/edgesReference.ts +++ b/test/edges/edgesReference.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' await test('multi reference', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -62,7 +56,7 @@ await test('multi reference', async (t) => { }) deepEqual( - await db.query('article').include('contributor.$friend').get().toObject(), + await db.query2('article').include('contributor.$friend').get(), [ { id: 1, @@ -73,14 +67,7 @@ await test('multi reference', async (t) => { }) await test('multiple references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { country: { props: { @@ -162,10 +149,10 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('name', 'contributor.$countries') .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -206,10 +193,10 @@ await test('multiple references', async (t) => { await db.drain() const articles = ( await db - .query('article') + .query2('article') .include('name', 'contributor.$countries') .get() - .toObject() + ).slice(-10) for (const article of articles) { diff --git a/test/edges/edgesReferences.ts b/test/edges/edgesReferences.ts index c7e5ccf2fe..5e4862e4fb 100644 --- a/test/edges/edgesReferences.ts +++ b/test/edges/edgesReferences.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' await test('multi reference', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -102,17 +96,17 @@ await test('multi reference', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.$age') .get() - .then((v) => v.toObject()), + .then((v) => v), [{ id: 1, contributors: [{ id: 1, $age: 66 }] }], 'age 66', ) deepEqual( await db - .query('article') + .query2('article') .include('contributors.$friend.name', 'contributors.$friend.location') .get(), [ @@ -134,7 +128,7 @@ await test('multi reference', async (t) => { ) deepEqual( - await db.query('article').include('contributors.$friend').get(), + await db.query2('article').include('contributors.$friend').get(), [ { id: 1, @@ -157,13 +151,7 @@ await test('multi reference', async (t) => { }) await test('multiple references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { country: { props: { @@ -251,10 +239,10 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.id') .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, contributors: [{ id: mrDerp }] }, { id: 2, contributors: [{ id: mrFlap }] }, @@ -263,10 +251,10 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.id', 'contributors.$countries.id') .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -281,10 +269,10 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.id', 'contributors.$countries.code') .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -315,10 +303,10 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include('contributors.id', 'contributors.$countries') .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -349,12 +337,12 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((t) => { t('contributors').include('$countries').include('name').sort('name') }) .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -387,12 +375,12 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((t) => { t('contributors').include('name').filter('nationality', '=', nl) }) .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -412,7 +400,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((t) => { t('contributors') .include('name') @@ -421,7 +409,7 @@ await test('multiple references', async (t) => { .filter('nationality', '=', nl) }) .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -445,7 +433,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((s) => { s('contributors') .include('name') @@ -456,7 +444,7 @@ await test('multiple references', async (t) => { .filter('nationality', '=', nl) }) .get() - .toObject(), + , [ { id: 1, @@ -477,7 +465,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((s) => { s('contributors') .include('name') @@ -488,7 +476,7 @@ await test('multiple references', async (t) => { .filter('nationality', '=', nl) }) .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -506,7 +494,7 @@ await test('multiple references', async (t) => { deepEqual( await db - .query('article') + .query2('article') .include((s) => { s('contributors') .include('name') @@ -517,7 +505,7 @@ await test('multiple references', async (t) => { .filter('nationality', '=', nl) }) .get() - .then((v) => v.toObject()), + .then((v) => v), [ { id: 1, @@ -538,15 +526,7 @@ await test('multiple references', async (t) => { }) await test('simple references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { round: { name: 'alias', @@ -595,19 +575,13 @@ await test('simple references', async (t) => { }, }) - deepEqual(await db.query('phase').include('scenarios').get(), [ + deepEqual(await db.query2('phase').include('scenarios').get(), [ { id: 1, scenarios: [{ id: scenarioId1, name: 'scenario' }] }, ]) }) await test('many to many', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - t.after(() => t.backup(db)) - - await db.start() + const db = await testDb(t, {}) await db.setSchema({ types: { @@ -634,9 +608,9 @@ await test('many to many', async (t) => { scenarios: { add: [{ id: phaseId2, $name: 'a' }] }, }) - // await db.query('phase').include('scenarios').get(). + // await db.query2('phase').include('scenarios').get(). - deepEqual(await db.query('phase').include('scenarios').get(), [ + deepEqual(await db.query2('phase').include('scenarios').get(), [ { id: 1, scenarios: [{ id: 2, name: 'phase' }] }, { id: 2, scenarios: [{ id: 1, name: 'phase' }] }, { id: 3, scenarios: [] }, @@ -648,7 +622,7 @@ await test('many to many', async (t) => { await db.drain() - deepEqual(await db.query('phase').include('scenarios').get(), [ + deepEqual(await db.query2('phase').include('scenarios').get(), [ { id: 1, scenarios: [ diff --git a/test/expire.ts b/test/expire.ts index 10ed4a8ea5..f6ff5c4dbe 100644 --- a/test/expire.ts +++ b/test/expire.ts @@ -38,9 +38,9 @@ await test('expire', async (t) => { db.expire('token', token1, 1) await db.drain() - equal((await db.query('token').get().toObject()).length, 1) + equal((await db.query('token').get()).length, 1) await setTimeout(2e3) - equal((await db.query('token').get().toObject()).length, 0) + equal((await db.query('token').get()).length, 0) const token2 = await db.create('token', { name: 'my new token', @@ -50,7 +50,7 @@ await test('expire', async (t) => { await db.drain() await db.save() equal( - (await db.query('token').get().toObject()).length, + (await db.query('token').get()).length, 1, '1 token before save', ) @@ -61,13 +61,13 @@ await test('expire', async (t) => { await db2.start() equal( - (await db2.query('token').get().toObject()).length, + (await db2.query('token').get()).length, 1, '1 token after load', ) await setTimeout(3e3) equal( - (await db2.query('token').get().toObject()).length, + (await db2.query('token').get()).length, 0, '0 tokens after expiry', ) diff --git a/test/filter/filter.ts b/test/filter/filter.ts index f88bde75a8..4b13ab66d9 100644 --- a/test/filter/filter.ts +++ b/test/filter/filter.ts @@ -47,7 +47,7 @@ await test('single', async (t) => { const x = [10, 20] - deepEqual((await db.query('org').filter('x', '=', x).get()).toObject(), [ + deepEqual((await db.query('org').filter('x', '=', x).get()), [ { id: 1, status: 'ok', @@ -56,7 +56,7 @@ await test('single', async (t) => { }, ]) deepEqual( - (await db.query('org').filter('orgs', '=', [org, org2]).get()).toObject(), + (await db.query('org').filter('orgs', '=', [org, org2]).get()), [ { id: 3, @@ -67,11 +67,11 @@ await test('single', async (t) => { ], ) deepEqual( - (await db.query('org').filter('status', '=', 'error').get()).toObject(), + (await db.query('org').filter('status', '=', 'error').get()), [], ) deepEqual( - (await db.query('org').filter('status', '=', 'ok').get()).toObject(), + (await db.query('org').filter('status', '=', 'ok').get()), [ { id: 1, @@ -88,13 +88,13 @@ await test('single', async (t) => { ], ) deepEqual( - (await db.query('org').filter('name', 'includes', '0').get()).toObject(), + (await db.query('org').filter('name', 'includes', '0').get()), [], ) deepEqual( ( await db.query('org').filter('name', 'includes', 'hello').get() - ).toObject(), + ), [ { id: 1, @@ -245,7 +245,7 @@ await test('simple', async (t) => { ) for (const envs of res) { - mi += envs.toObject().length + mi += envs.length measure += envs.execTime } @@ -268,7 +268,7 @@ await test('simple', async (t) => { .include('*') .filter('machines', 'includes', rand) .get() - mi += envs.toObject().length + mi += envs.length measure += envs.execTime }), ) @@ -287,7 +287,7 @@ await test('simple', async (t) => { .include('*') .filter('scheduled', '>', 'now + 694d + 10h') .get() - ).toObject().length, + ).length, 1, ) @@ -298,7 +298,7 @@ await test('simple', async (t) => { .include('*') .filter('scheduled', '<', 'now-694d-10h-15m') // Date, .get() - ).toObject().length, + ).length, 1, ) @@ -309,7 +309,7 @@ await test('simple', async (t) => { .include('*') .filter('scheduled', '<', '10/24/2000') // Date, .get() - ).toObject().length, + ).length, 0, 'parse date string', ) @@ -321,7 +321,7 @@ await test('simple', async (t) => { .include('*') .filter('requestsServed', '<', 1) .get() - ).toObject().length, + ).length, 1, ) @@ -332,7 +332,7 @@ await test('simple', async (t) => { .include('*') .filter('requestsServed', '<=', 1) .get() - ).toObject().length, + ).length, 2, ) @@ -344,7 +344,7 @@ await test('simple', async (t) => { .filter('derp', '<=', 0) .filter('derp', '>', -5) .get() - ).toObject().length, + ).length, 5, 'Negative range', ) @@ -357,7 +357,7 @@ await test('simple', async (t) => { .filter('temperature', '<=', 0) .filter('temperature', '>', -0.1) .get() - ).toObject().length < 500, + ).length < 500, true, 'Negative temperature (result amount)', ) @@ -370,7 +370,7 @@ await test('simple', async (t) => { .filter('temperature', '<=', 0) .filter('temperature', '>', -0.1) .get() - ).toObject()[0].temperature < 0, + )[0].temperature < 0, true, 'Negative temperature (check value)', ) @@ -383,7 +383,7 @@ await test('simple', async (t) => { .filter('env', '=', env) .range(0, 10) .get() - ).toObject(), + ), [ { id: 2 }, { id: 4 }, @@ -408,7 +408,7 @@ await test('simple', async (t) => { .filter('env', '=', [emptyEnv, env]) .range(0, 10) .get() - ).toObject(), + ), [{ id: 100000 }], 'Filter by reference (multiple)', ) @@ -478,7 +478,7 @@ await test('simple', async (t) => { .include('env', '*') .filter('env.status', '=', 5) .get() - ).toObject(), + ), [ { id: 100001, @@ -527,7 +527,7 @@ await test('simple', async (t) => { .filter('status', '=', '🦄') .include('status') .get() - ).toObject(), + ), [ { id: unicornMachine, @@ -536,13 +536,13 @@ await test('simple', async (t) => { ], ) - deepEqual((await db.query('env').filter('standby').get()).toObject(), []) + deepEqual((await db.query('env').filter('standby').get()), []) await db.update('env', derpEnv, { standby: true, }) - deepEqual((await db.query('env').filter('standby').get()).toObject(), [ + deepEqual((await db.query('env').filter('standby').get()), [ { id: 3, standby: true, status: 5, name: 'derp env' }, ]) @@ -636,7 +636,7 @@ await test('or', async (t) => { .filter('scheduled', '>', '01/01/2100') .or('lastPing', '>', 1e6 - 2) .get() - ).toObject(), + ), [ { id: 999999, @@ -659,7 +659,7 @@ await test('or', async (t) => { f.filter('lastPing', '>', 1e6 - 2) }) .get() - ).toObject(), + ), ( await db .query('machine') @@ -667,7 +667,7 @@ await test('or', async (t) => { .filter('scheduled', '>', '01/01/2100') .or('lastPing', '>', 1e6 - 2) .get() - ).toObject(), + ), ) equal( @@ -681,7 +681,7 @@ await test('or', async (t) => { f.or('temperature', '<', -30) }) .get() - ).toObject().length > 10, + ).length > 10, true, 'Branch or', ) @@ -699,7 +699,7 @@ await test('or', async (t) => { }) }) .get() - ).toObject(), + ), ( await db .query('machine') @@ -710,7 +710,7 @@ await test('or', async (t) => { f.or('temperature', '<', -30) }) .get() - ).toObject(), + ), ) const r = ( @@ -721,7 +721,7 @@ await test('or', async (t) => { .filter('temperature', '>', 0) .or('temperature', '<', -0.1) .get() - ).toObject() + ) equal( r @@ -768,7 +768,7 @@ await test('or numerical', async (t) => { .filter('temperature', '>', 150) .or('temperature', '<', 50) .get() - ).toObject() + ) equal( r @@ -803,7 +803,7 @@ await test('or numerical', async (t) => { f.or('temperature', '<', 10) }) .get() - ).toObject().length > 10, + ).length > 10, true, 'Branch or', ) @@ -908,7 +908,7 @@ await test.skip('includes', async (t) => { '*', ) .get() - .toObject() + ).filter((u) => u.buddies.length > 0), ), ) @@ -939,5 +939,5 @@ await test('lt x leq', async (t) => { blue: 6, }) const b = await db.query('bucket').filter('red', '<', 4).get() - equal(b.toObject().length, 1, 'lt must be different than leq') + equal(b.length, 1, 'lt must be different than leq') }) diff --git a/test/filter/string.ts b/test/filter/string.ts index 888e63fbe4..c3910bc609 100644 --- a/test/filter/string.ts +++ b/test/filter/string.ts @@ -221,7 +221,7 @@ await test('has compressed', async (t) => { .include('id') .range(0, 1e3) .get() - ).toObject().length, + ).length, 1, ) }) @@ -272,7 +272,7 @@ await test('has uncompressed', async (t) => { .include('id') .range(0, 1e3) .get() - ).toObject().length, + ).length, 0, ) @@ -284,7 +284,7 @@ await test('has uncompressed', async (t) => { .include('id') .range(0, 1e3) .get() - ).toObject().length, + ).length, 0, ) diff --git a/test/hooks.ts b/test/hooks.ts index 9ee763108e..56567fed50 100644 --- a/test/hooks.ts +++ b/test/hooks.ts @@ -414,7 +414,7 @@ await test('aggregate hooks', async (t) => { age: 100, }) - equal((await db.query('user').sum('age').get().toObject()).age.sum, 21) + equal((await db.query('user').sum('age').get()).age.sum, 21) }) await test('search hooks', async (t) => { @@ -464,7 +464,7 @@ await test('search hooks', async (t) => { age: 100, }) - equal((await db.query('user').search('youzi').get().toObject()).length, 1) + equal((await db.query('user').search('youzi').get()).length, 1) }) await test('groupBy hooks', async (t) => { @@ -514,7 +514,7 @@ await test('groupBy hooks', async (t) => { age: 100, }) - equal(await db.query('user').groupBy('name').sum('age').get().toObject(), { + equal(await db.query('user').groupBy('name').sum('age').get(), { youzi: { age: { sum: 21 } }, }) }) @@ -570,7 +570,7 @@ await test('filter hooks', async (t) => { age: 100, }) - equal(await db.query('user').filter('name', '=', 'youzi').get().toObject(), [ + equal(await db.query('user').filter('name', '=', 'youzi').get(), [ { id: 1, age: 21, name: 'youzi' }, ]) }) @@ -622,7 +622,7 @@ await test('include hooks', async (t) => { age: 100, }) - equal(await db.query('user').include('name', 'age').get().toObject(), [ + equal(await db.query('user').include('name', 'age').get(), [ { id: 1, age: 21, name: 'youzi' }, ]) }) @@ -662,7 +662,7 @@ await test('upsert calls create and/or update hooks', async (t) => { age: 21, }) - const results1 = await db.query('user').get().toObject() + const results1 = await db.query('user').get() equal(results1.length, 1) @@ -675,7 +675,7 @@ await test('upsert calls create and/or update hooks', async (t) => { age: 45, }) - const results2 = await db.query('user').get().toObject() + const results2 = await db.query('user').get() equal(results2.length, 1) equal(results2[0].createdString != 0, true) equal(results2[0].updatedString != 0, true) diff --git a/test/idOffset.ts b/test/idOffset.ts index bc4b53e266..9333cc550b 100644 --- a/test/idOffset.ts +++ b/test/idOffset.ts @@ -31,7 +31,7 @@ await test('idOffset', async (t) => { } await db.drain() - const allUsers1 = await db.query('user').get().toObject() + const allUsers1 = await db.query('user').get() let id = 0 console.log(allUsers1.length) diff --git a/test/instantModify.ts b/test/instantModify.ts index dfea4e6d31..a86cd7fd09 100644 --- a/test/instantModify.ts +++ b/test/instantModify.ts @@ -109,12 +109,12 @@ await test.skip('instantModify', async (t) => { let j = 1000 await db2.start() while (j--) { - // db2.query('country').get().toObject() + // db2.query('country').get() for (const update of updates) { db2.server.modify(update) } } - // console.log('AFTER:', await db2.query('country').get().toObject()) + // console.log('AFTER:', await db2.query('country').get()) await db2.destroy() }) diff --git a/test/isModified.perf.ts b/test/isModified.perf.ts index fabd443afb..1a6bdb4d93 100644 --- a/test/isModified.perf.ts +++ b/test/isModified.perf.ts @@ -32,7 +32,7 @@ await test('isModified', async (t) => { const r = await Promise.all(q) for (const result of r) { - deepEqual(result.toObject(), [ + deepEqual(result, [ { id: 1, nr: 0 }, { id: 2, nr: 1 }, { id: 3, nr: 2 }, diff --git a/test/locales.ts b/test/locales.ts index bded1b9f6f..617204cc31 100644 --- a/test/locales.ts +++ b/test/locales.ts @@ -36,7 +36,7 @@ await test('locales', async (t) => { client.create('thing', payload) } - const things = await client.query('thing').get().toObject() + const things = await client.query('thing').get() for (const thing of things) { const payload: typeof thing = { @@ -49,7 +49,7 @@ await test('locales', async (t) => { await client.drain() - const updatedThings = await client.query('thing').get().toObject() + const updatedThings = await client.query('thing').get() for (const thing of updatedThings) { if (thing.string !== '') { diff --git a/test/mainAndEmptyStringFieldDelete.ts b/test/mainAndEmptyStringFieldDelete.ts index 7eae465843..738d22bb4c 100644 --- a/test/mainAndEmptyStringFieldDelete.ts +++ b/test/mainAndEmptyStringFieldDelete.ts @@ -26,7 +26,7 @@ await test('main + empty', async (t) => { location: '', }) - deepEqual(await db.query('user').get().toObject(), [ + deepEqual(await db.query('user').get(), [ { id: 1, role: 'translator', location: '' }, ]) }) diff --git a/test/migration.ts b/test/migration.ts index 2e07059506..022d00c7d2 100644 --- a/test/migration.ts +++ b/test/migration.ts @@ -85,7 +85,7 @@ await test('migration', async (t) => { await db.create('person', payload) } - // console.dir(await db.query('person').include('*', '**').get().toObject(), { + // console.dir(await db.query('person').include('*', '**').get(), { // depth: null, // }) @@ -224,8 +224,8 @@ await test('migration', async (t) => { await db.setSchema(schema) } - const users = await db.query('user').get().toObject() - const people = await db.query('person').include('*', '**').get().toObject() + const users = await db.query('user').get() + const people = await db.query('person').include('*', '**').get() equal(users.length, 10) equal(people.length, 10) diff --git a/test/modify/insert.ts b/test/modify/insert.ts index b7f9d58f04..df5070f235 100644 --- a/test/modify/insert.ts +++ b/test/modify/insert.ts @@ -20,7 +20,7 @@ await test('insert', async (t) => { { email: 'youri@saulx.com', isNice: true }, ) - const res1 = await db.query('user', id1).get().toObject() + const res1 = await db.query2('user', id1).get() deepEqual(res1, { id: id1, uuid: '9dg786', @@ -33,7 +33,7 @@ await test('insert', async (t) => { deepEqual(id1, id2, 'Ids should be the same') - const res2 = await db.query('user', id1).get() + const res2 = await db.query2('user', id1).get() deepEqual(res2, { id: id1, uuid: '9dg786', @@ -48,7 +48,7 @@ await test('insert', async (t) => { { uuid: 'unique-id-2', isNice: true }, ) - const res3 = await db.query('user', id3).get() + const res3 = await db.query2('user', id3).get() deepEqual(res3, { id: id3, uuid: 'unique-id-2', @@ -65,7 +65,7 @@ await test('insert', async (t) => { deepEqual(id3, id4, 'Ids should be the same 2') - const res4 = await db.query('user', id3).get() + const res4 = await db.query2('user', id3).get() deepEqual(res4, { id: id3, uuid: 'unique-id-2', diff --git a/test/modify/upsert.ts b/test/modify/upsert.ts index 89d4191e5d..31ed5a9716 100644 --- a/test/modify/upsert.ts +++ b/test/modify/upsert.ts @@ -20,7 +20,7 @@ await test('upsert', async (t) => { { email: 'youri@saulx.com', isNice: true }, ) - const res1 = await db.query('user', id1).get() + const res1 = await db.query2('user', id1).get() deepEqual(res1, { id: id1, uuid: '9dg786', @@ -33,7 +33,7 @@ await test('upsert', async (t) => { deepEqual(id1, id2, 'Ids should be the same') - const res2 = await db.query('user', id1).get() + const res2 = await db.query2('user', id1).get() deepEqual(res2, { id: id1, uuid: '9dg786', @@ -48,7 +48,7 @@ await test('upsert', async (t) => { { uuid: 'unique-id-2', isNice: true }, ) - const res3 = await db.query('user', id3).get() + const res3 = await db.query2('user', id3).get() deepEqual(res3, { id: id3, uuid: 'unique-id-2', @@ -65,7 +65,7 @@ await test('upsert', async (t) => { deepEqual(id3, id4, 'Ids should be the same 2') - const res4 = await db.query('user', id3).get() + const res4 = await db.query2('user', id3).get() deepEqual(res4, { id: id3, uuid: 'unique-id-2', diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index 663406d797..350f168b83 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -295,5 +295,5 @@ await test('group by', async (t) => { // .get() // r.debug() - // console.dir(r.toObject(), { depth: 10 }) + // console.dir(r, { depth: 10 }) }) diff --git a/test/query/ast.ts b/test/query/ast.ts index 6ecd58e2e8..02a69ef75e 100644 --- a/test/query/ast.ts +++ b/test/query/ast.ts @@ -1,5 +1,5 @@ import { query } from '../../src/db-client/query2/index.js' -import { deepEqual, testDb } from '../shared/index.js' +import { deepEqual } from '../shared/index.js' import test from '../shared/test.js' await test('query ast creation', async (t) => { @@ -15,6 +15,13 @@ await test('query ast creation', async (t) => { prop: 'friend' $rating: 'uint32' } + friends: { + items: { + ref: 'user' + prop: 'friend' + $rating: 'uint32' + } + } name: 'string' isNice: 'boolean' age: 'number' @@ -23,7 +30,7 @@ await test('query ast creation', async (t) => { } { - const q = query('user') + const q = query('user') .filter('isNice', '=', false) .and('name', '=', 'youzi') @@ -205,27 +212,27 @@ await test('query ast creation', async (t) => { }) } { - const q1 = query('user').sort('age') + const q1 = query('user').sort('age') deepEqual(q1.ast, { type: 'user', order: 'asc', sort: { prop: 'age' }, }) - const q2 = query('user').sort('age', 'desc') + const q2 = query('user').sort('age', 'desc') deepEqual(q2.ast, { type: 'user', order: 'desc', sort: { prop: 'age' }, }) - const q3 = query('user').order('desc') + const q3 = query('user').order('desc') deepEqual(q3.ast, { type: 'user', order: 'desc', }) - const q4 = query('user').sort('age').order('desc') + const q4 = query('user').sort('age').order('desc') deepEqual(q4.ast, { type: 'user', order: 'desc', @@ -234,7 +241,9 @@ await test('query ast creation', async (t) => { } { - const res = query('user').include((select) => select('friend').sum('age')) + const res = query('user').include((select) => + select('friend').sum('age'), + ) deepEqual(res.ast, { type: 'user', props: { @@ -244,7 +253,9 @@ await test('query ast creation', async (t) => { } { - const res = query('user').sum((select) => select('friends').sum('age')) + const res = query('user').sum((select) => + select('friends').sum('age'), + ) console.dir(res.ast, { depth: null }) // deepEqual(res.ast, { // type: 'user', @@ -255,4 +266,64 @@ await test('query ast creation', async (t) => { // }, // }) } + + { + const res = query('user') + .include('friends.$rating') + .filter('friends.$rating', '>', 5) + deepEqual(res.ast, { + type: 'user', + props: { + friends: { + edges: { props: { $rating: { include: {} } } }, + }, + }, + filter: { + props: { + friends: { + edges: { + props: { + $rating: { ops: [{ op: '>', val: 5 }] }, + }, + }, + }, + }, + }, + }) + } + + { + const res = query('user').include((select) => + select('friends').include('$rating'), + ) + deepEqual(res.ast, { + type: 'user', + props: { + friends: { + edges: { props: { $rating: { include: {} } } }, + }, + }, + }) + } + + { + const res = query('user').include((select) => + select('friends').include('$rating').filter('$rating', '>', 5), + ) + deepEqual(res.ast, { + type: 'user', + props: { + friends: { + edges: { props: { $rating: { include: {} } } }, + filter: { + edges: { + props: { + $rating: { ops: [{ op: '>', val: 5 }] }, + }, + }, + }, + }, + }, + }) + } }) diff --git a/test/query/db.ts b/test/query/db.ts index a5cf4f6162..cc90b3125c 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -25,6 +25,8 @@ await test('query db', async (t) => { items: { ref: 'user', prop: 'friends', + $rating: 'uint32', + $rank: 'number', }, }, }, @@ -125,35 +127,44 @@ await test('query db', async (t) => { // deepEqual(res, { friends: { age: { sum: 70 } } }) // } - { - const res = await db - .query2('user') - .sum((select) => select('friends').sum('age')) - .get() - deepEqual(res, { friends: { age: { sum: 21 } } } as any) - } - // { // const res = await db // .query2('user') - // .include((select) => select('friend').sum('age')) + // .sum((select) => select('friends').sum('age')) // .get() + // deepEqual(res, { friends: { age: { sum: 21 } } }) + // } + + // // { + // // const res = await db + // // .query2('user') + // // .include((select) => select('friend').sum('age')) + // // .get() + + // // deepEqual(res, [{ id: 1, friend: { age: { sum: 70 } } }]) + // // } - // deepEqual(res, [{ id: 1, friend: { age: { sum: 70 } } }]) + // { + // const res = await db.query2('user').sum('age').groupBy('name').get() + // deepEqual(res, { john: { age: { sum: 21 } }, billy: { age: { sum: 49 } } }) // } - { - const res = await db.query2('user').sum('age').groupBy('name').get() - deepEqual(res, { john: { age: { sum: 21 } }, billy: { age: { sum: 49 } } }) - } + // { + // const res = await db + // .query2('user') + // .filter('isNice', '=', true) + // .sum('age') + // .groupBy('name') + // .get() + // deepEqual(res, { billy: { age: { sum: 49 } } }) + // } { - const res = await db - .query2('user') - .filter('isNice', '=', true) - .sum('age') - .groupBy('name') - .get() - deepEqual(res, { billy: { age: { sum: 49 } } }) + const q = db.query2('user').include('friends.$rank', 'friends.$rating') + const res = await q.get() + deepEqual(res, [ + { id: 1, friends: [{ id: 2, $rank: 0, $rating: 0 }] }, + { id: 2, friends: [{ id: 1, $rank: 0, $rating: 0 }] }, + ]) } }) diff --git a/test/range.ts b/test/range.ts index 2f009dd116..9a7cfed4f0 100644 --- a/test/range.ts +++ b/test/range.ts @@ -97,6 +97,6 @@ await test('default range: 1000', async (t) => { }) } await db.drain() - const res = await db.query('user').get().toObject() + const res = await db.query('user').get() equal(res.length, 1_000) }) diff --git a/test/raw.ts b/test/raw.ts index 86ffbc2939..b16bb84a37 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -26,13 +26,13 @@ await test.skip('cardinality', async (t) => { .query('user', one) .include('uniqueSkills', { raw: true }) .get() - .toObject() + await db.create('user', { uniqueSkills, }) - const [a, b] = await db.query('user').get().toObject() + const [a, b] = await db.query('user').get() deepEqual(a.uniqueSkills, b.uniqueSkills) }) @@ -63,7 +63,7 @@ await test('string', async (t) => { .query('user', one) .include(['name', 'role', 'resume'], { raw: true }) .get() - .toObject() + await db.create('user', { name, @@ -71,7 +71,7 @@ await test('string', async (t) => { resume, }) - const [a, b] = await db.query('user').get().toObject() + const [a, b] = await db.query('user').get() deepEqual(a.name, b.name) deepEqual(a.role, b.role) deepEqual(a.resume, b.resume) diff --git a/test/references/references.ts b/test/references/references.ts index 9a0d161b46..07a0339c53 100644 --- a/test/references/references.ts +++ b/test/references/references.ts @@ -738,7 +738,7 @@ await test('filter', async (t) => { // }) // console.dir( -// await db.query('contestant').include('*', '**').get().toObject(), +// await db.query('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -747,7 +747,7 @@ await test('filter', async (t) => { // const contestant1 = await db.create('contestant') // console.dir( -// await db.query('contestant').include('*', '**').get().toObject(), +// await db.query('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -756,7 +756,7 @@ await test('filter', async (t) => { // const country1 = await db.create('country', { name: 'xxx' }) // console.dir( -// await db.query('contestant').include('*', '**').get().toObject(), +// await db.query('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -768,7 +768,7 @@ await test('filter', async (t) => { // .query('contestant', contestant1) // .include('*', '**') // .get() -// .toObject(), +// , // ) // await db.update('contestant', contestant1, { @@ -778,11 +778,11 @@ await test('filter', async (t) => { // console.log( // '--->', -// await db.query('country', country1).include('*', '**').get().toObject(), +// await db.query('country', country1).include('*', '**').get(), // ) // console.dir( -// await db.query('contestant').include('*', '**').get().toObject(), +// await db.query('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -803,7 +803,7 @@ await test('filter', async (t) => { // }) // .include('*', '**') // .get() -// .toObject(), +// , // ) // }) diff --git a/test/references/referencesIndex.ts b/test/references/referencesIndex.ts index a1a49d6bf7..82866c961d 100644 --- a/test/references/referencesIndex.ts +++ b/test/references/referencesIndex.ts @@ -41,7 +41,7 @@ await test('references modify', async (t) => { await db.drain() deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -75,7 +75,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -102,7 +102,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -130,7 +130,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -155,7 +155,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -175,7 +175,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -191,7 +191,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', @@ -250,7 +250,7 @@ await test('index>len', async (t) => { }) deepEqual( - (await db.query('user', john).include('*', 'friends').get()).toObject(), + (await db.query('user', john).include('*', 'friends').get()), { id: 3, name: 'john', diff --git a/test/references/referencesModify.ts b/test/references/referencesModify.ts index f25805c4df..8881b9e561 100644 --- a/test/references/referencesModify.ts +++ b/test/references/referencesModify.ts @@ -51,7 +51,7 @@ await test('references modify', async (t) => { await db.drain() deepEqual( - (await db.query('user').include('*', 'friends').get()).toObject(), + (await db.query('user').include('*', 'friends').get()), [ { id: 1, name: 'bob', friends: [] }, { id: 2, name: 'marie', friends: [{ id: 3, name: 'john' }] }, @@ -69,7 +69,7 @@ await test('references modify', async (t) => { await db.drain() deepEqual( - (await db.query('user').include('*', 'friends').get()).toObject(), + (await db.query('user').include('*', 'friends').get()), [ { id: 1, name: 'bob', friends: [{ id: 3, name: 'john' }] }, { id: 2, name: 'marie', friends: [{ id: 3, name: 'john' }] }, @@ -90,7 +90,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user').include('*', 'friends').get()).toObject(), + (await db.query('user').include('*', 'friends').get()), [ { id: 1, name: 'bob', friends: [] }, { id: 2, name: 'marie', friends: [] }, @@ -201,7 +201,7 @@ await test('reference move', async (t) => { }) deepEqual( - (await db.query('a').include('bees').get()).toObject()[0].bees[0].id, + (await db.query('a').include('bees').get())[0].bees[0].id, 2, ) @@ -209,11 +209,11 @@ await test('reference move', async (t) => { bees: [b2, b2], }) deepEqual( - (await db.query('a').include('bees').get()).toObject()[0].bees.length, + (await db.query('a').include('bees').get())[0].bees.length, 1, ) deepEqual( - (await db.query('a').include('bees').get()).toObject()[0].bees[0].id, + (await db.query('a').include('bees').get())[0].bees[0].id, 2, ) }) diff --git a/test/save/save.ts b/test/save/save.ts index ae8dbf76d1..006ab78906 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -90,8 +90,8 @@ await test('simple', async (t) => { hooks: getDefaultHooks(db2.server), }) - const a = await client.query('user').get().toObject() - const b = await client2.query('user').get().toObject() + const a = await client.query('user').get() + const b = await client2.query('user').get() deepEqual(b, a) const c = await client.create('user', { name: 'jerp' }) @@ -189,8 +189,8 @@ await test('refs', async (t) => { t.after(() => db2.destroy()) await db2.start() - const users1 = await db.query('user').include('group').get().toObject() - const users2 = await db2.query('user').include('group').get().toObject() + const users1 = await db.query('user').include('group').get() + const users2 = await db2.query('user').include('group').get() deepEqual(users1, users2) }) @@ -282,8 +282,8 @@ await test('text', async (t) => { t.after(() => db2.destroy()) await db2.start() - const articles1 = await db.query('article').get().toObject() - const articles2 = await db2.query('article').get().toObject() + const articles1 = await db.query('article').get() + const articles2 = await db2.query('article').get() deepEqual(articles1, articles2) }) diff --git a/test/save/saveInterval.ts b/test/save/saveInterval.ts index 93e6397628..debbaddcb0 100644 --- a/test/save/saveInterval.ts +++ b/test/save/saveInterval.ts @@ -36,7 +36,7 @@ await test('saveInterval', async (t) => { await client.drain() await setTimeout(1e3) - const res1 = await client.query('user').get().toObject() + const res1 = await client.query('user').get() await db.stop(true) @@ -50,7 +50,7 @@ await test('saveInterval', async (t) => { }) await db2.schemaIsSet() - const res2 = await client2.query('user').get().toObject() + const res2 = await client2.query('user').get() deepEqual(res1, res2) }) diff --git a/test/scenarios/northwind.ts b/test/scenarios/northwind.ts index 4429d24843..37a9f54c02 100644 --- a/test/scenarios/northwind.ts +++ b/test/scenarios/northwind.ts @@ -924,7 +924,7 @@ await test('Basic SQL', async (t) => { // SELECT customer_id AS ID, company_name AS customer FROM customers; const r24 = ( - await db.query('customers').include('companyName').get().toObject() + await db.query('customers').include('companyName').get() ).map((r) => ({ id: r.id, customer: r.companyName })) deepEqual( r24, @@ -1035,13 +1035,13 @@ await test('Basic SQL', async (t) => { .include('contactName', 'city', 'country') .range(0, 2) .get() - .toObject() + const r25unionB = await db .query('suppliers') .include('contactName', 'city', 'country') .range(0, 2) .get() - .toObject() + const r25union = [ ...r25unionA.map((r) => ({ type: 'customer', ...r })), ...r25unionB.map((r) => ({ type: 'supplier', ...r })), @@ -1093,13 +1093,13 @@ await test('Basic SQL', async (t) => { .include('city', 'country') .range(0, 3) .get() - .toObject() + const r26unionAllB = await db .query('suppliers') .include('city', 'country') .range(0, 3) .get() - .toObject() + const r26unionAll = [ ...r26unionAllA.map(({ city, country }) => ({ city, country })), ...r26unionAllB.map(({ city, country }) => ({ city, country })), @@ -1201,7 +1201,7 @@ await test('insert and update', async (t) => { .include('id') .filter('companyName', '=', 'Cardinal') .get() - .toObject() + )[0].id, ) @@ -1415,12 +1415,12 @@ await test('full join', async (t) => { // FULL OUTER JOIN orders ON customers.customer_id=orders.customer_id // ORDER BY customers.company_name; - const customers = await db.query('customers').get().toObject() + const customers = await db.query('customers').get() const orders = await db .query('orders') .include('customer.id') .get() - .toObject() + const result: any[] = [] // LEFT JOIN: Customers with Orders @@ -1488,7 +1488,7 @@ await test('self join', async (t) => { .query('customers') .include('customerId', 'companyName', 'city') .get() - .toObject()) as { + ) as { id: number customerId: string city: string diff --git a/test/scenarios/nycTaxi.ts b/test/scenarios/nycTaxi.ts index 64055b2385..7e8672f5fe 100644 --- a/test/scenarios/nycTaxi.ts +++ b/test/scenarios/nycTaxi.ts @@ -480,22 +480,22 @@ await test.skip('taxi', async (t) => { .query('vendor', { vendorId: trip.VendorID }) .include('id') .get() - .toObject() + const { id: rate = null } = await db .query('rate', { rateCodeId: trip.RatecodeID ?? '99' }) .include('id') .get() - .toObject() + const { id: pickupLoc = null } = await db .query('zone', { locationId: trip.PULocationID ?? '264' }) .include('id') .get() - .toObject() + const { id: dropoffLoc = null } = await db .query('zone', { locationId: trip.DOLocationID ?? '264' }) .include('id') .get() - .toObject() + const pickup = new Date(trip.tpep_pickup_datetime) const dropoff = new Date(trip.tpep_dropoff_datetime) @@ -596,7 +596,7 @@ await test.skip('taxi', async (t) => { // .get(), // ), // ) - // res.map((r) => r.toObject()) + // res.map((r) => r) // Yearly/Monthly/Daily revenue console.log('Yearly/Monthly/Daily revenue') @@ -647,13 +647,13 @@ await test.skip('taxi', async (t) => { .groupBy('pickup', { step: 'dow', timeZone: 'America/New_York' }) .count() .get() - .toObject() + const rh2 = await db .query('trip') .groupBy('pickup', { step: 'dow', timeZone: 'America/New_York' }) .count() .get() - .toObject() + console.log( Object.keys(day2enum).reduce( (prev, key) => ( diff --git a/test/scenarios/vote.ts b/test/scenarios/vote.ts index b2656e0234..71d9769ca4 100644 --- a/test/scenarios/vote.ts +++ b/test/scenarios/vote.ts @@ -160,7 +160,7 @@ await test('schema with many uint8 fields', async (t) => { t.include(['status']) }) .get() - const r = rdyForConfirmationToken.toObject().payments + const r = rdyForConfirmationToken.payments for (const payment of r) { db.update('payment', payment.id, { status: 'ReadyForConfirmationToken', @@ -180,7 +180,7 @@ await test('schema with many uint8 fields', async (t) => { }) .get() - const r = rdyForPaymentIntent.toObject().payments + const r = rdyForPaymentIntent.payments for (const payment of r) { db.update('payment', payment.id, { status: 'ReadyForPaymentIntent', diff --git a/test/scenarios/voteLargeAmounts.perf.ts b/test/scenarios/voteLargeAmounts.perf.ts index ddfb741e65..36ed546815 100644 --- a/test/scenarios/voteLargeAmounts.perf.ts +++ b/test/scenarios/voteLargeAmounts.perf.ts @@ -158,7 +158,7 @@ await test('schema with many uint8 fields', async (t) => { // ).execTime.toFixed(2), // 'ms', //) - const n = cnt.toObject().count + const n = cnt.count const grp = await db .query('vote') .groupBy('fromCountry') diff --git a/test/scenarios/voteStorage.ts b/test/scenarios/voteStorage.ts index 37765d1139..259a622038 100644 --- a/test/scenarios/voteStorage.ts +++ b/test/scenarios/voteStorage.ts @@ -188,7 +188,7 @@ const testVotes = (opts: { votes: any; amount: number }) => { }) deepEqual( - (await db.query('round', final).include('votes').get().toObject()).votes + (await db.query('round', final).include('votes').get()).votes .length, 0, 'clear refs', @@ -209,7 +209,7 @@ const testVotes = (opts: { votes: any; amount: number }) => { .range(0, 1e6) .include('id') .get() - .toObject() + let i = votes.length - 1 for (i = 0; i < votes.length; i++) { db.delete('vote', votes[i].id) diff --git a/test/serializeQueryDef.ts b/test/serializeQueryDef.ts index 6a8d64cc97..556796b219 100644 --- a/test/serializeQueryDef.ts +++ b/test/serializeQueryDef.ts @@ -30,22 +30,22 @@ await test('serialize', async (t) => { await db.drain() - deepEqual((await db.query('user').get()).toObject(), [ + deepEqual((await db.query('user').get()), [ { id: 1, isNice: false }, { id: 2, isNice: true }, { id: 3, isNice: false }, ]) deepEqual( - (await db.query('user').filter('isNice', '=', true).get()).toObject(), + (await db.query('user').filter('isNice', '=', true).get()), [{ id: 2, isNice: true }], ) - deepEqual((await db.query('user').filter('isNice').get()).toObject(), [ + deepEqual((await db.query('user').filter('isNice').get()), [ { id: 2, isNice: true }, ]) - deepEqual((await db.query('user').filter('isNice', false).get()).toObject(), [ + deepEqual((await db.query('user').filter('isNice', false).get()), [ { id: 1, isNice: false }, { id: 3, isNice: false }, ]) diff --git a/test/shared/assert.ts b/test/shared/assert.ts index ac82359254..e5952ff629 100644 --- a/test/shared/assert.ts +++ b/test/shared/assert.ts @@ -11,10 +11,10 @@ export const deepEqual = ( msg?: string, ) => { if (a instanceof BasedQueryResponse) { - a = a.toObject() + a = a } if (b instanceof BasedQueryResponse) { - b = b.toObject() + b = b } if (!uDeepEqual(a, b)) { const m = `${msg || ``} diff --git a/test/shared/index.ts b/test/shared/index.ts index 25af021451..4822d388f5 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,7 +1,7 @@ import { createHash } from 'node:crypto' import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' import type { ResolveSchema, SchemaIn, StrictSchema } from '../../src/schema.js' -import { BasedDb, DbClient, DbServer, getDefaultHooks } from '../../src/sdk.js' +import { DbClient, DbServer, getDefaultHooks } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' export * from './examples.js' diff --git a/test/shared/test.ts b/test/shared/test.ts index bbb1dd83de..fc9a480a4d 100644 --- a/test/shared/test.ts +++ b/test/shared/test.ts @@ -1,9 +1,14 @@ import { styleText } from 'node:util' import { fileURLToPath } from 'url' import { join, dirname, resolve } from 'path' -import { BasedDb, DbClient, DbServer, getDefaultHooks } from '../../src/index.js' +import { + BasedDb, + DbClient, + DbServer, + getDefaultHooks, +} from '../../src/index.js' import { deepEqual } from './assert.js' -import { wait, bufToHex } from '../../src/utils/index.js' +import { wait } from '../../src/utils/index.js' import fs from 'node:fs/promises' export const counts = { @@ -97,7 +102,10 @@ const test: { console.log(styleText('gray', `saved db ${performance.now() - d} ms`)) const size = await dirSize(t.tmp) - const strSize = size < 1_048_576 ? `${Math.ceil(size / 1024)} kiB` : `${Math.ceil(size / 1_048_576)} MiB` + const strSize = + size < 1_048_576 + ? `${Math.ceil(size / 1024)} kiB` + : `${Math.ceil(size / 1_048_576)} MiB` console.log(styleText('gray', `backup size ${strSize}`)) await db.stop() diff --git a/test/simpleQuery.ts b/test/simpleQuery.ts index c1643eae61..16bac8188d 100644 --- a/test/simpleQuery.ts +++ b/test/simpleQuery.ts @@ -40,7 +40,7 @@ await test('query', async (t) => { await db.drain() deepEqual( - (await db.query('user').include('id').get()).toObject(), + (await db.query('user').include('id').get()), [{ id: 1 }], 'Id only', ) @@ -48,13 +48,13 @@ await test('query', async (t) => { deepEqual( ( await db.query('user').filter('age', '<', 20).include('id', 'age').get() - ).toObject(), + ), [], ) deepEqual( - (await db.query('user').include('*').get()).toObject(), - (await db.query('user').get()).toObject(), + (await db.query('user').include('*').get()), + (await db.query('user').get()), 'include * works as "get all fields"', ) }) diff --git a/test/singleRef.ts b/test/singleRef.ts index 5cb1acbba8..3e6702b134 100644 --- a/test/singleRef.ts +++ b/test/singleRef.ts @@ -208,7 +208,7 @@ await test('simple nested', async (t) => { await db.drain() - deepEqual((await db.query('blup').include('flap').get()).toObject(), [ + deepEqual((await db.query('blup').include('flap').get()), [ { id: 1, flap: 'B', @@ -227,7 +227,7 @@ await test('simple nested', async (t) => { equal(r.user.myBlup.flap, 'B') } - deepEqual((await db.query('user').include('simple').get()).toObject(), [ + deepEqual((await db.query('user').include('simple').get()), [ { id: 1, simple: { id: 1 }, @@ -240,14 +240,14 @@ await test('simple nested', async (t) => { await db.drain() - deepEqual((await db.query('simple').include('user').get()).toObject(), [ + deepEqual((await db.query('simple').include('user').get()), [ { id: 1, user: null, }, ]) - deepEqual((await db.query('user').include('simple').get()).toObject(), [ + deepEqual((await db.query('user').include('simple').get()), [ { id: 1, simple: null, @@ -321,7 +321,7 @@ await test('single reference object', async (t) => { await db.drain() - deepEqual((await db.query('simple').include('admin.user').get()).toObject(), [ + deepEqual((await db.query('simple').include('admin.user').get()), [ { id: 1, admin: { @@ -430,12 +430,12 @@ await test('nested', async (t) => { await db.drain() deepEqual( - (await db.query('simple').include('id').range(0, 1).get()).toObject(), + (await db.query('simple').include('id').range(0, 1).get()), [{ id: 1 }], ) deepEqual( - (await db.query('simple').include('user').range(0, 1).get()).toObject(), + (await db.query('simple').include('user').range(0, 1).get()), [ { id: 1, @@ -448,7 +448,7 @@ await test('nested', async (t) => { deepEqual( ( await db.query('simple', lastRes).include('user.location').get() - ).toObject(), + ), { id: await lastRes, user: { @@ -460,7 +460,7 @@ await test('nested', async (t) => { ) deepEqual( - (await db.query('simple', lastRes).include('user').get()).toObject(), + (await db.query('simple', lastRes).include('user').get()), { id: await lastRes, user: { @@ -484,7 +484,7 @@ await test('nested', async (t) => { .include('user.myBlup') .range((await lastRes!) - 1, await lastRes) .get() - ).toObject(), + ), [ { id: await lastRes, @@ -495,7 +495,7 @@ await test('nested', async (t) => { ) deepEqual( - (await db.query('simple', lastRes).include('user.myBlup').get()).toObject(), + (await db.query('simple', lastRes).include('user.myBlup').get()), { id: await lastRes, user: { id: 1, myBlup: { id: 1, flap: 'A', name: 'blup !' } }, @@ -506,7 +506,7 @@ await test('nested', async (t) => { deepEqual( ( await db.query('simple', lastRes).include('user.myBlup', 'lilBlup').get() - ).toObject(), + ), { id: await lastRes, user: { id: 1, myBlup: { id: 1, flap: 'A', name: 'blup !' } }, @@ -529,7 +529,7 @@ await test('nested', async (t) => { .include('user.myBlup', 'lilBlup', 'user.name') .range((await lastRes!) - 1, await lastRes) .get() - ).toObject(), + ), [ { id: await lastRes, @@ -547,14 +547,14 @@ await test('nested', async (t) => { deepEqual( ( await db.query('simple', lastRes).include('user.location.label').get() - ).toObject(), + ), { id: await lastRes, user: { id: 1, location: { label: 'BLA BLA' } } }, ) deepEqual( ( await db.query('simple', lastRes).include('user.location').get() - ).toObject(), + ), { id: await lastRes, user: { id: 1, location: { label: 'BLA BLA', x: 1, y: 2 } }, @@ -568,7 +568,7 @@ await test('nested', async (t) => { .include('user.myBlup', 'lilBlup') .range((await lastRes!) - 1, await lastRes) .get() - ).toObject(), + ), [ { id: await lastRes, @@ -596,7 +596,7 @@ await test('nested', async (t) => { .include('user', 'user.myBlup') .range((await lastRes!) - 1, await lastRes) .get() - ).toObject(), + ), [ { id: await lastRes, @@ -622,7 +622,7 @@ await test('nested', async (t) => { .query('simple', lastRes) .include('user', 'user.myBlup', 'lilBlup') .get() - ).toObject(), + ), { id: await lastRes, user: { @@ -728,7 +728,7 @@ await test('single reference multi refs strings', async (t) => { .include('user', 'user.myBlup', 'lilBlup') .get() - deepEqual(result2.toObject(), [ + deepEqual(result2, [ { id: 2, user: null, diff --git a/test/singleRefQuery.ts b/test/singleRefQuery.ts index 32f678a6be..3a06df2db1 100644 --- a/test/singleRefQuery.ts +++ b/test/singleRefQuery.ts @@ -114,7 +114,7 @@ await test('single reference query', async (t) => { .filter('user.myBlup.age', '=', 10) .get() - deepEqual(result2.toObject(), [ + deepEqual(result2, [ { id: 1, smurp: 0, @@ -131,7 +131,7 @@ await test('single reference query', async (t) => { .include('lilBlup', 'flap') .get() - deepEqual(result.toObject(), [ + deepEqual(result, [ { id: 4, lilBlup: { diff --git a/test/sort/sort.ts b/test/sort/sort.ts index 93b1eb35f3..639d20b417 100644 --- a/test/sort/sort.ts +++ b/test/sort/sort.ts @@ -523,7 +523,7 @@ await test('unset value on create', async (t) => { }, }) - await db.query('dialog').sort('fun', 'desc').get().toObject() + await db.query('dialog').sort('fun', 'desc').get() const id1 = await db.create('dialog', { fun: '1', diff --git a/test/sort/sortHll.ts b/test/sort/sortHll.ts index ec267131d4..b19a784842 100644 --- a/test/sort/sortHll.ts +++ b/test/sort/sortHll.ts @@ -61,7 +61,7 @@ await test('sortCardinality', async (t) => { .sort('brazilians', 'desc') .include('count', 'brazilians') .get() - ).toObject(), + ), [ { id: 1, @@ -80,7 +80,7 @@ await test('sortCardinality', async (t) => { deepEqual( ( await db.query('article').sort('count', 'asc').include('derp').get() - ).toObject(), + ), [ { id: 2, @@ -107,7 +107,7 @@ await test('sortCardinality', async (t) => { .sort('count', 'asc') .include('count', 'brazilians') .get() - ).toObject(), + ), [ { id: 2, @@ -154,7 +154,7 @@ await test('sortCardinality', async (t) => { .query('article') .filter('id', '=', testRecordId) .get() - .toObject() + const count = Math.abs(result[0].brazilians) const countError = count - num_brazos @@ -181,7 +181,7 @@ await test('sortCardinality', async (t) => { deepEqual( ( await db.query('article').sort('count', 'desc').include('count').get() - ).toObject(), + ), [ { id: 1, @@ -206,7 +206,7 @@ await test('sortCardinality', async (t) => { .sort('brazilians', 'desc') .include('derp', 'count') .get() - ).toObject(), + ), [ { id: 2, @@ -245,7 +245,7 @@ await test('sortCardinality', async (t) => { .sort('count', 'desc') .include('count') .get() - .toObject(), + , [ { id: 1008, count: 3 }, { id: 2, count: 2 }, diff --git a/test/sort/sortNodeId.ts b/test/sort/sortNodeId.ts index 1c0a6ef494..3a5ee8b6b1 100644 --- a/test/sort/sortNodeId.ts +++ b/test/sort/sortNodeId.ts @@ -34,7 +34,7 @@ await test.skip('basic sort by id', async (t) => { .sort('id', 'desc') .range(0, 5) .get() - .toObject(), + , [ { id: 99, diff --git a/test/sort/sortNumber.ts b/test/sort/sortNumber.ts index 96b8f706fa..da47c2dfc2 100644 --- a/test/sort/sortNumber.ts +++ b/test/sort/sortNumber.ts @@ -80,7 +80,7 @@ await test('numbers', async (t) => { .sort('enum') .include('enum') .get() - .then((v) => v.toObject().map((v) => v.enum)), + .then((v) => v.map((v) => v.enum)), animalsResult.sort((a, b) => animals.indexOf(a) - animals.indexOf(b)), ) db.delete('example', 1) @@ -90,5 +90,5 @@ await test('numbers', async (t) => { .query('example') .include('enum') .get() - .then((v) => v.toObject().map((v) => v.enum)) + .then((v) => v.map((v) => v.enum)) }) diff --git a/test/sort/sortString.ts b/test/sort/sortString.ts index 51ebb7c376..918ae35b7f 100644 --- a/test/sort/sortString.ts +++ b/test/sort/sortString.ts @@ -81,7 +81,7 @@ await test('compression / large strings', async (t) => { .sort('article') .range(0, len) .get() - .then((v) => v.toObject().map((v) => v.nr)), + .then((v) => v.map((v) => v.nr)), results.sort((a, b) => a.nr - b.nr).map((v) => v.nr), name, ) @@ -92,7 +92,7 @@ await test('compression / large strings', async (t) => { .sort('article', 'desc') .range(0, len) .get() - .then((v) => v.toObject().map((v) => v.nr)), + .then((v) => v.map((v) => v.nr)), results.sort((b, a) => a.nr - b.nr).map((v) => v.nr), name + ' desc', ) diff --git a/test/string.ts b/test/string.ts index 2678339de2..7583467a92 100644 --- a/test/string.ts +++ b/test/string.ts @@ -527,7 +527,7 @@ await test('string compression - max buf size', async (t) => { await db.drain() - const items = await db.query('file').get().toObject() + const items = await db.query('file').get() for (const item of items) { equal(item.contents, contents, 'contents are the same') diff --git a/test/subscription/subscription.ts b/test/subscription/subscription.ts index eb49a39709..3277d11205 100644 --- a/test/subscription/subscription.ts +++ b/test/subscription/subscription.ts @@ -194,7 +194,7 @@ await test('subscribe to refs', async (t) => { .include('items') .subscribe((q) => { updatesReceived++ - const res = q.toObject() + const res = q size = res.items.length const n = performance.now() console.log(updatesReceived, 'update received after', n - d) diff --git a/test/subscription/subscriptionSchemaChanges.ts b/test/subscription/subscriptionSchemaChanges.ts index 9e6db95e57..3699432230 100644 --- a/test/subscription/subscriptionSchemaChanges.ts +++ b/test/subscription/subscriptionSchemaChanges.ts @@ -54,7 +54,7 @@ await test('subscription schema changes', async (t) => { s('friends').include('*') }) .filter('lang', '=', 'de') - const result1 = q.get().toObject() + const result1 = q.get() await clients[0].setSchema({ types: { user: { @@ -76,7 +76,7 @@ await test('subscription schema changes', async (t) => { deepEqual(result1, q.get(), 'first schema change results are correct') const subResults: any[] = [] const close = q.subscribe((q) => { - subResults.push(q.toObject()) + subResults.push(q) cnt++ }) t.after(() => { @@ -134,7 +134,7 @@ await test('better subscription schema changes', async (t) => { const results: any[] = [] db.query('user').subscribe((res) => { - const obj = res.toObject() + const obj = res results.push(obj) }) diff --git a/test/timestamp.ts b/test/timestamp.ts index 54a17ffafa..d96e5cdc48 100644 --- a/test/timestamp.ts +++ b/test/timestamp.ts @@ -36,7 +36,7 @@ await test('timestamp', async (t) => { name: 'youzi', }) - let res = (await db.query('user').get()).toObject() + let res = (await db.query('user').get()) if (typeof res[0].createdAt !== 'number') { throw 'should be number' @@ -55,7 +55,7 @@ await test('timestamp', async (t) => { name: 'youzi1', }) - res = (await db.query('user').get()).toObject() + res = (await db.query('user').get()) if (!(res[0].updatedAt > res[0].createdAt)) { throw 'updatedAt should be updated after update' @@ -63,7 +63,7 @@ await test('timestamp', async (t) => { const measure = async (v: number) => { deepEqual( - Math.floor((await db.query('user', youzi).get().toObject()).mrDerp / 10), + Math.floor((await db.query('user', youzi).get()).mrDerp / 10), Math.floor(v / 10), ) } @@ -102,8 +102,8 @@ await test('timestamp', async (t) => { updatedAt: overwriteUpdatedAt, }) - const newUser = await db.query('user', jamex).get().toObject() - const updatedUser = await db.query('user', youzi).get().toObject() + const newUser = await db.query('user', jamex).get() + const updatedUser = await db.query('user', youzi).get() equal(newUser.createdAt, overwriteCreatedAt) equal(newUser.updatedAt, overwriteUpdatedAt) @@ -137,7 +137,7 @@ await test('timestamp before 1970', async (t) => { bday: d, }) - const res = await db.query('user', user).get().toObject() + const res = await db.query('user', user).get() equal(res.bday, d.valueOf()) }) diff --git a/test/upsert.ts b/test/upsert.ts index 7e88c9c2b6..36d9dfb91a 100644 --- a/test/upsert.ts +++ b/test/upsert.ts @@ -105,7 +105,7 @@ await test('upsert no alias', async (t) => { // await db.drain() equal( - (await db.query('lala').include('*').get().toObject()).length, + (await db.query('lala').include('*').get()).length, 0, 'before upsert', ) @@ -116,7 +116,7 @@ await test('upsert no alias', async (t) => { }) equal( - (await db.query('lala').include('*').get().toObject()).length, + (await db.query('lala').include('*').get()).length, 1, 'after upsert', ) @@ -127,7 +127,7 @@ await test('upsert no alias', async (t) => { }) equal( - (await db.query('lala').include('*').get().toObject()).length, + (await db.query('lala').include('*').get()).length, 2, 'upsert no alias should insert', ) diff --git a/test/vector.ts b/test/vector.ts index eabde79768..dc917e3dae 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -50,7 +50,7 @@ async function initDb(t: Parameters[1]>[0]): Promise { const db = await initDb(t) - const res = (await db.query('data').include('name', 'a').get()).toObject() + const res = (await db.query('data').include('name', 'a').get()) for (const r of res) { const a = new Uint8Array(r.a.buffer, 0, r.a.byteLength) const b = new Uint8Array(new Float32Array(data[r.name]).buffer) @@ -90,7 +90,7 @@ await test('query by vector', async (t) => { .include('name') .filter('a', '=', new Float32Array(data['car'].slice(0, 5))) .get() - .toObject() + deepEqual(r1[0].name, 'car') const r2 = await db @@ -98,7 +98,7 @@ await test('query by vector', async (t) => { .include('name') .filter('a', '=', new Float32Array(data['car'])) .get() - .toObject() + deepEqual(r2.length, 1) }) @@ -112,7 +112,7 @@ await test.skip('vector like', async (t) => { .include('name') .filter('a', 'like', fruit, { fn: 'euclideanDistance', score: 1 }) .get() - .toObject() + deepEqual(res, [ { id: 3, name: 'apple' }, @@ -136,7 +136,7 @@ await test.skip('vector like', async (t) => { .range(0, 1e6) .filter('a', 'like', fruit, { fn: 'euclideanDistance', score: 1 }) .get() - .toObject(), + , [ { id: 3, From 91a56a8e2e115fe0d3c299c52973ec1e34d9e9aa Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 15:35:35 +0100 Subject: [PATCH 417/449] replace query with query2 --- test/HLLunion.ts | 16 +- test/aggregate/dev.ts | 6 +- test/alignModify.ts | 2 +- test/based-client/addSpecs.ts | 2 +- test/based-client/authorize.ts | 8 +- test/based-client/authorizeOnSpec.ts | 2 +- test/based-client/browser/index.ts | 4 +- test/based-client/dbQuery.ts | 8 +- test/based-client/error.ts | 4 +- test/based-client/get.ts | 36 +- test/based-client/hooks.ts | 10 +- test/based-client/lazyConnect.ts | 2 +- test/based-client/messages.ts | 2 +- test/based-client/nestedFunctions.ts | 24 +- test/based-client/nestedFunctionsError.ts | 4 +- test/based-client/nestedQuerySimple.ts | 6 +- test/based-client/null.ts | 8 +- test/based-client/payloadPerf.ts | 4 +- test/based-client/persist.ts | 8 +- test/based-client/protocolContentType.ts | 30 +- test/based-client/query.ts | 4 +- test/based-client/queryCache.ts | 4 +- test/based-client/queryCtxBound.ts | 40 ++- test/based-client/queryDiff.ts | 2 +- test/based-client/queryErrorHandling.ts | 18 +- test/based-client/queryInstancePerf.ts | 2 +- test/based-client/queryReusedDiff.ts | 4 +- test/based-client/queryUint8Payload.ts | 6 +- test/based-client/reEvaluateAuthState.ts.ts | 2 +- test/based-client/relay.ts | 4 +- test/based-client/ssr.ts | 6 +- test/based-client/throttle.ts | 2 +- test/bench.perf.ts | 10 +- test/bigNode.perf.ts | 12 +- test/binary.ts | 13 +- test/boolean.ts | 8 +- test/cardinality.ts | 110 +++--- test/clientServer.perf.ts | 8 +- test/clientServer.ts | 6 +- test/concurrency.perf.ts | 4 +- test/copy.ts | 3 +- test/crc32c.ts | 21 +- test/db-schema/schemaDebug.ts | 2 +- test/db-schema/schemaProblems.ts | 20 +- test/delete.perf.ts | 6 +- test/delete.ts | 20 +- test/enum.ts | 12 +- test/errors.ts | 6 +- test/filter/api.ts | 8 +- test/filter/edges.ts | 10 +- test/filter/filter.ts | 320 ++++++++---------- test/filter/or.ts | 8 +- test/filter/references.ts | 4 +- test/filter/referencesField.ts | 12 +- test/filter/string.ts | 81 +++-- test/hooks.ts | 36 +- test/idOffset.ts | 2 +- test/include/include.ts | 12 +- test/include/includeMeta.ts | 32 +- test/include/includeNested.ts | 4 +- test/include/includeSlice.ts | 17 +- test/include/referencesField.ts | 10 +- test/include/thread.perf.ts | 12 +- test/insertOnly.ts | 2 +- test/instantModify.ts | 4 +- test/isModified.perf.ts | 2 +- test/json.ts | 10 +- test/locales.ts | 4 +- test/mainAndEmptyStringFieldDelete.ts | 2 +- test/mem.ts | 7 +- test/migration.ts | 6 +- test/query-ast/aggregates.ts | 2 +- test/queryResponse.ts | 4 +- test/range.ts | 6 +- test/raw.ts | 10 +- test/references/references.ts | 58 ++-- test/references/referencesIndex.ts | 156 ++++----- test/references/referencesModify.ts | 23 +- test/save/blockHash.ts | 4 +- test/save/save.ts | 46 +-- test/save/saveEdge.ts | 2 +- test/save/saveInterval.ts | 4 +- test/save/saveRange.ts | 10 +- test/scenarios/e-commerce.ts | 14 +- test/scenarios/northwind.ts | 122 ++++--- test/scenarios/nycTaxi.ts | 57 ++-- test/scenarios/vote.ts | 8 +- test/scenarios/voteEdges.ts | 4 +- test/scenarios/voteLargeAmounts.perf.ts | 12 +- test/scenarios/voteStorage.ts | 13 +- test/search.ts | 54 +-- test/serializeQueryDef.ts | 15 +- test/shared/playground.ts | 2 +- test/shared/test.ts | 6 +- test/simpleQuery.ts | 14 +- test/singleRef.ts | 122 +++---- test/singleRefQuery.ts | 4 +- test/sort/sort.perf.ts | 2 +- test/sort/sort.ts | 59 ++-- test/sort/sortAlias.ts | 8 +- test/sort/sortBinary.ts | 10 +- test/sort/sortById.ts | 6 +- test/sort/sortEnum.ts | 12 +- test/sort/sortHll.ts | 54 ++- test/sort/sortIds.ts | 14 +- test/sort/sortNodeId.ts | 5 +- test/sort/sortNumber.ts | 22 +- test/sort/sortString.ts | 6 +- test/sort/sortTimestamp.ts | 16 +- test/string.ts | 40 +-- test/subscription/subscription.perf.ts | 4 +- test/subscription/subscription.ts | 10 +- test/subscription/subscriptionId.ts | 8 +- test/subscription/subscriptionIdPartial.ts | 6 +- test/subscription/subscriptionIdRemove.ts | 2 +- test/subscription/subscriptionMulti.perf.ts | 2 +- test/subscription/subscriptionNow.ts | 6 +- .../subscription/subscriptionSchemaChanges.ts | 4 +- test/subscription/subscriptionWorkers.perf.ts | 2 +- test/text/textFallback.ts | 6 +- test/text/textFilter.ts | 9 +- test/text/textMany.ts | 2 +- test/validation/validation.ts | 184 +++++----- test/validation/validationReferences.ts | 10 +- test/youzi.ts | 2 +- 125 files changed, 1150 insertions(+), 1279 deletions(-) diff --git a/test/HLLunion.ts b/test/HLLunion.ts index 04f3b72b5f..a2bf0eeaf5 100644 --- a/test/HLLunion.ts +++ b/test/HLLunion.ts @@ -83,7 +83,7 @@ await test.skip('dev', async (t) => { // OK await db // dont break line - .query('user') + .query2('user') .include('**') .groupBy('country') .sum('name') @@ -93,7 +93,7 @@ await test.skip('dev', async (t) => { // OK // await db // // dont break line - // .query('user') + // .query2('user') // .groupBy('name') // .sum('flap') // .get() @@ -101,7 +101,7 @@ await test.skip('dev', async (t) => { // TODO: display is tagging "sum" when count with alias // TODO: also there os a misplaced comma in inspect // await db - // .query('article') + // .query2('article') // .include((q) => q('contributors').count('votes'), 'name') // .get() // .inspect() @@ -194,14 +194,14 @@ await test.skip('dev', async (t) => { // // TODO: display is tagging "sum" when count with alias // // TODO: also there os a misplaced comma in inspect // // await db -// // .query('article') +// // .query2('article') // // .include((q) => q('contributors').count('votes'), 'name') // // .get() // // .inspect() // // deepEqual( // // await db -// // .query('article') +// // .query2('article') // // .include((q) => q('contributors').sum('flap'), 'name') // // .get() // // , @@ -218,7 +218,7 @@ await test.skip('dev', async (t) => { // await db // // dont break line -// .query('user') +// .query2('user') // .groupBy('country') // .cardinality('myUniqueValuesCount') // .get() @@ -228,14 +228,14 @@ await test.skip('dev', async (t) => { // // const q = await db // // // dont break line -// // .query('users') +// // .query2('users') // // .get() // // q.inspect() // // await db // // // dont break line -// // .query('user') +// // .query2('user') // // .groupBy('name') // // .sum('flap') // // .get() diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index b857e89f6b..5e7c68b6f7 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -309,7 +309,7 @@ await test('yyy', async (t) => { } await db - .query('product') + .query2('product') // .include('*') .avg('flap') .groupBy('name') @@ -320,7 +320,7 @@ await test('yyy', async (t) => { // Number( // Object.keys( // await db - // .query('product') + // .query2('product') // .include('*') // .avg('flap') // .groupBy('name') @@ -336,7 +336,7 @@ await test('yyy', async (t) => { // Number( // Object.keys( // await db - // .query('shelve') + // .query2('shelve') // .include((q) => q('products').avg('flap').groupBy('name')) // .get() // .toObject(), diff --git a/test/alignModify.ts b/test/alignModify.ts index a5f7dedb6f..8f493bbe80 100644 --- a/test/alignModify.ts +++ b/test/alignModify.ts @@ -42,7 +42,7 @@ await test('alignModify - putrefs', async (t) => { }) } await db.drain() - const res = await db.query('user').include('friends', 'str').get() + const res = await db.query2('user').include('friends', 'str').get() deepEqual(res, [ { id: 1, diff --git a/test/based-client/addSpecs.ts b/test/based-client/addSpecs.ts index 77c5b3067a..a0d308d7f9 100644 --- a/test/based-client/addSpecs.ts +++ b/test/based-client/addSpecs.ts @@ -54,7 +54,7 @@ test('addSpecs', async (t: T) => { let errCnt = 0 // let msgCnt = 0 - client.query('cookie').subscribe( + client.query2('cookie').subscribe( () => {}, () => { errCnt++ diff --git a/test/based-client/authorize.ts b/test/based-client/authorize.ts index a5cb5aaee0..6d01550209 100644 --- a/test/based-client/authorize.ts +++ b/test/based-client/authorize.ts @@ -136,7 +136,7 @@ test('authorize observe', async (t: T) => { await new Promise((resolve) => { client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe( @@ -153,7 +153,7 @@ test('authorize observe', async (t: T) => { await new Promise((resolve) => { client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe( @@ -194,7 +194,7 @@ test('authorize after observe', async (t: T) => { let receiveCnt = 0 client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe( @@ -239,7 +239,7 @@ test('authorize from server after observe', async (t: T) => { let receiveCnt = 0 client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe( diff --git a/test/based-client/authorizeOnSpec.ts b/test/based-client/authorizeOnSpec.ts index 24b73c026a..6fcf66eae4 100644 --- a/test/based-client/authorizeOnSpec.ts +++ b/test/based-client/authorizeOnSpec.ts @@ -91,7 +91,7 @@ test('Specific authorize on spec', async (t: T) => { t.is(authCalled, 1) - await client.query('slax').get() + await client.query2('slax').get() t.is(authCalled, 2) client.channel('klax').subscribe(() => {}) diff --git a/test/based-client/browser/index.ts b/test/based-client/browser/index.ts index 7faea2361b..f15ffe1be0 100644 --- a/test/based-client/browser/index.ts +++ b/test/based-client/browser/index.ts @@ -75,7 +75,7 @@ export const app = () => { client.on('connect', (v) => { log.innerHTML += `

` }) - client.query('counter').subscribe( + client.query2('counter').subscribe( (d) => { log.innerHTML = `cnt: ${d}` }, @@ -86,7 +86,7 @@ export const app = () => { const text = document.createElement('pre') body.appendChild(text) - client.query('text').subscribe( + client.query2('text').subscribe( (d) => { text.innerHTML = d.join('\n') }, diff --git a/test/based-client/dbQuery.ts b/test/based-client/dbQuery.ts index 20ff1fbd0a..573fcd6b10 100644 --- a/test/based-client/dbQuery.ts +++ b/test/based-client/dbQuery.ts @@ -29,13 +29,13 @@ test('db query', async (t: T) => { type: 'query', uninstallAfterIdleTime: 1e3, fn: (_, __, update) => { - return db.query('user').subscribe(update) + return db.query2('user').subscribe(update) }, }, getUser: { type: 'function', async fn() { - return db.query('user').get() + return db.query2('user').get() }, }, }, @@ -70,8 +70,8 @@ test('db query', async (t: T) => { const res = await client.call('getUser') const resOld = await clientOld.call('getUser') - client.query('users').subscribe((res) => nextResolve?.(res)) - clientOld.query('users').subscribe((res) => nextResolveOld?.(res)) + client.query2('users').subscribe((res) => nextResolve?.(res)) + clientOld.query2('users').subscribe((res) => nextResolveOld?.(res)) const [resQuery, resQueryOld] = await Promise.all([ new Promise((r) => (nextResolve = r)), new Promise((r) => (nextResolveOld = r)), diff --git a/test/based-client/error.ts b/test/based-client/error.ts index 2d50ffe21f..29905f1262 100644 --- a/test/based-client/error.ts +++ b/test/based-client/error.ts @@ -137,7 +137,7 @@ test('observable authorize error', async (t: T) => { // TODO: Check error instance of const error = (await new Promise((resolve) => { - coreClient.query('counter', {}).subscribe( + coreClient.query2('counter', {}).subscribe( (v) => {}, (err) => { @@ -174,7 +174,7 @@ test('throw in an interval', async (t: T) => { }) await t.throwsAsync( new Promise((_, reject) => - coreClient.query('errorTimer', {}).subscribe(() => {}, reject), + coreClient.query2('errorTimer', {}).subscribe(() => {}, reject), ), ) }) diff --git a/test/based-client/get.ts b/test/based-client/get.ts index 36d49982d3..f8fe85bf3c 100644 --- a/test/based-client/get.ts +++ b/test/based-client/get.ts @@ -33,7 +33,7 @@ const setup = async (t: T) => { type: 'function', uninstallAfterIdleTime: 1e3, fn: async (based, payload) => { - const bla = await based.query('any', payload).get() + const bla = await based.query2('any', payload).get() return bla }, }, @@ -80,7 +80,7 @@ const setup = async (t: T) => { type: 'function', uninstallAfterIdleTime: 1e3, fn: async (based, payload) => { - return based.query('checkPayload', payload).get() + return based.query2('checkPayload', payload).get() }, }, }, @@ -105,14 +105,14 @@ test('get while subscribed', async (t: T) => { }) const res0 = await new Promise((resolve) => { - coreClient.query('any', 'xxx').subscribe((res) => { + coreClient.query2('any', 'xxx').subscribe((res) => { resolve(res) }) }) t.is(res0, 'xxx') - const res1 = await coreClient.query('any', 'xxx').get() + const res1 = await coreClient.query2('any', 'xxx').get() t.is(res1, res0) - const res2 = await coreClient.query('any', 'xxx').get() + const res2 = await coreClient.query2('any', 'xxx').get() t.is(res2, res1) }) @@ -130,25 +130,25 @@ test('get', async (t: T) => { }, }) - const str = await coreClient.query('any', 'xxx').get() + const str = await coreClient.query2('any', 'xxx').get() t.is(str, 'xxx') const nestedStr = await coreClient.call('nestedAny', 'xxx') t.is(nestedStr, 'xxx') - const num = await coreClient.query('any', 19).get() + const num = await coreClient.query2('any', 19).get() t.is(num, 19) const nestedNum = await coreClient.call('nestedAny', 19) t.is(nestedNum, 19) - const boolTrue = await coreClient.query('any', true).get() + const boolTrue = await coreClient.query2('any', true).get() t.is(boolTrue, true) const nestedBoolTrue = await coreClient.call('nestedAny', true) t.is(nestedBoolTrue, true) - const boolFalse = await coreClient.query('any', false).get() + const boolFalse = await coreClient.query2('any', false).get() t.is(boolFalse, false) const nestedBoolFalse = await coreClient.call('nestedAny', false) t.is(nestedBoolFalse, false) const power = await coreClient - .query('checkPayload', { + .query2('checkPayload', { power: { msg: 'powerfull stuff', }, @@ -169,24 +169,24 @@ test('get', async (t: T) => { await wait(1e3) - t.is(await coreClient.query('counter').get(), 0) + t.is(await coreClient.query2('counter').get(), 0) await wait(100) - t.is(await coreClient.query('counter').get(), 0) + t.is(await coreClient.query2('counter').get(), 0) await wait(1000) // stays zero because it has 0 cache time - t.is(await coreClient.query('counter').get(), 0) + t.is(await coreClient.query2('counter').get(), 0) await wait(1000) t.is(Object.keys(server.activeObservables).length, 0) t.is(server.activeObservablesById.size, 0) - t.is(await coreClient.query('counter-cached').get(), 0) - t.is(await coreClient.query('counter-cached').get(), 0) + t.is(await coreClient.query2('counter-cached').get(), 0) + t.is(await coreClient.query2('counter-cached').get(), 0) await wait(1500) @@ -219,14 +219,14 @@ test.only('authorize get', async (t: T) => { }) const error: BasedError = await t.throwsAsync( - coreClient.query('counter').get(), + coreClient.query2('counter').get(), ) t.is(error.code, BasedErrorCode.AuthorizeRejectedError) await coreClient.setAuthState({ token: 'mock_token' }) - await t.notThrowsAsync(coreClient.query('counter').get()) + await t.notThrowsAsync(coreClient.query2('counter').get()) }) test('getWhen', async (t: T) => { @@ -267,7 +267,7 @@ test('getWhen', async (t: T) => { }, }) - const g = await client.query('flap').getWhen((d) => d.status) + const g = await client.query2('flap').getWhen((d) => d.status) t.is(g.count, 2) }) diff --git a/test/based-client/hooks.ts b/test/based-client/hooks.ts index 62636cad7b..cd3587646d 100644 --- a/test/based-client/hooks.ts +++ b/test/based-client/hooks.ts @@ -109,14 +109,14 @@ test('Query hook', async (t: T) => { flap: { type: 'function', fn: (based) => { - return based.query('myobs').get() + return based.query2('myobs').get() }, }, myobs2: { type: 'query', closeAfterIdleTime: 500, fn: (based, _payload, update) => { - return based.query('myobs').subscribe(update) + return based.query2('myobs').subscribe(update) }, }, myobs: { @@ -135,7 +135,7 @@ test('Query hook', async (t: T) => { await client.connect({ url: async () => t.context.ws, }) - const close = client.query('myobs', { bla: true }).subscribe(() => {}) + const close = client.query2('myobs', { bla: true }).subscribe(() => {}) await wait(500) @@ -146,7 +146,7 @@ test('Query hook', async (t: T) => { t.is(unSubCnt, 1) - await client.query('myobs').get() + await client.query2('myobs').get() t.is(getCnt, 1) @@ -154,7 +154,7 @@ test('Query hook', async (t: T) => { t.is(getCnt, 2) - const close2 = client.query('myobs2', { bla: true }).subscribe(() => {}) + const close2 = client.query2('myobs2', { bla: true }).subscribe(() => {}) await wait(500) diff --git a/test/based-client/lazyConnect.ts b/test/based-client/lazyConnect.ts index 9cec86bf6e..3fbd9687fc 100644 --- a/test/based-client/lazyConnect.ts +++ b/test/based-client/lazyConnect.ts @@ -70,7 +70,7 @@ test('lazyConnect', async (t: T) => { await wait(2e3) // let msgCnt = 0 - const close = client.query('cookie').subscribe( + const close = client.query2('cookie').subscribe( () => {}, () => { errCnt++ diff --git a/test/based-client/messages.ts b/test/based-client/messages.ts index f6fa94ae23..9f2225db4a 100644 --- a/test/based-client/messages.ts +++ b/test/based-client/messages.ts @@ -59,7 +59,7 @@ test('message incoming/outgoing', async (t: T) => { }) let cnt = 0 - const close = client.query('counter').subscribe(() => { + const close = client.query2('counter').subscribe(() => { cnt++ }) diff --git a/test/based-client/nestedFunctions.ts b/test/based-client/nestedFunctions.ts index e1c7ce2b1a..83cf7bca78 100644 --- a/test/based-client/nestedFunctions.ts +++ b/test/based-client/nestedFunctions.ts @@ -30,7 +30,7 @@ const testShared = async ( let cnt = 0 - const closeX = coreClient.query('counter').subscribe(() => { + const closeX = coreClient.query2('counter').subscribe(() => { cnt++ }) @@ -42,18 +42,18 @@ const testShared = async ( let incomingCntNoJson = 0 - const close = coreClient.query('obsWithNested').subscribe(() => { + const close = coreClient.query2('obsWithNested').subscribe(() => { incomingCntNoJson++ }) let incomingCnt = 0 - const close2 = coreClient.query('obsWithNested', 'json').subscribe(() => { + const close2 = coreClient.query2('obsWithNested', 'json').subscribe(() => { incomingCnt++ }) await wait(1e3) - const bla = await coreClient.query('obsWithNested', 'json').get() + const bla = await coreClient.query2('obsWithNested', 'json').get() t.is(bla.bla.length, 1e4) @@ -64,12 +64,12 @@ const testShared = async ( close2() const close3 = coreClient - .query('obsWithNestedLvl2', 'glurk') + .query2('obsWithNestedLvl2', 'glurk') .subscribe(() => { incomingCnt2++ }) - const bla2 = await coreClient.query('obsWithNestedLvl2', 'glakkel').get() + const bla2 = await coreClient.query2('obsWithNestedLvl2', 'glakkel').get() t.is(bla2.bla.length, 1e4) @@ -106,7 +106,7 @@ test.serial('nested functions (raw api)', async (t: T) => { closeAfterIdleTime: 1e3, uninstallAfterIdleTime: 1e3, fn: (based, _, update) => { - return based.query('obsWithNested', 'json').subscribe(update) + return based.query2('obsWithNested', 'json').subscribe(update) }, }, obsWithNested: { @@ -115,7 +115,7 @@ test.serial('nested functions (raw api)', async (t: T) => { uninstallAfterIdleTime: 1e3, fn: async (based, payload, update) => { return based - .query(payload === 'json' ? 'objectCounter' : 'counter', payload) + .query2(payload === 'json' ? 'objectCounter' : 'counter', payload) .subscribe(update) }, }, @@ -164,7 +164,7 @@ test.serial('nested functions (raw api)', async (t: T) => { uninstallAfterIdleTime: 1e3, fn: async (based, payload, context) => { const x = await based.call('hello', payload, context) - await based.query('obsWithNested', 'json', context).get() + await based.query2('obsWithNested', 'json', context).get() return x }, }, @@ -199,7 +199,7 @@ test.serial('nested functions (fancy api)', async (t: T) => { uninstallAfterIdleTime: 1e3, type: 'query', fn: (based, _, update) => { - return based.query('obsWithNested', 'json').subscribe(update) + return based.query2('obsWithNested', 'json').subscribe(update) }, }, obsWithNested: { @@ -208,7 +208,7 @@ test.serial('nested functions (fancy api)', async (t: T) => { type: 'query', fn: async (based, payload, update) => { return based - .query(payload === 'json' ? 'objectCounter' : 'counter', payload) + .query2(payload === 'json' ? 'objectCounter' : 'counter', payload) .subscribe(update) }, }, @@ -257,7 +257,7 @@ test.serial('nested functions (fancy api)', async (t: T) => { uninstallAfterIdleTime: 1e3, fn: async (based, payload, context) => { const x = await based.call('hello', payload, context) - await based.query('obsWithNested', 'json').get() + await based.query2('obsWithNested', 'json').get() return x }, }, diff --git a/test/based-client/nestedFunctionsError.ts b/test/based-client/nestedFunctionsError.ts index d2966e63fd..b6e4334a0b 100644 --- a/test/based-client/nestedFunctionsError.ts +++ b/test/based-client/nestedFunctionsError.ts @@ -90,7 +90,7 @@ test('nested query functions fn does not exist error', async (t: T) => { type: 'query', uninstallAfterIdleTime: 1e3, fn: async (based, _, update) => { - return based.query('blabla').subscribe(update) + return based.query2('blabla').subscribe(update) }, }, }, @@ -105,7 +105,7 @@ test('nested query functions fn does not exist error', async (t: T) => { const errors: any[] = [] let r = 0 - client.query('hello').subscribe( + client.query2('hello').subscribe( () => { r++ }, diff --git a/test/based-client/nestedQuerySimple.ts b/test/based-client/nestedQuerySimple.ts index 184cce0891..5c49d3f647 100644 --- a/test/based-client/nestedQuerySimple.ts +++ b/test/based-client/nestedQuerySimple.ts @@ -44,7 +44,7 @@ test('query simple', async (t: T) => { type: 'query', fn: (based, _, update) => { update(1) - return based.query('nested').subscribe((r) => { + return based.query2('nested').subscribe((r) => { internal.push(r) }) }, @@ -55,9 +55,9 @@ test('query simple', async (t: T) => { await server.start() client.connect({ url: t.context.ws }) - client.query('bla').subscribe(() => {}) + client.query2('bla').subscribe(() => {}) await wait(1000) - client.query('bla', { x: 1 }).subscribe(() => {}) + client.query2('bla', { x: 1 }).subscribe(() => {}) await wait(1000) t.true(internal.length > 1) for (const r of internal) { diff --git a/test/based-client/null.ts b/test/based-client/null.ts index 0b99f660a6..07b78fe2b3 100644 --- a/test/based-client/null.ts +++ b/test/based-client/null.ts @@ -43,7 +43,7 @@ test('null', async (t: T) => { nestedNull: { type: 'query', fn: (b, __, update) => { - return b.query('null').subscribe(update) + return b.query2('null').subscribe(update) }, }, }, @@ -57,18 +57,18 @@ test('null', async (t: T) => { }, }) - const val = await client.query('null').get() + const val = await client.query2('null').get() t.deepEqual(val, null) const x = await client.call('nullFn') t.deepEqual(x, null) - const val2 = await client.query('nestedNull').get() + const val2 = await client.query2('nestedNull').get() t.deepEqual(val2, null) const obs: any[] = [] - const close = client.query('null').subscribe((v) => { + const close = client.query2('null').subscribe((v) => { obs.push(v) }) diff --git a/test/based-client/payloadPerf.ts b/test/based-client/payloadPerf.ts index b364b79b76..8a64ea8e54 100644 --- a/test/based-client/payloadPerf.ts +++ b/test/based-client/payloadPerf.ts @@ -82,7 +82,7 @@ test.serial('query perf', async (t: T) => { }) let close = client - .query('counter', { + .query2('counter', { myQuery: 1, }) .subscribe(() => { @@ -107,7 +107,7 @@ test.serial('query perf', async (t: T) => { }) close = client - .query('counterUint8', { + .query2('counterUint8', { myQuery: 1, }) .subscribe(() => { diff --git a/test/based-client/persist.ts b/test/based-client/persist.ts index d94ac3edd6..905727a597 100644 --- a/test/based-client/persist.ts +++ b/test/based-client/persist.ts @@ -70,7 +70,7 @@ test.serial('persist, store 1M length array or 8mb (nodejs)', async (t: T) => { const r: any[] = [] const close = client - .query( + .query2( 'counter', { myQuery: 123, @@ -82,7 +82,7 @@ test.serial('persist, store 1M length array or 8mb (nodejs)', async (t: T) => { }) client - .query( + .query2( 'bigData', { myQuery: 123, @@ -108,7 +108,7 @@ test.serial('persist, store 1M length array or 8mb (nodejs)', async (t: T) => { let fromStorage: any await new Promise((resolve) => client2 - .query( + .query2( 'counter', { myQuery: 123, @@ -125,7 +125,7 @@ test.serial('persist, store 1M length array or 8mb (nodejs)', async (t: T) => { await new Promise((resolve) => client2 - .query( + .query2( 'bigData', { myQuery: 123, diff --git a/test/based-client/protocolContentType.ts b/test/based-client/protocolContentType.ts index 9229bc6b76..ff13bef702 100644 --- a/test/based-client/protocolContentType.ts +++ b/test/based-client/protocolContentType.ts @@ -242,74 +242,74 @@ test.serial('fallback to old protocol - incoming', async (t: T) => { const bufResults: any[] = [] const closers = [ - client.query('errorQuery').subscribe((d, err) => { + client.query2('errorQuery').subscribe((d, err) => { obs1Results.push(d || err) }), - clientOld.query('errorQuery').subscribe((d, err) => { + clientOld.query2('errorQuery').subscribe((d, err) => { obs2Results.push(d || err) }), - client.query('nullQuery').subscribe((d) => { + client.query2('nullQuery').subscribe((d) => { obs1Results.push(d === null ? undefined : d) }), - clientOld.query('nullQuery').subscribe((d) => { + clientOld.query2('nullQuery').subscribe((d) => { obs2Results.push(d) }), - client.query('undefinedQuery').subscribe((d) => { + client.query2('undefinedQuery').subscribe((d) => { obs1Results.push(d) }), - clientOld.query('undefinedQuery').subscribe((d) => { + clientOld.query2('undefinedQuery').subscribe((d) => { obs2Results.push(d) }), - client.query('numberQuery').subscribe((d) => { + client.query2('numberQuery').subscribe((d) => { obs1Results.push(d) }), - clientOld.query('numberQuery').subscribe((d) => { + clientOld.query2('numberQuery').subscribe((d) => { obs2Results.push(d) }), client - .query('stringQuery', { + .query2('stringQuery', { myQuery: 123, }) .subscribe((d) => { obs1Results.push(d) }), clientOld - .query('stringQuery', { + .query2('stringQuery', { myQuery: 123, }) .subscribe((d) => { obs2Results.push(d) }), client - .query('bigStringQuery', { + .query2('bigStringQuery', { myQuery: 123, }) .subscribe((d) => { obs1Results.push(d) }), clientOld - .query('bigStringQuery', { + .query2('bigStringQuery', { myQuery: 123, }) .subscribe((d) => { obs2Results.push(d) }), client - .query('flap', { + .query2('flap', { myQuery: 123, }) .subscribe((d) => { obs1Results.push(d) }), clientOld - .query('flap', { + .query2('flap', { myQuery: 123, }) .subscribe((d) => { obs2Results.push(d) }), client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d) => { diff --git a/test/based-client/query.ts b/test/based-client/query.ts index a1a7a90570..d7b643e501 100644 --- a/test/based-client/query.ts +++ b/test/based-client/query.ts @@ -48,7 +48,7 @@ test.only('query functions', async (t: T) => { const obs2Results: any[] = [] const close = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d) => { @@ -56,7 +56,7 @@ test.only('query functions', async (t: T) => { }) const close2 = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d) => { diff --git a/test/based-client/queryCache.ts b/test/based-client/queryCache.ts index c9feb83efe..4273c92cf9 100644 --- a/test/based-client/queryCache.ts +++ b/test/based-client/queryCache.ts @@ -51,7 +51,7 @@ test('query cache', async (t: T) => { for (let i = 0; i < 1000; i++) { const close = client - .query('counter', { + .query2('counter', { myQuery: i, }) .subscribe((d) => { @@ -65,7 +65,7 @@ test('query cache', async (t: T) => { await wait(1000) const close = client - .query('counter', { + .query2('counter', { myQuery: 1212, }) .subscribe((d) => { diff --git a/test/based-client/queryCtxBound.ts b/test/based-client/queryCtxBound.ts index 53fcd73fe1..8cff7c7ba3 100644 --- a/test/based-client/queryCtxBound.ts +++ b/test/based-client/queryCtxBound.ts @@ -67,7 +67,7 @@ test('query ctx bound + default verifyAuthState', async (t: T) => { const errs: any[] = [] let close = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe( @@ -105,7 +105,7 @@ test('query ctx bound + default verifyAuthState', async (t: T) => { close() const r2: any[] = [] - close = client.query('counter', 'error').subscribe( + close = client.query2('counter', 'error').subscribe( (d) => { r2.push({ ...d }) }, @@ -118,10 +118,13 @@ test('query ctx bound + default verifyAuthState', async (t: T) => { close() - t.deepEqual(await client.query('counter').get(), { userId: 1, cnt: 0 }) - t.deepEqual(await client.query('counter', 'bla').get(), { userId: 1, cnt: 0 }) + t.deepEqual(await client.query2('counter').get(), { userId: 1, cnt: 0 }) + t.deepEqual(await client.query2('counter', 'bla').get(), { + userId: 1, + cnt: 0, + }) - t.throwsAsync(() => client.query('counter', 'error').get()) + t.throwsAsync(() => client.query2('counter', 'error').get()) await wait(1000) @@ -181,7 +184,7 @@ test('query ctx bound on authState.token', async (t: T) => { const results: any[] = [] const close = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d) => { @@ -274,7 +277,7 @@ test('query ctx bound on authState.userId require auth', async (t: T) => { const errs: any[] = [] let close = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe( @@ -312,7 +315,7 @@ test('query ctx bound on authState.userId require auth', async (t: T) => { close() const r2: any[] = [] - close = client.query('counter', 'error').subscribe( + close = client.query2('counter', 'error').subscribe( (d) => { r2.push({ ...d }) }, @@ -325,10 +328,13 @@ test('query ctx bound on authState.userId require auth', async (t: T) => { close() - t.deepEqual(await client.query('counter').get(), { userId: 1, cnt: 0 }) - t.deepEqual(await client.query('counter', 'bla').get(), { userId: 1, cnt: 0 }) + t.deepEqual(await client.query2('counter').get(), { userId: 1, cnt: 0 }) + t.deepEqual(await client.query2('counter', 'bla').get(), { + userId: 1, + cnt: 0, + }) - t.throwsAsync(() => client.query('counter', 'error').get()) + t.throwsAsync(() => client.query2('counter', 'error').get()) await wait(1000) @@ -408,11 +414,11 @@ test('query ctx bound on geo', async (t: T) => { const results: any[] = [] - const close = client.query('counter').subscribe((d) => { + const close = client.query2('counter').subscribe((d) => { results.push({ ...d }) }) - const close2 = client2.query('counter').subscribe((d) => { + const close2 = client2.query2('counter').subscribe((d) => { results.push({ ...d }) }) @@ -468,7 +474,7 @@ test('query ctx bound internal (nested calls)', async (t: T) => { closeAfterIdleTime: 1, uninstallAfterIdleTime: 1e3, fn: async (based, payload, update, error, ctx) => { - return based.query('nest', payload, ctx).subscribe(update) + return based.query2('nest', payload, ctx).subscribe(update) }, }, }, @@ -483,7 +489,7 @@ test('query ctx bound internal (nested calls)', async (t: T) => { }) await client.once('connect') const results: any[] = [] - const close = client.query('counter').subscribe((d) => { + const close = client.query2('counter').subscribe((d) => { results.push({ ...d }) }) await wait(250) @@ -536,7 +542,7 @@ test('query ctx bound internal (nested call from call)', async (t: T) => { public: true, uninstallAfterIdleTime: 1e3, fn: async (based, payload, ctx) => { - return based.query('nest', payload, ctx).get() + return based.query2('nest', payload, ctx).get() }, }, }, @@ -604,7 +610,7 @@ test.serial('ctxBound attachCtx perf', async (t: T) => { if (cnt === amount) { resolve() } - return based.query('nest', payload, ctx).get() + return based.query2('nest', payload, ctx).get() }, }, }, diff --git a/test/based-client/queryDiff.ts b/test/based-client/queryDiff.ts index 403ba6fb0d..f6235f9391 100644 --- a/test/based-client/queryDiff.ts +++ b/test/based-client/queryDiff.ts @@ -59,7 +59,7 @@ test('observablesDiff', async (t: T) => { const results: any[] = [] const close = coreClient - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d) => { diff --git a/test/based-client/queryErrorHandling.ts b/test/based-client/queryErrorHandling.ts index 1e81d9d790..839efdfafd 100644 --- a/test/based-client/queryErrorHandling.ts +++ b/test/based-client/queryErrorHandling.ts @@ -43,7 +43,7 @@ test('query error nested', async (t: T) => { type: 'query', fn: (based, _, update) => { update(1) - return based.query('nested').subscribe( + return based.query2('nested').subscribe( (r) => { r.flap.snurp }, @@ -55,7 +55,7 @@ test('query error nested', async (t: T) => { type: 'query', fn: (based, _, update, onError) => { update(1) - return based.query('nested').subscribe(async (r) => { + return based.query2('nested').subscribe(async (r) => { await wait(1) r.cookiepants.snurp }, onError) @@ -69,29 +69,29 @@ test('query error nested', async (t: T) => { const closers = [] client.connect({ url: t.context.ws }) - client.query('bla').subscribe( + client.query2('bla').subscribe( () => {}, () => {}, ) let errs: any[] = [] await wait(10) - client.query('bla', { x: 1 }).subscribe( + client.query2('bla', { x: 1 }).subscribe( () => {}, (err) => { errs.push(err) }, ) - client.query('bla', new Uint8Array(1000)).subscribe( + client.query2('bla', new Uint8Array(1000)).subscribe( () => {}, () => {}, ) - client.query('asyncBla', new Uint8Array(1000)).subscribe( + client.query2('asyncBla', new Uint8Array(1000)).subscribe( () => {}, () => {}, ) - client.query('asyncBla', new Uint8Array(1000)).subscribe( + client.query2('asyncBla', new Uint8Array(1000)).subscribe( () => {}, (err) => { errs.push(err) @@ -142,7 +142,7 @@ test('query error alternate', async (t: T) => { const errs: any[] = [] const results: any[] = [] - const close = client.query('bla', { x: 1 }).subscribe( + const close = client.query2('bla', { x: 1 }).subscribe( (d) => { results.push(d) }, @@ -218,7 +218,7 @@ test('query error alternate auth', async (t: T) => { const errs: any[] = [] const results: any[] = [] - const close = client.query('bla', { x: 1 }).subscribe( + const close = client.query2('bla', { x: 1 }).subscribe( (d) => { results.push(d) }, diff --git a/test/based-client/queryInstancePerf.ts b/test/based-client/queryInstancePerf.ts index 5e2d90f278..a3d22a434c 100644 --- a/test/based-client/queryInstancePerf.ts +++ b/test/based-client/queryInstancePerf.ts @@ -73,7 +73,7 @@ test('query functions perf (100k query fn instances)', async (t: T) => { for (let i = 0; i < 1e5; i++) { closers.push( client - .query('counter', { + .query2('counter', { myQuery: i, }) .subscribe(() => { diff --git a/test/based-client/queryReusedDiff.ts b/test/based-client/queryReusedDiff.ts index bee8932a09..02ce9e3a4f 100644 --- a/test/based-client/queryReusedDiff.ts +++ b/test/based-client/queryReusedDiff.ts @@ -59,7 +59,7 @@ test('query reuse diff', async (t: T) => { const obs2Results: any[] = [] const close = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d, checksum) => { @@ -70,7 +70,7 @@ test('query reuse diff', async (t: T) => { close() const close2 = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d, checksum) => { diff --git a/test/based-client/queryUint8Payload.ts b/test/based-client/queryUint8Payload.ts index 2bbe825b62..0aa2405e41 100644 --- a/test/based-client/queryUint8Payload.ts +++ b/test/based-client/queryUint8Payload.ts @@ -63,11 +63,11 @@ test('query uint8Array args', async (t: T) => { flap2[100] = 1 - const close = client.query('counter', flap).subscribe((d) => { + const close = client.query2('counter', flap).subscribe((d) => { obs1Results.push(d) }) - const close2 = client.query('counter', flap2).subscribe((d) => { + const close2 = client.query2('counter', flap2).subscribe((d) => { obs1Results.push(d) }) @@ -81,7 +81,7 @@ test('query uint8Array args', async (t: T) => { close() close2() - const x = await client.query('bla', flap2).get() + const x = await client.query2('bla', flap2).get() t.is(x.length, 8888890) diff --git a/test/based-client/reEvaluateAuthState.ts.ts b/test/based-client/reEvaluateAuthState.ts.ts index 0836371481..01fa09b2b3 100644 --- a/test/based-client/reEvaluateAuthState.ts.ts +++ b/test/based-client/reEvaluateAuthState.ts.ts @@ -80,7 +80,7 @@ test('re-evaluate authState', async (t: T) => { let counter = 0 await t.notThrowsAsync( new Promise((resolve) => { - client.query('counter').subscribe( + client.query2('counter').subscribe( () => { counter++ }, diff --git a/test/based-client/relay.ts b/test/based-client/relay.ts index 87fd1f2fff..318d876374 100644 --- a/test/based-client/relay.ts +++ b/test/based-client/relay.ts @@ -122,7 +122,7 @@ test('Relay', async (t: T) => { }, }) - const x = await client.query('counter').get() + const x = await client.query2('counter').get() t.is(x, 1) const hello = await client.call('hello', { snap: 'je' }) t.is(hello, 'from hello je') @@ -139,7 +139,7 @@ test('Relay', async (t: T) => { t.deepEqual(msges, ['bla']) - const count = await client.query('flap').get() + const count = await client.query2('flap').get() t.true(count > 0) diff --git a/test/based-client/ssr.ts b/test/based-client/ssr.ts index 4632d57839..8af09fe645 100644 --- a/test/based-client/ssr.ts +++ b/test/based-client/ssr.ts @@ -48,8 +48,8 @@ test('query cache', async (t: T) => { }, }) - await client.query('counter').get() - await client.query('counter', { bla: true }).get() + await client.query2('counter').get() + await client.query2('counter', { bla: true }).get() const script = createInlineFromCurrentCache(client, [{ endpoint: 'counter' }]) @@ -68,7 +68,7 @@ test('query cache', async (t: T) => { client.cache.clear() const selectiveGet = await createInlineCache(client, [ - client.query('counter'), + client.query2('counter'), ]) t.deepEqual( diff --git a/test/based-client/throttle.ts b/test/based-client/throttle.ts index afc513238f..9dc2089af8 100644 --- a/test/based-client/throttle.ts +++ b/test/based-client/throttle.ts @@ -51,7 +51,7 @@ test('throttle', async (t: T) => { const obs1Results: any[] = [] const close = client - .query('counter', { + .query2('counter', { myQuery: 123, }) .subscribe((d) => { diff --git a/test/bench.perf.ts b/test/bench.perf.ts index 65d1799fdb..b9c9d31eea 100644 --- a/test/bench.perf.ts +++ b/test/bench.perf.ts @@ -51,7 +51,7 @@ await test('test embedded', async (t) => { res = ( await Promise.all( arr.map(() => - db.query('test').filter('x', '=', 0).range(1, 10_001).get(), + db.query2('test').filter('x', '=', 0).range(1, 10_001).get(), ), ) ).reduce((prev, cur) => prev + cur.length, 0) @@ -64,7 +64,7 @@ await test('test embedded', async (t) => { await Promise.all( Array.from({ length: N3 }).map(async (_, i) => { await s.acquire() - db.query('test', i + 1) + db.query2('test', i + 1) .get() .then(() => s.release()) }), @@ -72,7 +72,7 @@ await test('test embedded', async (t) => { await s.drain() //res = ( // await Promise.all( - // Array.from({ length: N3 }).map((_, i) => db.query('test', i + 1).get()), + // Array.from({ length: N3 }).map((_, i) => db.query2('test', i + 1).get()), // ) //).reduce((prev, cur) => prev + cur.length, 0) }, 'q1') @@ -110,7 +110,7 @@ await test('test client-server', async (t) => { res = ( await Promise.all( arr.map(() => - client1.query('test').filter('x', '=', 0).range(1, 10_001).get(), + client1.query2('test').filter('x', '=', 0).range(1, 10_001).get(), ), ) ).reduce((prev, cur) => prev + cur.length, 0) @@ -122,7 +122,7 @@ await test('test client-server', async (t) => { res = ( await Promise.all( Array.from({ length: N3 }).map((_, i) => - client1.query('test', i + 1).get(), + client1.query2('test', i + 1).get(), ), ) ).reduce((prev, cur) => prev + cur.length, 0) diff --git a/test/bigNode.perf.ts b/test/bigNode.perf.ts index 8a56f8b26b..8493a9a407 100644 --- a/test/bigNode.perf.ts +++ b/test/bigNode.perf.ts @@ -54,8 +54,8 @@ await test('big nodes', async (t) => { }) await db.drain() - const mega = (await db.query('mega').get()) - const giga = (await db.query('giga').get()) + const mega = await db.query2('mega').get() + const giga = await db.query2('giga').get() deepEqual(mega[1].f4092, 1337) deepEqual(giga[1].f100, 1337) @@ -63,15 +63,15 @@ await test('big nodes', async (t) => { db.update('giga', giga1, { ref: mega2 }) await db.drain() - const megaRefQ = await db.query('mega').include('ref').get() + const megaRefQ = await db.query2('mega').include('ref').get() const megaRef = megaRefQ - const gigaRef = (await db.query('giga').include('ref').get()) + const gigaRef = await db.query2('giga').include('ref').get() deepEqual(gigaRef[0].ref.id, 2) deepEqual(megaRef[1].ref.id, 1) - const megaInclude = await db.query('mega').get() + const megaInclude = await db.query2('mega').get() const serializedSchema = serialize(megaInclude.def.readSchema!) const deserializedSchema = deSerializeSchema(serializedSchema) @@ -85,7 +85,7 @@ await test('big nodes', async (t) => { deepEqual(obj[1].f4092, 1337) - const megaIncludeSelective = await db.query('mega').include('f4092').get() + const megaIncludeSelective = await db.query2('mega').include('f4092').get() const serializedSchemaSmall = serialize(megaIncludeSelective.def.readSchema!) const deserializedSchemaSmall = deSerializeSchema(serializedSchemaSmall) diff --git a/test/binary.ts b/test/binary.ts index 059a6780ac..47b1d07f20 100644 --- a/test/binary.ts +++ b/test/binary.ts @@ -29,7 +29,7 @@ await test('simple', async (t) => { await db.drain() deepEqual( - await db.query('user').get(), + await db.query2('user').get(), [ { id: 1, @@ -43,7 +43,7 @@ await test('simple', async (t) => { file: new Uint8Array([1, 2, 3, 4]), }) - deepEqual(await db.query('user', id).get(), { + deepEqual(await db.query2('user', id).get(), { id, file: new Uint8Array([1, 2, 3, 4]), }) @@ -53,10 +53,7 @@ await test('simple', async (t) => { file: italyBytes, }) - equal( - (await db.query('user', id2).get()).file.length, - italyBytes.byteLength, - ) + equal((await db.query2('user', id2).get()).file.length, italyBytes.byteLength) }) await test('binary and crc32', async (t) => { @@ -80,13 +77,13 @@ await test('binary and crc32', async (t) => { article: new Uint8Array([1]), }) - const checksum = (await db.query('user', user1).get()).checksum + const checksum = (await db.query2('user', user1).get()).checksum await db.update('user', user1, { article: new Uint8Array([2]), }) - const checksum2 = (await db.query('user', user1).get()).checksum + const checksum2 = (await db.query2('user', user1).get()).checksum notEqual(checksum, checksum2, 'Checksum is not the same') }) diff --git a/test/boolean.ts b/test/boolean.ts index f870eda9ef..de4d152bea 100644 --- a/test/boolean.ts +++ b/test/boolean.ts @@ -31,21 +31,21 @@ await test('boolean', async (t) => { await client.drain() - deepEqual(await client.query('user').get(), [ + deepEqual(await client.query2('user').get(), [ { id: 1, isNice: false }, { id: 2, isNice: true }, { id: 3, isNice: false }, ]) - deepEqual(await client.query('user').filter('isNice', '=', true).get(), [ + deepEqual(await client.query2('user').filter('isNice', '=', true).get(), [ { id: 2, isNice: true }, ]) - deepEqual(await client.query('user').filter('isNice').get(), [ + deepEqual(await client.query2('user').filter('isNice').get(), [ { id: 2, isNice: true }, ]) - deepEqual(await client.query('user').filter('isNice', '=', false).get(), [ + deepEqual(await client.query2('user').filter('isNice', '=', false).get(), [ { id: 1, isNice: false }, { id: 3, isNice: false }, ]) diff --git a/test/cardinality.ts b/test/cardinality.ts index e55dc3bd62..8cc5c10bfa 100644 --- a/test/cardinality.ts +++ b/test/cardinality.ts @@ -46,12 +46,10 @@ await test('hll', async (t) => { console.log('a') deepEqual( - ( - await db - .query('article') - .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') - .get() - ), + await db + .query2('article') + .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') + .get(), [ { id: 1, @@ -63,13 +61,11 @@ await test('hll', async (t) => { console.log('b') deepEqual( - ( - await db - .query('article') - .include('myUniqueValuesCount') - .filter('myUniqueValuesCount', '!=', 0) - .get() - ), + await db + .query2('article') + .include('myUniqueValuesCount') + .filter('myUniqueValuesCount', '!=', 0) + .get(), [ { id: 1, @@ -101,12 +97,10 @@ await test('hll', async (t) => { }) deepEqual( - ( - await db - .query('article') - .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') - .get() - ), + await db + .query2('article') + .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') + .get(), [ { id: 1, myUniqueValuesCount: 1, myUniqueValuesCountFromArray: 0 }, { id: 2, myUniqueValuesCountFromArray: 7, myUniqueValuesCount: 0 }, @@ -114,13 +108,11 @@ await test('hll', async (t) => { ) deepEqual( - ( - await db - .query('article') - .include('myUniqueValuesCountFromArray') - .filter('myUniqueValuesCountFromArray', '=', 7) - .get() - ), + await db + .query2('article') + .include('myUniqueValuesCountFromArray') + .filter('myUniqueValuesCountFromArray', '=', 7) + .get(), [ { id: 2, @@ -130,13 +122,11 @@ await test('hll', async (t) => { ) deepEqual( - ( - await db - .query('article') - .include('myUniqueValuesCount') - .filter('myUniqueValuesCount', '>', 1) - .get() - ), + await db + .query2('article') + .include('myUniqueValuesCount') + .filter('myUniqueValuesCount', '>', 1) + .get(), [], ) @@ -164,12 +154,10 @@ await test('hll', async (t) => { await db.drain() deepEqual( - ( - await db - .query('article') - .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') - .get() - ), + await db + .query2('article') + .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') + .get(), [ { id: 1, myUniqueValuesCount: 7, myUniqueValuesCountFromArray: 0 }, { id: 2, myUniqueValuesCountFromArray: 7, myUniqueValuesCount: 0 }, @@ -196,13 +184,11 @@ await test('hll', async (t) => { await db.drain() deepEqual( - ( - await db - .query('article') - .filter('myUniqueValuesCount', '=', 11) - .or('myUniqueValuesCountFromArray', '>', 6) - .get() - ), + await db + .query2('article') + .filter('myUniqueValuesCount', '=', 11) + .or('myUniqueValuesCountFromArray', '>', 6) + .get(), [ { id: 1, @@ -230,7 +216,7 @@ await test('hll', async (t) => { deepEqual( await db - .query('article') + .query2('article') .filter('id', '>=', 3) .include('contributors.$tokens') .get(), @@ -274,13 +260,11 @@ await test('hll', async (t) => { }) deepEqual( - ( - await db - .query('article') - .filter('id', '>=', 3) - .include('contributors.$tokens') - .get() - ), + await db + .query2('article') + .filter('id', '>=', 3) + .include('contributors.$tokens') + .get(), [ { id: 3, @@ -297,11 +281,10 @@ await test('hll', async (t) => { // handle undefined case deepEqual( await db - .query('article') + .query2('article') .filter('id', '>=', 3) .include('contributors.$undeftokens') - .get() - , + .get(), [ { id: 3, @@ -326,11 +309,10 @@ await test('hll', async (t) => { deepEqual( await db - .query('article') + .query2('article') .filter('id', '>=', 3) .include('contributors.$undeftokens') - .get() - , + .get(), [ { id: 3, @@ -375,7 +357,7 @@ await test('switches', async (t) => { }) deepEqual( - await db.query('store').include('visitors').get(), + await db.query2('store').include('visitors').get(), [ { id: 1, @@ -392,7 +374,7 @@ await test('switches', async (t) => { await db.drain() deepEqual( - await db.query('store').include('visitors').get(), + await db.query2('store').include('visitors').get(), [ { id: 1, @@ -437,7 +419,7 @@ await test('defaultPrecision', async (t) => { customers: [cus], }) - const pb = await db.query('customer').include('productsBought').get() + const pb = await db.query2('customer').include('productsBought').get() // pb.inspect() deepEqual( pb, @@ -450,7 +432,7 @@ await test('defaultPrecision', async (t) => { 'simple cardinality default precision', ) - const pbr = await db.query('store').include('*', '**').get() + const pbr = await db.query2('store').include('*', '**').get() // pbr.inspect() deepEqual( pbr, diff --git a/test/clientServer.perf.ts b/test/clientServer.perf.ts index 3af6c04be0..aeaf9508eb 100644 --- a/test/clientServer.perf.ts +++ b/test/clientServer.perf.ts @@ -40,7 +40,7 @@ await test('client server rapid fire', async (t) => { }) userIds.push(userId) })(), - client.query('user').sort('name').include('name', 'users').get(), + client.query2('user').sort('name').include('name', 'users').get(), ) } } else { @@ -51,11 +51,7 @@ await test('client server rapid fire', async (t) => { }) await Promise.all(promises) - const allUsers1 = await clients[0] - .query('user') - .range(0, 100_000) - .get() - + const allUsers1 = await clients[0].query2('user').range(0, 100_000).get() let id = 0 for (const user of allUsers1) { diff --git a/test/clientServer.ts b/test/clientServer.ts index b083101a04..471b6886a8 100644 --- a/test/clientServer.ts +++ b/test/clientServer.ts @@ -23,7 +23,7 @@ await test('client server basic', async (t) => { name: 'jamez', }) - deepEqual(await client1.query('user').get(), [ + deepEqual(await client1.query2('user').get(), [ { id: 1, name: 'youzi' }, { id: 2, name: 'jamez' }, ]) @@ -36,7 +36,7 @@ await test('client server basic', async (t) => { }, }) - deepEqual(await client1.query('user').get(), [ + deepEqual(await client1.query2('user').get(), [ { id: 1, age: 0 }, { id: 2, age: 0 }, ]) @@ -77,7 +77,7 @@ await test('client server basic', async (t) => { }) deepEqual( - await client1.query('user', res).include('*', '**').get(), + await client1.query2('user', res).include('*', '**').get(), { id: 1, age: 0, diff --git a/test/concurrency.perf.ts b/test/concurrency.perf.ts index 2a82db8479..5bf5e0e21d 100644 --- a/test/concurrency.perf.ts +++ b/test/concurrency.perf.ts @@ -47,7 +47,7 @@ await test('concurrency', async (t) => { queries++ try { const x = await db - .query('user') + .query2('user') .include((s) => s('friends').range(0, 10)) .range(0, 1000_000) .get() @@ -114,7 +114,7 @@ await test('many instances', async (t) => { } await Promise.all( dbs.map((db) => - db.query('t').search('contribution', 's').include('i').get(), + db.query2('t').search('contribution', 's').include('i').get(), ), ) await Promise.all(dbs.map((db) => db.drain())) diff --git a/test/copy.ts b/test/copy.ts index 4f721d0554..185706a711 100644 --- a/test/copy.ts +++ b/test/copy.ts @@ -91,10 +91,9 @@ await test('copy', async (t) => { }) const res = await db - .query('edition') + .query2('edition') .include('*', 'versionOf', 'versions', 'sequences', 'sequences.pages') .get() - deepEqual(res, [ { diff --git a/test/crc32c.ts b/test/crc32c.ts index b29741b0ed..002f5a366e 100644 --- a/test/crc32c.ts +++ b/test/crc32c.ts @@ -118,22 +118,15 @@ await test('simple', async (t) => { myNativeMadeHash: nativeCrc32(ENCODER.encode('oid123')), }) - equal( - await db.query('transaction').include('id', 'myHash').get(), - [ - { - id: 1, - myHash: 2628032717, - }, - ], - ) + equal(await db.query2('transaction').include('id', 'myHash').get(), [ + { + id: 1, + myHash: 2628032717, + }, + ]) equal( - await db - .query('transactionN') - .include('id', 'myNativeMadeHash') - .get() - , + await db.query2('transactionN').include('id', 'myNativeMadeHash').get(), [ { id: 1, diff --git a/test/db-schema/schemaDebug.ts b/test/db-schema/schemaDebug.ts index d8b6745f77..bed86d1cc5 100644 --- a/test/db-schema/schemaDebug.ts +++ b/test/db-schema/schemaDebug.ts @@ -87,7 +87,7 @@ await test('schema debug', async (t) => { let i = 1 while (i--) { - const contestants = await client.query('contestant').get() + const contestants = await client.query2('contestant').get() await client.setSchema( deepMerge(schema, { diff --git a/test/db-schema/schemaProblems.ts b/test/db-schema/schemaProblems.ts index b2e079ca49..c343c54e02 100644 --- a/test/db-schema/schemaProblems.ts +++ b/test/db-schema/schemaProblems.ts @@ -25,13 +25,13 @@ await test('schema problems', async (t) => { const q: any[] = [] q.push( clientWorker(t, db, async (c) => { - await c.query('flap').get().inspect() + await c.query2('flap').get().inspect() }), ) q.push( clientWorker(t, db, async (c) => { - // c.query('flap') + // c.query2('flap') // .count() // .subscribe( // (d) => { @@ -42,7 +42,7 @@ await test('schema problems', async (t) => { // }, // ) - c.query('seq') + c.query2('seq') .include('flap') .subscribe( (d) => { @@ -53,7 +53,7 @@ await test('schema problems', async (t) => { }, ) - c.query('flap').subscribe( + c.query2('flap').subscribe( (d) => { // console.log('sub2', d) }, @@ -67,7 +67,7 @@ await test('schema problems', async (t) => { q.push( clientWorker(t, db, async (c) => { - c.query('flap') + c.query2('flap') .include('flap') .subscribe( (d) => { @@ -213,21 +213,17 @@ await test('schema problems', async (t) => { await Promise.all(q) - equal( - (await db.query('flap').count().get().inspect()).count, - 1_100_001, - ) - equal((await db.query('seq').count().get().inspect()).count, 1) + equal((await db.query2('flap').count().get().inspect()).count, 1_100_001) + equal((await db.query2('seq').count().get().inspect()).count, 1) // to Object on nested refs does not work if combin count + sum equal( ( await db - .query('seq') + .query2('seq') .include((s) => s('flap').count()) .get() .inspect() - )[0].flap.count, 1_100_000, ) diff --git a/test/delete.perf.ts b/test/delete.perf.ts index 5742651e2a..6f43d14785 100644 --- a/test/delete.perf.ts +++ b/test/delete.perf.ts @@ -55,7 +55,7 @@ await test('delete performance', async (t) => { }, 'delete 1M users') assert(t0 < 400, 'delete 1M users') - deepEqual(await db.query('user').get(), []) + deepEqual(await db.query2('user').get(), []) const amountArticles = 1e6 const articles: any[] = [] @@ -76,7 +76,7 @@ await test('delete performance', async (t) => { }, 'delete 1M articles') assert(t2 < 400, 'delete 1M users') - deepEqual(await db.query('article').get(), []) + deepEqual(await db.query2('article').get(), []) const articles2: any[] = [] @@ -111,5 +111,5 @@ await test('delete performance', async (t) => { } await db.drain() }, 'delete 1M articles - again') // < 400 - deepEqual(await db.query('article').get(), []) + deepEqual(await db.query2('article').get(), []) }) diff --git a/test/delete.ts b/test/delete.ts index b15bc6264b..53652dbc8a 100644 --- a/test/delete.ts +++ b/test/delete.ts @@ -38,11 +38,11 @@ await test('delete', async (t) => { await db.delete('user', simple) await db.drain() - deepEqual(await db.query('user').get(), []) + deepEqual(await db.query2('user').get(), []) const nurp = db.create('nurp', {}) await db.drain() - deepEqual(await db.query('nurp').include('email').get(), [ + deepEqual(await db.query2('nurp').include('email').get(), [ { email: '', id: 1, @@ -52,7 +52,7 @@ await test('delete', async (t) => { db.delete('nurp', nurp) await db.drain() - deepEqual(await db.query('user').include('email').get(), []) + deepEqual(await db.query2('user').include('email').get(), []) const nurp2 = db.create('nurp', { email: 'flippie' }) await db.drain() @@ -62,7 +62,7 @@ await test('delete', async (t) => { }) await db.drain() - deepEqual(await db.query('nurp').include('email').get(), [ + deepEqual(await db.query2('nurp').include('email').get(), [ { email: '', id: 2, @@ -104,12 +104,12 @@ await test('non existing 1', async (t) => { db.delete('user', simple) await db.drain() - deepEqual(await db.query('user').get(), []) + deepEqual(await db.query2('user').get(), []) const nurp = db.create('nurp', {}) await db.drain() - deepEqual(await db.query('nurp').include('email').get(), [ + deepEqual(await db.query2('nurp').include('email').get(), [ { email: '', id: 1, @@ -157,13 +157,13 @@ await test('non existing 2', async (t) => { await db.delete('user', simple) - deepEqual(await db.query('user').get(), []) + deepEqual(await db.query2('user').get(), []) db.create('nurp', {}) await db.drain() - deepEqual(await db.query('nurp').include('email').get(), [ + deepEqual(await db.query2('nurp').include('email').get(), [ { email: '', id: 1, @@ -223,6 +223,6 @@ await test('save', async (t) => { t.after(() => db2.destroy(), true) - deepEqual(await db2.query('user').include('id').get(), [{ id: 2 }]) - deepEqual(await db.query('user').include('id').get(), [{ id: 2 }]) + deepEqual(await db2.query2('user').include('id').get(), [{ id: 2 }]) + deepEqual(await db.query2('user').include('id').get(), [{ id: 2 }]) }) diff --git a/test/enum.ts b/test/enum.ts index 35c80159da..59d5a5797b 100644 --- a/test/enum.ts +++ b/test/enum.ts @@ -37,7 +37,7 @@ await test('enum', async (t) => { client.create('user', {}) - deepEqual(await client.query('user').include('fancyness').get(), [ + deepEqual(await client.query2('user').include('fancyness').get(), [ { id: 1, fancyness: 'mid' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -46,7 +46,7 @@ await test('enum', async (t) => { deepEqual( await client - .query('user') + .query2('user') .include('fancyness') .filter('fancyness', '=', 'fire') .get(), @@ -60,7 +60,7 @@ await test('enum', async (t) => { fancyness: 'beta', }) - deepEqual(await client.query('user').include('fancyness').get(), [ + deepEqual(await client.query2('user').include('fancyness').get(), [ { id: 1, fancyness: 'beta' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -82,7 +82,7 @@ await test('enum', async (t) => { }), ) - deepEqual(await client.query('user').include('fancyness').get(), [ + deepEqual(await client.query2('user').include('fancyness').get(), [ { id: 1, fancyness: 'fire' }, { id: 2, fancyness: 'fire' }, { id: 3, fancyness: 'beta' }, @@ -114,7 +114,7 @@ await test('emoji enum', async (t) => { client.create('review', {}) client.create('review', { score: '🙂' }) - deepEqual(await client.query('review').include('score').get(), [ + deepEqual(await client.query2('review').include('score').get(), [ { id: 1, score: '😐', @@ -128,7 +128,7 @@ await test('emoji enum', async (t) => { client.create('review', { score: '☹️' }) client.create('review', { score: '😐' }) deepEqual( - await client.query('review').include('score').sort('score', 'desc').get(), + await client.query2('review').include('score').sort('score', 'desc').get(), [ { id: 2, score: '🙂' }, { id: 1, score: '😐' }, diff --git a/test/errors.ts b/test/errors.ts index aea96c9905..7d0cf9802a 100644 --- a/test/errors.ts +++ b/test/errors.ts @@ -27,7 +27,7 @@ await test('handle errors - references', async (t) => { friends: [2], }) - equal(await db.query('user').include('friends').get(), [ + equal(await db.query2('user').include('friends').get(), [ { id: 1, friends: [], @@ -58,7 +58,7 @@ await test('handle errors - single ref', async (t) => { friend: 2, }) - equal(await db.query('user').include('friend').get(), [ + equal(await db.query2('user').include('friend').get(), [ { id: 1, friend: null, @@ -88,7 +88,7 @@ await test('handle errors - non existent id', async (t) => { await db.drain() - equal(await db.query('user').get(), [ + equal(await db.query2('user').get(), [ { id: 1, name: 'bob' }, { id: 2, name: 'bert' }, ]) diff --git a/test/filter/api.ts b/test/filter/api.ts index 9b633f647e..871d2d199f 100644 --- a/test/filter/api.ts +++ b/test/filter/api.ts @@ -35,7 +35,7 @@ await test('filter api: object', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('friends') .filter('friends', 'includes', { id: 2 }) .get(), @@ -48,7 +48,7 @@ await test('filter api: object', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('friends') .filter('friends', 'includes', [{ id: 2 }, { id: 1 }]) .get(), @@ -61,7 +61,7 @@ await test('filter api: object', async (t) => { ) deepEqual( - await db.query('user').filter('bestFriend', '=', { id: 9 }).get(), + await db.query2('user').filter('bestFriend', '=', { id: 9 }).get(), [ { id: 10, @@ -72,7 +72,7 @@ await test('filter api: object', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('bestFriend', '=', [{ id: 9 }, { id: 10 }]) .get(), [ diff --git a/test/filter/edges.ts b/test/filter/edges.ts index 33341ad038..900eacdc9d 100644 --- a/test/filter/edges.ts +++ b/test/filter/edges.ts @@ -56,7 +56,7 @@ await test('filter edges', async (t) => { deepEqual( await db - .query('team', 1) + .query2('team', 1) .include((q) => q('files').filter('fileType', '=', 'document').include('id'), ) @@ -74,7 +74,7 @@ await test('filter edges', async (t) => { deepEqual( await db - .query('team') + .query2('team') .filter('files', 'exists') .include((s) => s('files').filter('fileType', '=', 'document').include('id'), @@ -145,11 +145,11 @@ await test('filter references', async (t) => { fileType: 'document', teams: [t3], }) - // await db.query('team').include('*', '**').get().inspect(100) - // await db.query('libraryFile').include('*', '**').get().inspect(100) + // await db.query2('team').include('*', '**').get().inspect(100) + // await db.query2('libraryFile').include('*', '**').get().inspect(100) //await db - // .query('team') + // .query2('team') // .include('files') // // need to make this // // still missing diff --git a/test/filter/filter.ts b/test/filter/filter.ts index 4b13ab66d9..90aedee064 100644 --- a/test/filter/filter.ts +++ b/test/filter/filter.ts @@ -47,7 +47,7 @@ await test('single', async (t) => { const x = [10, 20] - deepEqual((await db.query('org').filter('x', '=', x).get()), [ + deepEqual(await db.query2('org').filter('x', '=', x).get(), [ { id: 1, status: 'ok', @@ -55,61 +55,44 @@ await test('single', async (t) => { name: 'hello', }, ]) - deepEqual( - (await db.query('org').filter('orgs', '=', [org, org2]).get()), - [ - { - id: 3, - status: undefined, - x: 0, - name: 'hello ???????', - }, - ], - ) - deepEqual( - (await db.query('org').filter('status', '=', 'error').get()), - [], - ) - deepEqual( - (await db.query('org').filter('status', '=', 'ok').get()), - [ - { - id: 1, - status: 'ok', - x: 10, - name: 'hello', - }, - { - id: 2, - status: 'ok', - x: 0, - name: 'x', - }, - ], - ) - deepEqual( - (await db.query('org').filter('name', 'includes', '0').get()), - [], - ) - deepEqual( - ( - await db.query('org').filter('name', 'includes', 'hello').get() - ), - [ - { - id: 1, - status: 'ok', - x: 10, - name: 'hello', - }, - { - id: 3, - status: undefined, - x: 0, - name: 'hello ???????', - }, - ], - ) + deepEqual(await db.query2('org').filter('orgs', '=', [org, org2]).get(), [ + { + id: 3, + status: undefined, + x: 0, + name: 'hello ???????', + }, + ]) + deepEqual(await db.query2('org').filter('status', '=', 'error').get(), []) + deepEqual(await db.query2('org').filter('status', '=', 'ok').get(), [ + { + id: 1, + status: 'ok', + x: 10, + name: 'hello', + }, + { + id: 2, + status: 'ok', + x: 0, + name: 'x', + }, + ]) + deepEqual(await db.query2('org').filter('name', 'includes', '0').get(), []) + deepEqual(await db.query2('org').filter('name', 'includes', 'hello').get(), [ + { + id: 1, + status: 'ok', + x: 10, + name: 'hello', + }, + { + id: 3, + status: undefined, + x: 0, + name: 'hello ???????', + }, + ]) }) await test('simple', async (t) => { @@ -214,7 +197,7 @@ await test('simple', async (t) => { const x = [300, 400, 10, 20, 1, 2, 99, 9999, 888, 6152] equal( - (await db.query('machine').filter('lastPing', '=', x).get()).length, + (await db.query2('machine').filter('lastPing', '=', x).get()).length, x.length, 'OR number', ) @@ -237,7 +220,7 @@ await test('simple', async (t) => { const rand = ~~(Math.random() * lastId) || 1 const derp = [make(), make(), make(), rand] return db - .query('env') + .query2('env') .include('*') .filter('machines', 'includes', derp) .get() @@ -264,7 +247,7 @@ await test('simple', async (t) => { Array.from({ length: amount }).map(async () => { const rand = ~~(Math.random() * lastId) || 1 const envs = await db - .query('env') + .query2('env') .include('*') .filter('machines', 'includes', rand) .get() @@ -283,7 +266,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('scheduled', '>', 'now + 694d + 10h') .get() @@ -294,7 +277,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('scheduled', '<', 'now-694d-10h-15m') // Date, .get() @@ -305,7 +288,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('scheduled', '<', '10/24/2000') // Date, .get() @@ -317,7 +300,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('requestsServed', '<', 1) .get() @@ -328,7 +311,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('requestsServed', '<=', 1) .get() @@ -339,7 +322,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('derp', '<=', 0) .filter('derp', '>', -5) @@ -352,7 +335,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('temperature', '<=', 0) .filter('temperature', '>', -0.1) @@ -365,7 +348,7 @@ await test('simple', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('*') .filter('temperature', '<=', 0) .filter('temperature', '>', -0.1) @@ -376,14 +359,12 @@ await test('simple', async (t) => { ) equal( - ( - await db - .query('machine') - .include('id') - .filter('env', '=', env) - .range(0, 10) - .get() - ), + await db + .query2('machine') + .include('id') + .filter('env', '=', env) + .range(0, 10) + .get(), [ { id: 2 }, { id: 4 }, @@ -400,15 +381,13 @@ await test('simple', async (t) => { ) equal( - ( - await db - .query('machine') - .include('id') - .filter('lastPing', '>=', 1e5 - 1) // order optmization automaticly - .filter('env', '=', [emptyEnv, env]) - .range(0, 10) - .get() - ), + await db + .query2('machine') + .include('id') + .filter('lastPing', '>=', 1e5 - 1) // order optmization automaticly + .filter('env', '=', [emptyEnv, env]) + .range(0, 10) + .get(), [{ id: 100000 }], 'Filter by reference (multiple)', ) @@ -440,7 +419,7 @@ await test('simple', async (t) => { ]) deepEqual( - await db.query('env').filter('machines', '<', 10).get(), + await db.query2('env').filter('machines', '<', 10).get(), [ { id: 2, @@ -459,7 +438,7 @@ await test('simple', async (t) => { ) deepEqual( - await db.query('env').filter('machines', '=', ids).get(), + await db.query2('env').filter('machines', '=', ids).get(), [ { id: 3, @@ -472,13 +451,11 @@ await test('simple', async (t) => { ) deepEqual( - ( - await db - .query('machine') - .include('env', '*') - .filter('env.status', '=', 5) - .get() - ), + await db + .query2('machine') + .include('env', '*') + .filter('env.status', '=', 5) + .get(), [ { id: 100001, @@ -521,13 +498,11 @@ await test('simple', async (t) => { }) deepEqual( - ( - await db - .query('machine') - .filter('status', '=', '🦄') - .include('status') - .get() - ), + await db + .query2('machine') + .filter('status', '=', '🦄') + .include('status') + .get(), [ { id: unicornMachine, @@ -536,18 +511,18 @@ await test('simple', async (t) => { ], ) - deepEqual((await db.query('env').filter('standby').get()), []) + deepEqual(await db.query2('env').filter('standby').get(), []) await db.update('env', derpEnv, { standby: true, }) - deepEqual((await db.query('env').filter('standby').get()), [ + deepEqual(await db.query2('env').filter('standby').get(), [ { id: 3, standby: true, status: 5, name: 'derp env' }, ]) let rangeResult = await db - .query('machine') + .query2('machine') .include('temperature') .filter('temperature', '..', [-0.1, 0]) .get() @@ -560,7 +535,7 @@ await test('simple', async (t) => { ) rangeResult = await db - .query('machine') + .query2('machine') .include('*') .range(0, 10) // .filter('temperature', '!..', [-0.1, 0]) @@ -629,14 +604,12 @@ await test('or', async (t) => { await db.drain() deepEqual( - ( - await db - .query('machine') - .include('id', 'lastPing') - .filter('scheduled', '>', '01/01/2100') - .or('lastPing', '>', 1e6 - 2) - .get() - ), + await db + .query2('machine') + .include('id', 'lastPing') + .filter('scheduled', '>', '01/01/2100') + .or('lastPing', '>', 1e6 - 2) + .get(), [ { id: 999999, @@ -650,30 +623,26 @@ await test('or', async (t) => { ) deepEqual( - ( - await db - .query('machine') - .include('id', 'lastPing') - .filter('scheduled', '>', '01/01/2100') - .or((f) => { - f.filter('lastPing', '>', 1e6 - 2) - }) - .get() - ), - ( - await db - .query('machine') - .include('id', 'lastPing') - .filter('scheduled', '>', '01/01/2100') - .or('lastPing', '>', 1e6 - 2) - .get() - ), + await db + .query2('machine') + .include('id', 'lastPing') + .filter('scheduled', '>', '01/01/2100') + .or((f) => { + f.filter('lastPing', '>', 1e6 - 2) + }) + .get(), + await db + .query2('machine') + .include('id', 'lastPing') + .filter('scheduled', '>', '01/01/2100') + .or('lastPing', '>', 1e6 - 2) + .get(), ) equal( ( await db - .query('machine') + .query2('machine') .include('id', 'lastPing') .filter('scheduled', '>', '01/01/2100') .or((f) => { @@ -687,42 +656,36 @@ await test('or', async (t) => { ) deepEqual( - ( - await db - .query('machine') - .include('id', 'lastPing') - .filter('scheduled', '>', '01/01/2100') - .or((f) => { - f.filter('lastPing', '>', 1e6 - 2) - f.or((f) => { - f.filter('temperature', '<', -30) - }) - }) - .get() - ), - ( - await db - .query('machine') - .include('id', 'lastPing') - .filter('scheduled', '>', '01/01/2100') - .or((f) => { - f.filter('lastPing', '>', 1e6 - 2) - f.or('temperature', '<', -30) + await db + .query2('machine') + .include('id', 'lastPing') + .filter('scheduled', '>', '01/01/2100') + .or((f) => { + f.filter('lastPing', '>', 1e6 - 2) + f.or((f) => { + f.filter('temperature', '<', -30) }) - .get() - ), - ) - - const r = ( + }) + .get(), await db - .query('machine') - .include('temperature') - .range(0, 15) - .filter('temperature', '>', 0) - .or('temperature', '<', -0.1) - .get() + .query2('machine') + .include('id', 'lastPing') + .filter('scheduled', '>', '01/01/2100') + .or((f) => { + f.filter('lastPing', '>', 1e6 - 2) + f.or('temperature', '<', -30) + }) + .get(), ) + const r = await db + .query2('machine') + .include('temperature') + .range(0, 15) + .filter('temperature', '>', 0) + .or('temperature', '<', -0.1) + .get() + equal( r .map((v, i) => { @@ -760,15 +723,13 @@ await test('or numerical', async (t) => { } await db.drain() - const r = ( - await db - .query('machine') - .include('temperature') - .range(0, 1000) - .filter('temperature', '>', 150) - .or('temperature', '<', 50) - .get() - ) + const r = await db + .query2('machine') + .include('temperature') + .range(0, 1000) + .filter('temperature', '>', 150) + .or('temperature', '<', 50) + .get() equal( r @@ -795,7 +756,7 @@ await test('or numerical', async (t) => { equal( ( await db - .query('machine') + .query2('machine') .include('id', 'temperature') .filter('temperature', '>', 201) .or((f) => { @@ -816,7 +777,7 @@ await test('or numerical', async (t) => { await db.drain() deepEqual( - (await db.query('machine').include('id').range(0, 3).get()).node(-1), + (await db.query2('machine').include('id').range(0, 3).get()).node(-1), { id: 3, }, @@ -825,7 +786,7 @@ await test('or numerical', async (t) => { deepEqual( ( await db - .query('machine') + .query2('machine') .include('temperature') .filter('id', '<=', 20000) .range(10000, 20000) @@ -882,7 +843,7 @@ await test.skip('includes', async (t) => { // filtering refs await db - .query('user') + .query2('user') .include('*') .filter('bestBud.name', 'includes', 'Jose') .get() @@ -890,7 +851,7 @@ await test.skip('includes', async (t) => { // filtering multi refs await db - .query('user') + .query2('user') .include( (q) => q('buddies').include('*').filter('name', 'includes', 'Jose'), '*', @@ -902,13 +863,12 @@ await test.skip('includes', async (t) => { JSON.stringify( ( await db - .query('user') + .query2('user') .include( (q) => q('buddies').include('*').filter('name', 'includes', 'Jose'), '*', ) .get() - ).filter((u) => u.buddies.length > 0), ), ) @@ -938,6 +898,6 @@ await test('lt x leq', async (t) => { red: 4, blue: 6, }) - const b = await db.query('bucket').filter('red', '<', 4).get() + const b = await db.query2('bucket').filter('red', '<', 4).get() equal(b.length, 1, 'lt must be different than leq') }) diff --git a/test/filter/or.ts b/test/filter/or.ts index ae18f0aa50..202688f6ba 100644 --- a/test/filter/or.ts +++ b/test/filter/or.ts @@ -24,7 +24,7 @@ await test('filter or', async (t) => { } deepEqual( - await db.query('user').filter('nr', '>', 8).or('nr', '<', 1).get(), + await db.query2('user').filter('nr', '>', 8).or('nr', '<', 1).get(), [ { id: 1, @@ -40,7 +40,7 @@ await test('filter or', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('nr', '>', 8) .or((t) => { t.filter('nr', '<', 1).or('nr', '=', 5) @@ -65,7 +65,7 @@ await test('filter or', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('nr', '>', 8) .or('nr', '<', 1) .or('nr', '=', 5) @@ -89,7 +89,7 @@ await test('filter or', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('nr', '>', 8) .or(() => {}) .get(), diff --git a/test/filter/references.ts b/test/filter/references.ts index 5377033abe..7882b514ec 100644 --- a/test/filter/references.ts +++ b/test/filter/references.ts @@ -52,7 +52,7 @@ await test('filter references drones', async (t) => { const user = 1 const drones = await db - .query('user') + .query2('user') .include((s) => s('workspaces').include((s) => s('drones').include('*').filter('workspace.users', 'includes', user), @@ -62,7 +62,7 @@ await test('filter references drones', async (t) => { .get() const drones2 = await db - .query('drone') + .query2('drone') .filter('workspace.users', 'includes', user) .get() diff --git a/test/filter/referencesField.ts b/test/filter/referencesField.ts index 854e6cfcbf..ce9006ebbd 100644 --- a/test/filter/referencesField.ts +++ b/test/filter/referencesField.ts @@ -34,7 +34,7 @@ await test('filter references shortcut', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('name', 'age', 'friends') .filter('friends.age', '<', 40) .get(), @@ -51,7 +51,7 @@ await test('filter references shortcut', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('name', 'age', 'friends') .filter('friends.age', '>', 40) .get(), @@ -68,7 +68,7 @@ await test('filter references shortcut', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('name', 'age', 'friends') .filter('friends[*].age', '>', 40) .get(), @@ -85,7 +85,7 @@ await test('filter references shortcut', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include('name', 'age', 'friends') .filter('friends[0].age', '>', 40) .get(), @@ -106,7 +106,7 @@ await test('filter references shortcut', async (t) => { deepEqual( await db - .query('user', mrA) + .query2('user', mrA) .include('name', 'age', 'friends') .filter('friends[-1].age', '>', 100) .get(), @@ -133,7 +133,7 @@ await test('filter references shortcut', async (t) => { deepEqual( await db - .query('user', mrA) + .query2('user', mrA) .include('name', 'age', 'friends') .filter('friends[2].age', '=', 93) .get(), diff --git a/test/filter/string.ts b/test/filter/string.ts index c3910bc609..8be4bb7785 100644 --- a/test/filter/string.ts +++ b/test/filter/string.ts @@ -76,12 +76,12 @@ await test('variable size (string/binary)', async (t) => { await db.drain() deepEqual( - await db - .query('article') - .filter('stuff', '=', ENCODER.encode('#' + 2)) - .include('name', 'stuff', 'derp', 'type') - .range(0, 10) - .get(), + await db + .query2('article') + .filter('stuff', '=', ENCODER.encode('#' + 2)) + .include('name', 'stuff', 'derp', 'type') + .range(0, 10) + .get(), [ { id: 3, @@ -96,7 +96,7 @@ await test('variable size (string/binary)', async (t) => { const len = ( await db - .query('article') + .query2('article') .filter('stuff', 'includes', new Uint8Array([55, 57])) .range(0, 100) .get() @@ -127,7 +127,7 @@ await test('variable size (string/binary)', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('derp', 'includes', ENCODER.encode('vitorio')) .include('id') .get() @@ -139,7 +139,7 @@ await test('variable size (string/binary)', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('derp', 'includes', ENCODER.encode('xx')) .include('id') .get() @@ -151,7 +151,7 @@ await test('variable size (string/binary)', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('derp', 'includes', q) .include('id') .get() @@ -163,7 +163,7 @@ await test('variable size (string/binary)', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('derp', '=', largeDerp) .include('id') .range(0, 1e3) @@ -176,7 +176,7 @@ await test('variable size (string/binary)', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', '=', italy) .include('id') .range(0, 1e3) @@ -216,7 +216,7 @@ await test('has compressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', n) .include('id') .range(0, 1e3) @@ -267,7 +267,7 @@ await test('has uncompressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', 'derp derp derp', { lowerCase: true }) .include('id') .range(0, 1e3) @@ -279,7 +279,7 @@ await test('has uncompressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', 'derp derp derp') .include('id') .range(0, 1e3) @@ -290,7 +290,7 @@ await test('has uncompressed', async (t) => { deepEqual( await db - .query('italy') + .query2('italy') .filter('headline', 'includes', 'pager') .include('id', 'headline') .range(0, 1e3) @@ -309,7 +309,7 @@ await test('has uncompressed', async (t) => { deepEqual( await db - .query('italy') + .query2('italy') .filter('headline', 'includes', 'Pager', { lowerCase: true }) .include('id', 'headline') .range(0, 1e3) @@ -328,7 +328,7 @@ await test('has uncompressed', async (t) => { deepEqual( await db - .query('italy') + .query2('italy') .filter('headline', 'includes', 'refugee', { lowerCase: true }) .include('id', 'headline') .range(0, 1e3) @@ -338,7 +338,7 @@ await test('has uncompressed', async (t) => { deepEqual( await db - .query('italy') + .query2('italy') .filter('headline', 'includes', 'gaza', { lowerCase: true }) .include('id', 'headline') .range(0, 1e3) @@ -379,16 +379,15 @@ await test('main has (string/binary)', async (t) => { stuff, derp: new Uint8Array([1, 2, 3, 4]), } - deepEqual(await db.query('article').get(), [derpResult]) + deepEqual(await db.query2('article').get(), [derpResult]) + deepEqual(await db.query2('article').filter('stuff', '=', stuff).get(), [ + derpResult, + ]) deepEqual( - await db.query('article').filter('stuff', '=', stuff).get(), - [derpResult], - ) - deepEqual( - await db - .query('article') - .filter('derp', 'includes', new Uint8Array([4])) - .get(), + await db + .query2('article') + .filter('derp', 'includes', new Uint8Array([4])) + .get(), [derpResult], ) }) @@ -421,7 +420,7 @@ await test('has normalized uncompressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', 'aaaaaa', { lowerCase: true }) .include('id') .range(0, 1e5) @@ -457,7 +456,7 @@ await test('has normalized compressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', 'aaaaa', { lowerCase: true }) .include('id', 'body') .range(0, 1e3) @@ -502,7 +501,7 @@ await test('has OR uncompressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', ['aaaaaaaaaaa', 'bbbbbb'], { lowerCase: true, }) // ['aaa', 'bbb', 'ccc', 'eee'] @@ -515,7 +514,7 @@ await test('has OR uncompressed', async (t) => { deepEqual( await db - .query('italy') + .query2('italy') .filter('title', 'includes', ['gaza', 'tubbies'], { lowerCase: true }) .include('id', 'title') .range(0, 1e3) @@ -531,7 +530,7 @@ await test('has OR uncompressed', async (t) => { deepEqual( await db - .query('italy') + .query2('italy') .filter('title', 'includes', ['crisis', 'refugee'], { lowerCase: true }) .include('id', 'title') .range(0, 1e3) @@ -572,7 +571,7 @@ await test('has OR compressed', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', 'includes', ['aaaaaaaaaaa', 'bbbbbbbb'], { lowerCase: true, }) // ['aaa', 'bbb', 'ccc', 'eee'] @@ -617,7 +616,7 @@ await test('OR equal', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', '=', [derpItaly, 'derp', italy]) .range(0, 1e3) .get() @@ -654,7 +653,7 @@ await test('OR equal main', async (t) => { equal( ( await db - .query('italy') + .query2('italy') .filter('body', '=', ['xx', 'bb']) .range(0, 1e3) .get() @@ -711,7 +710,7 @@ await test('includes and neq', async (t) => { deepEqual( await db - .query('ent') + .query2('ent') .filter('country', 'includes', ['Italy', 'Germany']) .filter('city', '!=', 'Berlin') .get(), @@ -743,7 +742,7 @@ await test('empty string', async (t) => { const user2 = db.create('user', {}) const user3 = db.create('user', { potato: '' }) deepEqual( - await db.query('user').filter('potato', '=', '').get(), + await db.query2('user').filter('potato', '=', '').get(), [ { id: 2, @@ -782,7 +781,7 @@ await test('empty string fixed', async (t) => { const user2 = db.create('user', { region: 'XX', city: 'Amsterdam' }) const user3 = db.create('user', { potato: 'flap', city: 'Rome' }) deepEqual( - await db.query('user').filter('potato', '!=', '').get(), + await db.query2('user').filter('potato', '!=', '').get(), [ { id: 1, region: 'AA', potato: 'cool', city: '' }, { id: 3, region: '', potato: 'flap', city: 'Rome' }, @@ -792,7 +791,7 @@ await test('empty string fixed', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('potato', '!=', '') .filter('region', '!=', '') .get(), @@ -803,7 +802,7 @@ await test('empty string fixed', async (t) => { const user4 = db.create('user', { region: 'YY', city: 'Denver' }) deepEqual( await db - .query('user') + .query2('user') .filter('potato', '=', '') .filter('region', '!=', '') .filter('city', '=', 'Amsterdam') diff --git a/test/hooks.ts b/test/hooks.ts index 56567fed50..81692ef3b2 100644 --- a/test/hooks.ts +++ b/test/hooks.ts @@ -67,7 +67,7 @@ await test('hooks - undefined values', async (t) => { age: 25, }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, name: 'youzi', @@ -80,7 +80,7 @@ await test('hooks - undefined values', async (t) => { }, ]) - deepEqual(await db.query('user').filter('age', '<', 50).get(), [ + deepEqual(await db.query2('user').filter('age', '<', 50).get(), [ { id: 2, name: 'james', @@ -92,7 +92,7 @@ await test('hooks - undefined values', async (t) => { age: 31, }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, name: 'youzi', @@ -105,7 +105,7 @@ await test('hooks - undefined values', async (t) => { }, ]) - deepEqual(await db.query('user').filter('age', '<', 50).get(), [ + deepEqual(await db.query2('user').filter('age', '<', 50).get(), [ { id: 1, name: 'youzi', @@ -163,7 +163,7 @@ await test('hooks - private nodes', async (t) => { age: 25, }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, private: true, @@ -181,7 +181,7 @@ await test('hooks - private nodes', async (t) => { private: false, }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, name: 'youzi', @@ -233,7 +233,7 @@ await test('hooks - as SQL CHECK constraints', async (t) => { }), ) - deepEqual((await db.query('user').get()).length, 0) + deepEqual((await db.query2('user').get()).length, 0) }) await test('property modify hooks', async (t) => { @@ -287,7 +287,7 @@ await test('property modify hooks', async (t) => { city: 'wut', }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, age: 21, name: 'youzi', city: 'Snurko' }, ]) @@ -295,7 +295,7 @@ await test('property modify hooks', async (t) => { city: 'Fail', }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, age: 21, name: 'youzi', city: 'Success' }, ]) @@ -303,7 +303,7 @@ await test('property modify hooks', async (t) => { city: 'ignore', }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, age: 21, name: 'youzi', city: 'Success' }, ]) }) @@ -355,7 +355,7 @@ await test('property read hooks', async (t) => { city: 'wut', }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, age: 21 * 2, @@ -414,7 +414,7 @@ await test('aggregate hooks', async (t) => { age: 100, }) - equal((await db.query('user').sum('age').get()).age.sum, 21) + equal((await db.query2('user').sum('age').get()).age.sum, 21) }) await test('search hooks', async (t) => { @@ -464,7 +464,7 @@ await test('search hooks', async (t) => { age: 100, }) - equal((await db.query('user').search('youzi').get()).length, 1) + equal((await db.query2('user').search('youzi').get()).length, 1) }) await test('groupBy hooks', async (t) => { @@ -514,7 +514,7 @@ await test('groupBy hooks', async (t) => { age: 100, }) - equal(await db.query('user').groupBy('name').sum('age').get(), { + equal(await db.query2('user').groupBy('name').sum('age').get(), { youzi: { age: { sum: 21 } }, }) }) @@ -570,7 +570,7 @@ await test('filter hooks', async (t) => { age: 100, }) - equal(await db.query('user').filter('name', '=', 'youzi').get(), [ + equal(await db.query2('user').filter('name', '=', 'youzi').get(), [ { id: 1, age: 21, name: 'youzi' }, ]) }) @@ -622,7 +622,7 @@ await test('include hooks', async (t) => { age: 100, }) - equal(await db.query('user').include('name', 'age').get(), [ + equal(await db.query2('user').include('name', 'age').get(), [ { id: 1, age: 21, name: 'youzi' }, ]) }) @@ -662,7 +662,7 @@ await test('upsert calls create and/or update hooks', async (t) => { age: 21, }) - const results1 = await db.query('user').get() + const results1 = await db.query2('user').get() equal(results1.length, 1) @@ -675,7 +675,7 @@ await test('upsert calls create and/or update hooks', async (t) => { age: 45, }) - const results2 = await db.query('user').get() + const results2 = await db.query2('user').get() equal(results2.length, 1) equal(results2[0].createdString != 0, true) equal(results2[0].updatedString != 0, true) diff --git a/test/idOffset.ts b/test/idOffset.ts index 9333cc550b..74b6e90731 100644 --- a/test/idOffset.ts +++ b/test/idOffset.ts @@ -31,7 +31,7 @@ await test('idOffset', async (t) => { } await db.drain() - const allUsers1 = await db.query('user').get() + const allUsers1 = await db.query2('user').get() let id = 0 console.log(allUsers1.length) diff --git a/test/include/include.ts b/test/include/include.ts index b765928f9d..45f177eaaa 100644 --- a/test/include/include.ts +++ b/test/include/include.ts @@ -29,7 +29,7 @@ await test('include ', async (t) => { db.create('user', { nr: 3 }) deepEqual( - await db.query('user').include([]).range(0, 5).get(), + await db.query2('user').include([]).range(0, 5).get(), [ { id: 1, @@ -44,10 +44,10 @@ await test('include ', async (t) => { 'empty array should return no fields', ) - equal((await db.query('user', 1).get()).id, 1) - //equal((await db.query('user', 1).get()).queryId, 3978712180) - equal((await db.query('user').get()).checksum, 2149520223) - equal((await db.query('user').get()).version, 4507870634704934) + equal((await db.query2('user', 1).get()).id, 1) + //equal((await db.query2('user', 1).get()).queryId, 3978712180) + equal((await db.query2('user').get()).checksum, 2149520223) + equal((await db.query2('user').get()).version, 4507870634704934) }) await test('main', async (t) => { @@ -78,7 +78,7 @@ await test('main', async (t) => { }) deepEqual( - await db.query('user').range(0, 5).get(), + await db.query2('user').range(0, 5).get(), [{ id: 1, c: 10, d: 32, a: 'Derp!', b: 250 }], 'should return correct fields', ) diff --git a/test/include/includeMeta.ts b/test/include/includeMeta.ts index ff9bc6478a..80dad62563 100644 --- a/test/include/includeMeta.ts +++ b/test/include/includeMeta.ts @@ -42,7 +42,7 @@ await test('meta for selva string', async (t) => { }, }) - deepEqual(await db.query('item').include('name', { meta: true }).get(), [ + deepEqual(await db.query2('item').include('name', { meta: true }).get(), [ { id: 1, name: { @@ -57,7 +57,7 @@ await test('meta for selva string', async (t) => { await db.create('item', {}) - deepEqual(await db.query('item').include('name', { meta: true }).get(), [ + deepEqual(await db.query2('item').include('name', { meta: true }).get(), [ { id: 1, name: { @@ -74,7 +74,7 @@ await test('meta for selva string', async (t) => { }, ]) - deepEqual(await db.query('item').include('name', { meta: 'only' }).get(), [ + deepEqual(await db.query2('item').include('name', { meta: 'only' }).get(), [ { id: 1, name: { @@ -100,7 +100,7 @@ await test('meta for selva string', async (t) => { }) deepEqual( - await db.query('item').include('items.$edgeName', { meta: 'only' }).get(), + await db.query2('item').include('items.$edgeName', { meta: 'only' }).get(), [ { id: 1, @@ -134,7 +134,7 @@ await test('meta for selva string', async (t) => { 'Edge meta', ) - deepEqual(await db.query('item').include('email', { meta: 'only' }).get(), [ + deepEqual(await db.query2('item').include('email', { meta: 'only' }).get(), [ { id: 1, email: { @@ -150,7 +150,7 @@ await test('meta for selva string', async (t) => { }, ]) - deepEqual(await db.query('item').include('email', { meta: true }).get(), [ + deepEqual(await db.query2('item').include('email', { meta: true }).get(), [ { id: 1, email: { @@ -169,7 +169,7 @@ await test('meta for selva string', async (t) => { await db.update('item', 1, { name: italy }) - deepEqual(await db.query('item').include('name', { meta: true }).get(), [ + deepEqual(await db.query2('item').include('name', { meta: true }).get(), [ { id: 1, name: { @@ -186,7 +186,7 @@ await test('meta for selva string', async (t) => { }, ]) - deepEqual(await db.query('item').include('body', { meta: true }).get(), [ + deepEqual(await db.query2('item').include('body', { meta: true }).get(), [ { id: 1, body: { @@ -222,7 +222,7 @@ await test('meta for selva string', async (t) => { }) deepEqual( - await db.query('item').include('body', { meta: true }).get(), + await db.query2('item').include('body', { meta: true }).get(), [ { id: 1, @@ -261,7 +261,7 @@ await test('meta for selva string', async (t) => { ) deepEqual( - await db.query('item').include('body', { meta: 'only' }).get(), + await db.query2('item').include('body', { meta: 'only' }).get(), [ { id: 1, @@ -297,7 +297,11 @@ await test('meta for selva string', async (t) => { ) deepEqual( - await db.query('item').include('body', { meta: 'only' }).locale('it').get(), + await db + .query2('item') + .include('body', { meta: 'only' }) + .locale('it') + .get(), [ { id: 1, @@ -316,7 +320,11 @@ await test('meta for selva string', async (t) => { ) deepEqual( - await db.query('item').include('body', { meta: 'only' }).locale('en').get(), + await db + .query2('item') + .include('body', { meta: 'only' }) + .locale('en') + .get(), [ { id: 1, diff --git a/test/include/includeNested.ts b/test/include/includeNested.ts index 7d1c5993e9..32238223e5 100644 --- a/test/include/includeNested.ts +++ b/test/include/includeNested.ts @@ -29,7 +29,7 @@ await test('include */**', async (t) => { db.create('user', { nr: 3 }) deepEqual( - await db.query('user').include('*', '**').range(0, 5).get(), + await db.query2('user').include('*', '**').range(0, 5).get(), [ { id: 1, @@ -63,7 +63,7 @@ await test('include */**', async (t) => { ) deepEqual( - await db.query('user').include('friends.*').range(0, 5).get(), + await db.query2('user').include('friends.*').range(0, 5).get(), [ { id: 1, diff --git a/test/include/includeSlice.ts b/test/include/includeSlice.ts index be86009e44..49b48b5652 100644 --- a/test/include/includeSlice.ts +++ b/test/include/includeSlice.ts @@ -60,12 +60,12 @@ await test('slice string / text', async (t) => { }, }) - const q = await db.query('item', 1).get() + const q = await db.query2('item', 1).get() equal(q.id, 1) deepEqual( await db - .query('item', id1) + .query2('item', id1) .include('name', { end: 1, meta: true, @@ -124,14 +124,14 @@ await test('slice string / text', async (t) => { ) deepEqual( - await db.query('item', id1).include('body', { end: 3 }).get(), + await db.query2('item', id1).include('body', { end: 3 }).get(), { id: 1, body: { en: '🤪🇺🇸🇿🇼', fi: 'fin', it: 'abc' } }, 'Text all + chars', ) deepEqual( await db - .query('item', id1) + .query2('item', id1) .include('body.fi', { end: 3 }, 'body.en', { end: 3 }) .get(), { id: 1, body: { en: '🤪🇺🇸🇿🇼', fi: 'fin' } }, @@ -139,14 +139,14 @@ await test('slice string / text', async (t) => { ) deepEqual( - await db.query('item', id1).include('body', { end: 3 }).locale('en').get(), + await db.query2('item', id1).include('body', { end: 3 }).locale('en').get(), { id: 1, body: '🤪🇺🇸🇿🇼' }, 'Text specific locale', ) deepEqual( await db - .query('item', id1) + .query2('item', id1) .include('body', { end: 4, bytes: true }) .locale('en') .get(), @@ -155,7 +155,10 @@ await test('slice string / text', async (t) => { ) deepEqual( - await db.query('item', id1).include('body.en', { end: 3 }, 'body.fi').get(), + await db + .query2('item', id1) + .include('body.en', { end: 3 }, 'body.fi') + .get(), { id: 1, body: { en: '🤪🇺🇸🇿🇼', fi: 'finland 🇫🇮! this is finland!' } }, 'Different ends per language', ) diff --git a/test/include/referencesField.ts b/test/include/referencesField.ts index 4fa1a827f7..2ae03d48bc 100644 --- a/test/include/referencesField.ts +++ b/test/include/referencesField.ts @@ -37,27 +37,27 @@ await test('references shortcut', async (t) => { } deepEqual( - await db.query('user', mrA).include('name', 'age', 'friends[0].age').get(), + await db.query2('user', mrA).include('name', 'age', 'friends[0].age').get(), { id: 2, age: 50, name: 'Mr a', friends: [{ id: 1, age: 25 }] }, '[0]', ) deepEqual( - await db.query('user').at(0).get(), + await db.query2('user').at(0).get(), { id: 1, age: 25, name: 'Mr b' }, '.at(0)', ) deepEqual( - await db.query('user').at(3).get(), + await db.query2('user').at(3).get(), { id: 4, age: 93, name: 'Mr 1' }, '.at(3)', ) - // await db.query('user').range(-10, -1).get().inspect() + // await db.query2('user').range(-10, -1).get().inspect() // deepEqual( - // await db.query('user').range(-10, -1).get(), + // await db.query2('user').range(-10, -1).get(), // { id: 4, age: 93, name: 'Mr 1' }, // '.at(3)', // ) diff --git a/test/include/thread.perf.ts b/test/include/thread.perf.ts index 99d42b468b..67f97fef89 100644 --- a/test/include/thread.perf.ts +++ b/test/include/thread.perf.ts @@ -75,7 +75,7 @@ await test('include', async (t) => { let q: any = [] - const x = client.query('simple', 1) + const x = client.query2('simple', 1) registerQuery(x) @@ -96,7 +96,7 @@ await test('include', async (t) => { //.range(0, 1) q.push( client - .query('simple') + .query2('simple') .range(0, 5e6 + i) // .include('id') .count() @@ -164,7 +164,7 @@ await test('include', async (t) => { for (let i = 0; i < 5; i++) { q.push( client - .query('simple') + .query2('simple') .include('id', 'nr') // .range(0, 10e6 + i) .filter('nr', '=', 100 + i) @@ -183,7 +183,7 @@ await test('include', async (t) => { for (let i = 0; i < 5; i++) { q.push( client - .query('simple') + .query2('simple') .include('id', 'nr') // .range(0, 10e6 + i) .filter('nr', '=', 100 + i) @@ -203,7 +203,7 @@ await test('include', async (t) => { }) await client - .query('simple') + .query2('simple') .include('nr') // 'start', 'end', 'target' .filter('target.nr', '>', 1001) // .or('nr', '=', 100) @@ -322,7 +322,7 @@ await test.skip('default', async (t) => { await perf( async () => { await db - .query('user') + .query2('user') .include('name', 'bio', 'hack', 'hack2', 'book') .get() .inspect() diff --git a/test/insertOnly.ts b/test/insertOnly.ts index fab28dc6b4..d69b8bd636 100644 --- a/test/insertOnly.ts +++ b/test/insertOnly.ts @@ -18,7 +18,7 @@ await test('insert only => no delete', async (t) => { const a = await client.create('audit', { v: 100 }) await client.create('audit', { v: 100 }) await throws(() => client.delete('audit', a)) - deepEqual(await client.query('audit', a).get(), { id: 1, v: 100 }) + deepEqual(await client.query2('audit', a).get(), { id: 1, v: 100 }) }) await test('colvec requires insertOnly', async (t) => { diff --git a/test/instantModify.ts b/test/instantModify.ts index a86cd7fd09..e76532117c 100644 --- a/test/instantModify.ts +++ b/test/instantModify.ts @@ -109,12 +109,12 @@ await test.skip('instantModify', async (t) => { let j = 1000 await db2.start() while (j--) { - // db2.query('country').get() + // db2.query2('country').get() for (const update of updates) { db2.server.modify(update) } } - // console.log('AFTER:', await db2.query('country').get()) + // console.log('AFTER:', await db2.query2('country').get()) await db2.destroy() }) diff --git a/test/isModified.perf.ts b/test/isModified.perf.ts index 1a6bdb4d93..d837af166a 100644 --- a/test/isModified.perf.ts +++ b/test/isModified.perf.ts @@ -26,7 +26,7 @@ await test('isModified', async (t) => { const q: any = [] for (let i = 0; i < 10; i++) { - q.push(db.query('user').range(0, 5).get()) + q.push(db.query2('user').range(0, 5).get()) } const r = await Promise.all(q) diff --git a/test/json.ts b/test/json.ts index fd1b192099..afda9746e8 100644 --- a/test/json.ts +++ b/test/json.ts @@ -33,7 +33,7 @@ await test('json', async (t) => { await db.create('jsonDerulo', derulo) deepEqual( - await db.query('jsonDerulo').get(), + await db.query2('jsonDerulo').get(), [ { id: 1, @@ -48,7 +48,7 @@ await test('json', async (t) => { }) deepEqual( - await db.query('jsonDerulo').get(), + await db.query2('jsonDerulo').get(), [ { id: 1, ...derulo }, { id: 2, myJson: {}, name: '' }, @@ -62,7 +62,7 @@ await test('json', async (t) => { }) deepEqual( - await db.query('jsonDerulo').get(), + await db.query2('jsonDerulo').get(), [ { id: 1, ...derulo }, { id: 2, myJson: null, name: '' }, @@ -92,13 +92,13 @@ await test('json and crc32', async (t) => { article: 'a', }) - const checksum = (await db.query('user', user1).get()).checksum + const checksum = (await db.query2('user', user1).get()).checksum await db.update('user', user1, { article: 'b', }) - const checksum2 = (await db.query('user', user1).get()).checksum + const checksum2 = (await db.query2('user', user1).get()).checksum notEqual(checksum, checksum2, 'Checksum is not the same') }) diff --git a/test/locales.ts b/test/locales.ts index ded36f0513..f7011fec36 100644 --- a/test/locales.ts +++ b/test/locales.ts @@ -38,7 +38,7 @@ await test('locales', async (t) => { client.create('thing', payload) } - const things = await client.query('thing').get() + const things = await client.query2('thing').get() for (const thing of things) { const payload: typeof thing = { @@ -51,7 +51,7 @@ await test('locales', async (t) => { await client.drain() - const updatedThings = await client.query('thing').get() + const updatedThings = await client.query2('thing').get() for (const thing of updatedThings) { if (thing.string !== '') { diff --git a/test/mainAndEmptyStringFieldDelete.ts b/test/mainAndEmptyStringFieldDelete.ts index 738d22bb4c..ac8849815a 100644 --- a/test/mainAndEmptyStringFieldDelete.ts +++ b/test/mainAndEmptyStringFieldDelete.ts @@ -26,7 +26,7 @@ await test('main + empty', async (t) => { location: '', }) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, role: 'translator', location: '' }, ]) }) diff --git a/test/mem.ts b/test/mem.ts index d02ad694d5..b7130856fb 100644 --- a/test/mem.ts +++ b/test/mem.ts @@ -62,7 +62,7 @@ await test('mem', async (t) => { equal( ( await client - .query('data') + .query2('data') .include('b') .filter('b', 'exists') .range(0, amount) @@ -77,6 +77,9 @@ await test('mem', async (t) => { await client.drain() - equal((await client.query('data').range(0, 10e6).get()).length, (j + 1) * 2) + equal( + (await client.query2('data').range(0, 10e6).get()).length, + (j + 1) * 2, + ) } }) diff --git a/test/migration.ts b/test/migration.ts index 022d00c7d2..7fb8b20f94 100644 --- a/test/migration.ts +++ b/test/migration.ts @@ -85,7 +85,7 @@ await test('migration', async (t) => { await db.create('person', payload) } - // console.dir(await db.query('person').include('*', '**').get(), { + // console.dir(await db.query2('person').include('*', '**').get(), { // depth: null, // }) @@ -224,8 +224,8 @@ await test('migration', async (t) => { await db.setSchema(schema) } - const users = await db.query('user').get() - const people = await db.query('person').include('*', '**').get() + const users = await db.query2('user').get() + const people = await db.query2('person').include('*', '**').get() equal(users.length, 10) equal(people.length, 10) diff --git a/test/query-ast/aggregates.ts b/test/query-ast/aggregates.ts index 350f168b83..39803067a9 100644 --- a/test/query-ast/aggregates.ts +++ b/test/query-ast/aggregates.ts @@ -288,7 +288,7 @@ await test('group by', async (t) => { // console.log('🙈🙈🙈 ------------------------------- 🙈🙈🙈') // const r = await db - // .query('trip') + // .query2('trip') // // .count() // .sum('distance') // .groupBy('class', {}) diff --git a/test/queryResponse.ts b/test/queryResponse.ts index 123f45b9fe..c3a3c1e72b 100644 --- a/test/queryResponse.ts +++ b/test/queryResponse.ts @@ -25,7 +25,7 @@ await test('correct version', async (t) => { status: 'a', }) - const response = await db.query('user', user1).get() + const response = await db.query2('user', user1).get() equal( extractNumber(response.version), @@ -37,7 +37,7 @@ await test('correct version', async (t) => { status: 'b', }) - const response2 = await db.query('user', user1).get() + const response2 = await db.query2('user', user1).get() notEqual(response.version, response2.version) }) diff --git a/test/range.ts b/test/range.ts index 9a7cfed4f0..7da4b1ce63 100644 --- a/test/range.ts +++ b/test/range.ts @@ -62,11 +62,11 @@ await test('range', async (t) => { await db.drain() - deepEqual(await db.query('user').include('nr').range(1, 2).get(), [ + deepEqual(await db.query2('user').include('nr').range(1, 2).get(), [ { id: 2, nr: 2 }, ]) deepEqual( - await db.query('user').include('nr').sort('email').range(1, 2).get(), + await db.query2('user').include('nr').sort('email').range(1, 2).get(), [{ id: 2, nr: 2 }], ) }) @@ -97,6 +97,6 @@ await test('default range: 1000', async (t) => { }) } await db.drain() - const res = await db.query('user').get() + const res = await db.query2('user').get() equal(res.length, 1_000) }) diff --git a/test/raw.ts b/test/raw.ts index fe68916b63..0945b6716a 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -23,16 +23,15 @@ await test.skip('cardinality', async (t) => { uniqueSkills: ['juggling', 'cabaret'], }) const { uniqueSkills } = await db - .query('user', one) + .query2('user', one) .include('uniqueSkills', { raw: true }) .get() - await db.create('user', { uniqueSkills, }) - const [a, b] = await db.query('user').get() + const [a, b] = await db.query2('user').get() deepEqual(a.uniqueSkills, b.uniqueSkills) }) @@ -60,10 +59,9 @@ await test('string', async (t) => { resume: italy, }) const { name, role, resume } = await db - .query('user', one) + .query2('user', one) .include(['name', 'role', 'resume'], { raw: true }) .get() - await db.create('user', { name, @@ -71,7 +69,7 @@ await test('string', async (t) => { resume, }) - const [a, b] = await db.query('user').get() + const [a, b] = await db.query2('user').get() deepEqual(a.name, b.name) deepEqual(a.role, b.role) deepEqual(a.resume, b.resume) diff --git a/test/references/references.ts b/test/references/references.ts index 07a0339c53..f412bdbb5a 100644 --- a/test/references/references.ts +++ b/test/references/references.ts @@ -62,7 +62,7 @@ await test('references', async (t) => { await db.drain() - deepEqual(await db.query('article').include('contributors.name').get(), [ + deepEqual(await db.query2('article').include('contributors.name').get(), [ { id: await strudelArticle, contributors: [{ id: await mrSnurp, name: 'Mr snurp' }], @@ -76,7 +76,7 @@ await test('references', async (t) => { }, ]) - deepEqual(await db.query('user').include('articles.name').get(), [ + deepEqual(await db.query2('user').include('articles.name').get(), [ { id: 1, articles: [ @@ -137,7 +137,7 @@ await test('one to many', async (t) => { } await db.drain() - deepEqual(await db.query('user').include('resources').get(), [ + deepEqual(await db.query2('user').include('resources').get(), [ { id: 1, resources: [ @@ -165,7 +165,7 @@ await test('one to many', async (t) => { }, ]) - deepEqual(await db.query('user').include('resources.name').get(), [ + deepEqual(await db.query2('user').include('resources.name').get(), [ { id: 1, resources: [ @@ -231,7 +231,7 @@ await test('one to many really', async (t) => { resources: [cpu, kbd, mouse, fd], }) await db.drain() - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -255,7 +255,7 @@ await test('one to many really', async (t) => { await db.update('user', user, { resources: [cpu, kbd, mouse], }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -276,7 +276,7 @@ await test('one to many really', async (t) => { await db.update('user', user, { resources: [cpu, kbd, mouse], }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -297,7 +297,7 @@ await test('one to many really', async (t) => { await db.update('user', user, { resources: [cpu, kbd, mouse, fd], }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -322,7 +322,7 @@ await test('one to many really', async (t) => { await db.update('user', user, { resources: [kbd, cpu, fd, mouse], }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -348,7 +348,7 @@ await test('one to many really', async (t) => { await db.update('resource', joy, { owner: user }) await db.update('resource', joy, { owner: user }) await db.update('resource', joy, { owner: user }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -382,7 +382,7 @@ await test('one to many really', async (t) => { update: [joy], }, }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -413,7 +413,7 @@ await test('one to many really', async (t) => { update: [joy, kbd, cpu, fd, mouse], }, }) - deepEqual(await db.query('user', user).include('resources').get(), { + deepEqual(await db.query2('user', user).include('resources').get(), { id: 1, resources: [ { @@ -496,7 +496,7 @@ await test('update', async (t) => { contributors: [flippie], }) await db.drain() - deepEqual(await db.query('article').include('contributors.name').get(), [ + deepEqual(await db.query2('article').include('contributors.name').get(), [ { id: 1, contributors: [ @@ -575,7 +575,7 @@ await test('filter', async (t) => { await db.drain() deepEqual( - await db.query('article', strudelArticle).include('contributors').get(), + await db.query2('article', strudelArticle).include('contributors').get(), { id: 1, contributors: [ @@ -590,7 +590,7 @@ await test('filter', async (t) => { deepEqual( await db - .query('article', strudelArticle) + .query2('article', strudelArticle) .include((q) => q('contributors').include('name').filter('flap', '>', 25)) .get(), { @@ -605,7 +605,7 @@ await test('filter', async (t) => { deepEqual( await db - .query('article', strudelArticle) + .query2('article', strudelArticle) .include((q) => { q('contributors').include('flap') q('contributors').include('name') @@ -622,7 +622,7 @@ await test('filter', async (t) => { deepEqual( await db - .query('article', strudelArticle) + .query2('article', strudelArticle) .include((select) => { select('contributors') .include('name') @@ -738,7 +738,7 @@ await test('filter', async (t) => { // }) // console.dir( -// await db.query('contestant').include('*', '**').get(), +// await db.query2('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -747,7 +747,7 @@ await test('filter', async (t) => { // const contestant1 = await db.create('contestant') // console.dir( -// await db.query('contestant').include('*', '**').get(), +// await db.query2('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -756,7 +756,7 @@ await test('filter', async (t) => { // const country1 = await db.create('country', { name: 'xxx' }) // console.dir( -// await db.query('contestant').include('*', '**').get(), +// await db.query2('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -765,7 +765,7 @@ await test('filter', async (t) => { // console.log( // '--->', // await db -// .query('contestant', contestant1) +// .query2('contestant', contestant1) // .include('*', '**') // .get() // , @@ -778,11 +778,11 @@ await test('filter', async (t) => { // console.log( // '--->', -// await db.query('country', country1).include('*', '**').get(), +// await db.query2('country', country1).include('*', '**').get(), // ) // console.dir( -// await db.query('contestant').include('*', '**').get(), +// await db.query2('contestant').include('*', '**').get(), // { // depth: null, // }, @@ -792,7 +792,7 @@ await test('filter', async (t) => { // '--->', // await db // // @ts-ignore -// .query('contestant', { +// .query2('contestant', { // id: 1, // maxVotes: 0, // price: 0, @@ -844,7 +844,7 @@ await test('single ref save and load', async (t) => { invitedBy: 2, }) - deepEqual(await db.query('user').include('email', 'invitedBy').get(), [ + deepEqual(await db.query2('user').include('email', 'invitedBy').get(), [ { id: 1, email: '1@saulx.com', invitedBy: null }, { id: 2, email: '2@saulx.com', invitedBy: null }, { @@ -915,8 +915,8 @@ await test('single2many - update refs', async (t) => { reviews: [review1, review2, review3], }) - const products = await db.query('product').include('*', '**').get() - const reviews = await db.query('review').include('*', '**').get() + const products = await db.query2('product').include('*', '**').get() + const reviews = await db.query2('review').include('*', '**').get() deepEqual(products, [ { id: 1, reviews: [] }, @@ -972,7 +972,7 @@ await test('reference to a non-existing node', async (t) => { articles: [1], }) // RFE Is this the correct behavior - deepEqual(await db.query('user', mrSnurp).include('**').get(), { + deepEqual(await db.query2('user', mrSnurp).include('**').get(), { id: 1, articles: [], }) @@ -980,7 +980,7 @@ await test('reference to a non-existing node', async (t) => { const article = await db.create('article', {}) deepEqual(article, 1) - deepEqual(await db.query('user', mrSnurp).include('**').get(), { + deepEqual(await db.query2('user', mrSnurp).include('**').get(), { id: 1, articles: [], }) diff --git a/test/references/referencesIndex.ts b/test/references/referencesIndex.ts index 82866c961d..1bd82db78c 100644 --- a/test/references/referencesIndex.ts +++ b/test/references/referencesIndex.ts @@ -40,17 +40,14 @@ await test('references modify', async (t) => { await db.drain() - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 1, name: 'bob' }, - { id: 2, name: 'marie' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 1, name: 'bob' }, + { id: 2, name: 'marie' }, + ], + }) await db.update('user', john, { friends: { @@ -74,17 +71,14 @@ await test('references modify', async (t) => { }, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 2, name: 'marie' }, - { id: 1, name: 'bob' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 2, name: 'marie' }, + { id: 1, name: 'bob' }, + ], + }) const billy = db.create('user', { name: 'billy', @@ -101,18 +95,15 @@ await test('references modify', async (t) => { }, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 4, name: 'billy' }, - { id: 2, name: 'marie' }, - { id: 1, name: 'bob' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 4, name: 'billy' }, + { id: 2, name: 'marie' }, + { id: 1, name: 'bob' }, + ], + }) const malcolm = db.create('user', { name: 'malcolm', @@ -129,19 +120,16 @@ await test('references modify', async (t) => { }, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 4, name: 'billy' }, - { id: 2, name: 'marie' }, - { id: 5, name: 'malcolm' }, - { id: 1, name: 'bob' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 4, name: 'billy' }, + { id: 2, name: 'marie' }, + { id: 5, name: 'malcolm' }, + { id: 1, name: 'bob' }, + ], + }) await db.update('user', john, { friends: { @@ -154,19 +142,16 @@ await test('references modify', async (t) => { }, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 4, name: 'billy' }, - { id: 2, name: 'marie' }, - { id: 1, name: 'bob' }, - { id: 5, name: 'malcolm' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 4, name: 'billy' }, + { id: 2, name: 'marie' }, + { id: 1, name: 'bob' }, + { id: 5, name: 'malcolm' }, + ], + }) await db.update('user', john, { friends: { @@ -174,30 +159,24 @@ await test('references modify', async (t) => { }, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 4, name: 'billy' }, - { id: 1, name: 'bob' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 4, name: 'billy' }, + { id: 1, name: 'bob' }, + ], + }) await db.update('user', john, { friends: null, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [], + }) }) await test('index>len', async (t) => { @@ -249,15 +228,12 @@ await test('index>len', async (t) => { }, }) - deepEqual( - (await db.query('user', john).include('*', 'friends').get()), - { - id: 3, - name: 'john', - friends: [ - { id: 2, name: 'marie' }, - { id: 1, name: 'bob' }, - ], - }, - ) + deepEqual(await db.query2('user', john).include('*', 'friends').get(), { + id: 3, + name: 'john', + friends: [ + { id: 2, name: 'marie' }, + { id: 1, name: 'bob' }, + ], + }) }) diff --git a/test/references/referencesModify.ts b/test/references/referencesModify.ts index 8881b9e561..907dda0cf2 100644 --- a/test/references/referencesModify.ts +++ b/test/references/referencesModify.ts @@ -51,7 +51,7 @@ await test('references modify', async (t) => { await db.drain() deepEqual( - (await db.query('user').include('*', 'friends').get()), + await db.query2('user').include('*', 'friends').get(), [ { id: 1, name: 'bob', friends: [] }, { id: 2, name: 'marie', friends: [{ id: 3, name: 'john' }] }, @@ -69,7 +69,7 @@ await test('references modify', async (t) => { await db.drain() deepEqual( - (await db.query('user').include('*', 'friends').get()), + await db.query2('user').include('*', 'friends').get(), [ { id: 1, name: 'bob', friends: [{ id: 3, name: 'john' }] }, { id: 2, name: 'marie', friends: [{ id: 3, name: 'john' }] }, @@ -90,7 +90,7 @@ await test('references modify', async (t) => { }) deepEqual( - (await db.query('user').include('*', 'friends').get()), + await db.query2('user').include('*', 'friends').get(), [ { id: 1, name: 'bob', friends: [] }, { id: 2, name: 'marie', friends: [] }, @@ -200,22 +200,13 @@ await test('reference move', async (t) => { bees: [b2], }) - deepEqual( - (await db.query('a').include('bees').get())[0].bees[0].id, - 2, - ) + deepEqual((await db.query2('a').include('bees').get())[0].bees[0].id, 2) await db.update('a', a, { bees: [b2, b2], }) - deepEqual( - (await db.query('a').include('bees').get())[0].bees.length, - 1, - ) - deepEqual( - (await db.query('a').include('bees').get())[0].bees[0].id, - 2, - ) + deepEqual((await db.query2('a').include('bees').get())[0].bees.length, 1) + deepEqual((await db.query2('a').include('bees').get())[0].bees[0].id, 2) }) // https://linear.app/1ce/issue/FDN-1735 @@ -263,7 +254,7 @@ await test('try to modify undefined refs', async (t) => { const a1 = db.create('actor', { name: 'Uma Thurman', movies: [m1, m2] }) const a2 = db.create('actor', { name: 'Jonh Travolta', movies: [m2] }) - //await db.query('movie').include('*', '**').get().inspect() + //await db.query2('movie').include('*', '**').get().inspect() await db.update('movie', m1, { actors: { delete: [a1] }, diff --git a/test/save/blockHash.ts b/test/save/blockHash.ts index e261d0304d..bc583fa6be 100644 --- a/test/save/blockHash.ts +++ b/test/save/blockHash.ts @@ -48,8 +48,8 @@ await test('isomorphic types have equal hashes', async (t) => { await client.drain() deepEqual( - (await client.query('article').get()).checksum, - (await client.query('story').get()).checksum, + (await client.query2('article').get()).checksum, + (await client.query2('story').get()).checksum, ) assert( native.equals( diff --git a/test/save/save.ts b/test/save/save.ts index 006ab78906..3ccbc77f20 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -90,8 +90,8 @@ await test('simple', async (t) => { hooks: getDefaultHooks(db2.server), }) - const a = await client.query('user').get() - const b = await client2.query('user').get() + const a = await client.query2('user').get() + const b = await client2.query2('user').get() deepEqual(b, a) const c = await client.create('user', { name: 'jerp' }) @@ -189,8 +189,8 @@ await test('refs', async (t) => { t.after(() => db2.destroy()) await db2.start() - const users1 = await db.query('user').include('group').get() - const users2 = await db2.query('user').include('group').get() + const users1 = await db.query2('user').include('group').get() + const users2 = await db2.query2('user').include('group').get() deepEqual(users1, users2) }) @@ -282,8 +282,8 @@ await test('text', async (t) => { t.after(() => db2.destroy()) await db2.start() - const articles1 = await db.query('article').get() - const articles2 = await db2.query('article').get() + const articles1 = await db.query2('article').get() + const articles2 = await db2.query2('article').get() deepEqual(articles1, articles2) }) @@ -354,8 +354,8 @@ await test.skip('db is drained before save', async (t) => { await db2.start() deepEqual( - await db2.query('person').include('name', 'books').get(), - await db.query('person').include('name', 'books').get(), + await db2.query2('person').include('name', 'books').get(), + await db.query2('person').include('name', 'books').get(), ) }) @@ -408,7 +408,7 @@ await test('create', async (t) => { await db2.start() t.after(() => db2.destroy()) - deepEqual(await db2.query('person').get(), await db.query('person').get()) + deepEqual(await db2.query2('person').get(), await db.query2('person').get()) }) await test('upsert', async (t) => { @@ -451,10 +451,10 @@ await test('upsert', async (t) => { hooks: getDefaultHooks(db2.server), }) - deepEqual(await client.query('person').get(), [ + deepEqual(await client.query2('person').get(), [ { id: 1, name: 'Joe', age: 42, alias: 'boss' }, ]) - deepEqual(await client2.query('person').get(), [ + deepEqual(await client2.query2('person').get(), [ { id: 1, name: 'Joe', age: 42, alias: 'boss' }, ]) }) @@ -509,8 +509,8 @@ await test('alias blocks', async (t) => { }) deepEqual( - await client2.query('person').get(), - await client.query('person').get(), + await client2.query2('person').get(), + await client.query2('person').get(), ) }) @@ -627,7 +627,7 @@ await test('simulated periodic save', async (t) => { // Change node using alias saved deepEqual( await client - .query('person') + .query2('person') .filter('alias', 'includes', 'slim') .include('alias', 'name') .get(), @@ -635,7 +635,7 @@ await test('simulated periodic save', async (t) => { ) deepEqual( await client2 - .query('person') + .query2('person') .filter('alias', 'includes', 'slim') .include('alias', 'name') .get(), @@ -645,7 +645,7 @@ await test('simulated periodic save', async (t) => { // Replace alias saved deepEqual( await client - .query('person') + .query2('person') .filter('alias', 'includes', 'slick') .include('alias', 'name') .get(), @@ -653,7 +653,7 @@ await test('simulated periodic save', async (t) => { ) deepEqual( await client2 - .query('person') + .query2('person') .filter('alias', 'includes', 'slick') .include('alias', 'name') .get(), @@ -663,7 +663,7 @@ await test('simulated periodic save', async (t) => { // Move alias saved deepEqual( await client - .query('person') + .query2('person') .filter('alias', 'includes', 'boss') .include('alias', 'name') .get(), @@ -671,7 +671,7 @@ await test('simulated periodic save', async (t) => { ) deepEqual( await client2 - .query('person') + .query2('person') .filter('alias', 'includes', 'boss') .include('alias', 'name') .get(), @@ -680,8 +680,8 @@ await test('simulated periodic save', async (t) => { // All have the same books deepEqual( - await client2.query('person').include('name', 'alias', 'books').get(), - await client.query('person').include('name', 'alias', 'books').get(), + await client2.query2('person').include('name', 'alias', 'books').get(), + await client.query2('person').include('name', 'alias', 'books').get(), ) }) @@ -745,7 +745,7 @@ await test('edge val', async (t) => { ], }, }) - //await client.query('phase').include('scenarios.$sequence').get().inspect() + //await client.query2('phase').include('scenarios.$sequence').get().inspect() await db.save() await client.update('phase', phase, { @@ -753,7 +753,7 @@ await test('edge val', async (t) => { delete: [scenario1], }, }) - //await client.query('phase').include('scenarios.$sequence').get().inspect() + //await client.query2('phase').include('scenarios.$sequence').get().inspect() }) await test('no mismatch', async (t) => { diff --git a/test/save/saveEdge.ts b/test/save/saveEdge.ts index e629cf142e..3c4006a9dc 100644 --- a/test/save/saveEdge.ts +++ b/test/save/saveEdge.ts @@ -40,7 +40,7 @@ await test('save edge', async (t) => { }, }) - deepEqual(await client.query('user', user2).include('**').get(), { + deepEqual(await client.query2('user', user2).include('**').get(), { id: 2, bestFriend: { id: 1, diff --git a/test/save/saveInterval.ts b/test/save/saveInterval.ts index debbaddcb0..a9deecd5c5 100644 --- a/test/save/saveInterval.ts +++ b/test/save/saveInterval.ts @@ -36,7 +36,7 @@ await test('saveInterval', async (t) => { await client.drain() await setTimeout(1e3) - const res1 = await client.query('user').get() + const res1 = await client.query2('user').get() await db.stop(true) @@ -50,7 +50,7 @@ await test('saveInterval', async (t) => { }) await db2.schemaIsSet() - const res2 = await client2.query('user').get() + const res2 = await client2.query2('user').get() deepEqual(res1, res2) }) diff --git a/test/save/saveRange.ts b/test/save/saveRange.ts index a91756fe20..7e60564bb9 100644 --- a/test/save/saveRange.ts +++ b/test/save/saveRange.ts @@ -60,7 +60,7 @@ await test('save simple range', async (t) => { age: 1337, }) await client.drain() - deepEqual(await client.query('user').include('age').range(0, 1).get(), [ + deepEqual(await client.query2('user').include('age').range(0, 1).get(), [ { id: 1, age: 1337, @@ -109,7 +109,7 @@ await test('save simple range', async (t) => { notEqual(firstHash, secondHash) equal(secondHash, thirdHash) - deepEqual(await client2.query('user').include('age').range(0, 1).get(), [ + deepEqual(await client2.query2('user').include('age').range(0, 1).get(), [ { id: 1, age: 1337, @@ -117,7 +117,7 @@ await test('save simple range', async (t) => { ]) deepEqual( await client2 - .query('user') + .query2('user') .include('age') .range(200000, 200000 + 1) .get(), @@ -129,7 +129,7 @@ await test('save simple range', async (t) => { ], ) - deepEqual(await client2.query('user').include('name').range(0, 2).get(), [ + deepEqual(await client2.query2('user').include('name').range(0, 2).get(), [ { id: 1, name: 'mr flop 1', @@ -142,7 +142,7 @@ await test('save simple range', async (t) => { deepEqual( await client2 - .query('user') + .query2('user') .include('name') .range(200_000, 200_000 + 2) .get(), diff --git a/test/scenarios/e-commerce.ts b/test/scenarios/e-commerce.ts index c5f10cfd38..4d7d8f06f4 100644 --- a/test/scenarios/e-commerce.ts +++ b/test/scenarios/e-commerce.ts @@ -332,7 +332,7 @@ await test('E-commerce Simulation', async (t) => { const queryType = Math.random() if (queryType < 0.1) { isSorted( - await db.query('user').sort('lastLogin', 'asc').get(), + await db.query2('user').sort('lastLogin', 'asc').get(), 'lastLogin', 'asc', ) @@ -341,7 +341,7 @@ await test('E-commerce Simulation', async (t) => { const categoryId = getRandom(categoryIds) if (categoryId) { await db - .query('product') + .query2('product') .filter('category', '=', categoryId) .sort('price', Math.random() > 0.5 ? 'asc' : 'desc') .include('name', 'price', 'stock') @@ -354,7 +354,7 @@ await test('E-commerce Simulation', async (t) => { if (userId) { // console.log({ userId }) await db - .query('user', userId) + .query2('user', userId) .include( 'name', 'viewedProducts.name', @@ -368,7 +368,7 @@ await test('E-commerce Simulation', async (t) => { const productId = getRandom(productIds) if (productId) { await db - .query('review') + .query2('review') .filter('product', '=', productId) .sort('rating', 'desc') .include('rating', 'comment', 'user.name') @@ -390,7 +390,7 @@ await test('E-commerce Simulation', async (t) => { } if (searchTerm) { await db - .query('product') + .query2('product') .search(searchTerm, 'name', 'description') .include('name', 'price') .range(0, 5) @@ -400,7 +400,7 @@ await test('E-commerce Simulation', async (t) => { // Get user by email (alias) const email = `user${getRandom(userIds)}@example.com` if (email) { - await db.query('user', { email }).get() + await db.query2('user', { email }).get() } } } @@ -484,7 +484,7 @@ await test('E-commerce Simulation', async (t) => { await wait(500) const finalProductCount = ( - await db.query('product').range(0, 10_000_000).get() + await db.query2('product').range(0, 10_000_000).get() ).length equal( diff --git a/test/scenarios/northwind.ts b/test/scenarios/northwind.ts index 37a9f54c02..b25c47bd1a 100644 --- a/test/scenarios/northwind.ts +++ b/test/scenarios/northwind.ts @@ -15,7 +15,7 @@ await test('Basic SQL', async (t) => { await createNorthwindDb(db) // 1. Retrieve all columns in the Region table. - const r1 = await db.query('region').include('*').get() + const r1 = await db.query2('region').include('*').get() deepEqual(r1, [ { id: 1, @@ -36,7 +36,7 @@ await test('Basic SQL', async (t) => { ]) // 2. Select the FirstName and LastName columns from the Employees table. - const r2 = await db.query('employees').include('firstName', 'lastName').get() + const r2 = await db.query2('employees').include('firstName', 'lastName').get() deepEqual(r2, [ { id: 1, lastName: 'Davolio', firstName: 'Nancy' }, { id: 2, lastName: 'Fuller', firstName: 'Andrew' }, @@ -52,7 +52,7 @@ await test('Basic SQL', async (t) => { // 3. Select the FirstName and LastName columns from the Employees table. // Sort by LastName. const r3 = await db - .query('employees') + .query2('employees') .include('firstName', 'lastName') .sort('lastName') .get() @@ -72,7 +72,7 @@ await test('Basic SQL', async (t) => { // expensive to cheapest. // Show OrderId, OrderDate, ShippedDate, CustomerId, and Freight. const r4 = await db - .query('orders') + .query2('orders') .include('orderDate', 'shippedDate', 'customer.id', 'freight') .sort('freight', 'desc') .range(0, 3) @@ -103,7 +103,7 @@ await test('Basic SQL', async (t) => { // 5. Create a report showing the title and the first and last name of all sales representatives. const r5 = await db - .query('employees') + .query2('employees') .include('title', 'firstName', 'lastName') .filter('title', '=', 'Sales Representative') .get() @@ -152,7 +152,7 @@ await test('Basic SQL', async (t) => { // 6a. Create a report showing the first and last names of all employees who have a region specified. const r6a = await db - .query('employees') + .query2('employees') .include('firstName', 'lastName', 'region') .filter('region', '!=', '') .get() @@ -170,7 +170,7 @@ await test('Basic SQL', async (t) => { // 6b. Create a report showing the first and last names of all employees who don't have a region specified. const r6b = await db - .query('employees') + .query2('employees') .include('firstName', 'lastName', 'region') .filter('region', '=', '') .get() @@ -189,7 +189,7 @@ await test('Basic SQL', async (t) => { // with a letter in the last half of the alphabet. // Sort by LastName in descending order. // TODO - // const r7 = await db.query('employees').include('firstName', 'lastName').filter('lastName', 'startsWith', ?? + // const r7 = await db.query2('employees').include('firstName', 'lastName').filter('lastName', 'startsWith', ?? // 8. Create a report showing the title of courtesy and the first and last name of all employees // whose title of courtesy begins with "M". @@ -199,7 +199,7 @@ await test('Basic SQL', async (t) => { // Seattle or Redmond. // TODO Impossible to OR const r9 = await db - .query('employees') + .query2('employees') .include('firstName', 'lastName', 'title', 'city', 'region') .filter('title', 'includes', 'Sales') .filter('region', '!=', '') @@ -240,7 +240,7 @@ await test('Basic SQL', async (t) => { // customers in Mexico or in any city in Spain except Madrid. // TODO Impossible const r10 = await db - .query('customers') + .query2('customers') .include('companyName', 'contactTitle', 'city', 'country') //.filter('country', 'includes', ['Mexico', 'Spain']) .filter('country', 'includes', ['Mexico', 'Spain']) @@ -309,7 +309,7 @@ await test('Basic SQL', async (t) => { // 12. Find the Total Number of Units Ordered of Product ID 3 const r12 = await db - .query('orderDetails') + .query2('orderDetails') .filter('product', '=', 3) .count() .get() @@ -320,7 +320,7 @@ await test('Basic SQL', async (t) => { ) // 13. Retrieve the number of employees in each city - const r13 = await db.query('employees').groupBy('city').count().get() + const r13 = await db.query2('employees').groupBy('city').count().get() deepEqual( r13, { @@ -339,7 +339,7 @@ await test('Basic SQL', async (t) => { // 15. Find the Companies (the CompanyName) that placed orders in 1997 const r15 = await db - .query('orders') + .query2('orders') .include('orderDate', 'customer.companyName') .filter('orderDate', '..', [ new Date('1997'), @@ -379,7 +379,7 @@ await test('Basic SQL', async (t) => { // Sort by Company Name. // TODO filter by field? const r17 = await db - .query('orders') + .query2('orders') .include( 'customer.companyName', 'employee.firstName', @@ -430,7 +430,10 @@ await test('Basic SQL', async (t) => { // SELECT * FROM Customers // WHERE country='Mexico'; - const r19 = await db.query('customers').filter('country', '=', 'Mexico').get() + const r19 = await db + .query2('customers') + .filter('country', '=', 'Mexico') + .get() deepEqual( r19, [ @@ -510,7 +513,7 @@ await test('Basic SQL', async (t) => { // SELECT * FROM products ORDER BY price; const r20 = await db - .query('products') + .query2('products') .sort('unitPrice', 'desc') .range(0, 4) .get() @@ -563,7 +566,7 @@ await test('Basic SQL', async (t) => { // SELECT * FROM products ORDER BY price; const r21 = await db - .query('products') + .query2('products') .sort('unitPrice', 'desc') .range(0, 3) .get() @@ -606,7 +609,7 @@ await test('Basic SQL', async (t) => { // SELECT * FROM customers WHERE country IN ('Germany', 'France', 'UK'); const r22 = await db - .query('customers') + .query2('customers') .filter('country', '=', ['Germany', 'France', 'UK']) .range(0, 3) .get() @@ -661,7 +664,7 @@ await test('Basic SQL', async (t) => { // SELECT * FROM products WHERE unitPrice BETWEEN 10 AND 20 ORDER BY price; const r23 = await db - .query('products') + .query2('products') .filter('unitPrice', '..', [10, 20]) .sort('unitPrice', 'desc') .get() @@ -923,9 +926,9 @@ await test('Basic SQL', async (t) => { ) // SELECT customer_id AS ID, company_name AS customer FROM customers; - const r24 = ( - await db.query('customers').include('companyName').get() - ).map((r) => ({ id: r.id, customer: r.companyName })) + const r24 = (await db.query2('customers').include('companyName').get()).map( + (r) => ({ id: r.id, customer: r.companyName }), + ) deepEqual( r24, [ @@ -1031,17 +1034,17 @@ await test('Basic SQL', async (t) => { // SELECT 'supplier', contact_name, city, country // FROM Suppliers const r25unionA = await db - .query('customers') + .query2('customers') .include('contactName', 'city', 'country') .range(0, 2) .get() - + const r25unionB = await db - .query('suppliers') + .query2('suppliers') .include('contactName', 'city', 'country') .range(0, 2) .get() - + const r25union = [ ...r25unionA.map((r) => ({ type: 'customer', ...r })), ...r25unionB.map((r) => ({ type: 'supplier', ...r })), @@ -1089,17 +1092,17 @@ await test('Basic SQL', async (t) => { // WHERE Country='Germany' // ORDER BY City; const r26unionAllA = await db - .query('customers') + .query2('customers') .include('city', 'country') .range(0, 3) .get() - + const r26unionAllB = await db - .query('suppliers') + .query2('suppliers') .include('city', 'country') .range(0, 3) .get() - + const r26unionAll = [ ...r26unionAllA.map(({ city, country }) => ({ city, country })), ...r26unionAllB.map(({ city, country }) => ({ city, country })), @@ -1139,7 +1142,7 @@ await test('insert and update', async (t) => { deepEqual( await db - .query('customers') + .query2('customers') .include('*') .filter('companyName', '=', 'Cardinal') .get(), @@ -1170,7 +1173,7 @@ await test('insert and update', async (t) => { deepEqual( await db - .query('customers') + .query2('customers') .include('*') .filter('companyName', '=', 'Cardinal') .get(), @@ -1197,17 +1200,16 @@ await test('insert and update', async (t) => { 'customers', ( await db - .query('customers') + .query2('customers') .include('id') .filter('companyName', '=', 'Cardinal') .get() - )[0].id, ) deepEqual( await db - .query('customers') + .query2('customers') .include('*') .filter('companyName', '=', 'Cardinal') .get(), @@ -1228,7 +1230,7 @@ await test('inner join', async (t) => { // INNER JOIN customers ON orders.customer_id = customers.customer_id; deepEqual( await db - .query('orders') + .query2('orders') .include('customer.companyName', 'orderDate') .range(0, 10) .get(), @@ -1300,10 +1302,10 @@ await test('left join', async (t) => { // LEFT JOIN orders // ON customers.customer_id = orders.customer_id // ORDER BY customers.company_name; - //console.log(await db.query('customers').include('companyName', (q) => q('orders').filter('customerId' '=' ??) + //console.log(await db.query2('customers').include('companyName', (q) => q('orders').filter('customerId' '=' ??) deepEqual( await db - .query('customers') + .query2('customers') .include('companyName', (q) => q('orders').include('id')) .sort('companyName') .range(0, 5) @@ -1397,13 +1399,13 @@ await test('full join', async (t) => { db.delete( 'customers', - (await db.query('customers', { customerId: 'WELLI' }).get()).id!, + (await db.query2('customers', { customerId: 'WELLI' }).get()).id!, ) // Delete orders by WANDK - const wandk = await db.query('customers', { customerId: 'WANDK' }).get() + const wandk = await db.query2('customers', { customerId: 'WANDK' }).get() const wandkOrders = await db - .query('orders') + .query2('orders') .filter('customer', '=', wandk) .get() for (const order of wandkOrders) { @@ -1415,12 +1417,9 @@ await test('full join', async (t) => { // FULL OUTER JOIN orders ON customers.customer_id=orders.customer_id // ORDER BY customers.company_name; - const customers = await db.query('customers').get() - const orders = await db - .query('orders') - .include('customer.id') - .get() - + const customers = await db.query2('customers').get() + const orders = await db.query2('orders').include('customer.id').get() + const result: any[] = [] // LEFT JOIN: Customers with Orders @@ -1485,10 +1484,9 @@ await test('self join', async (t) => { const result: any[] = [] ;( (await db - .query('customers') + .query2('customers') .include('customerId', 'companyName', 'city') - .get() - ) as { + .get()) as { id: number customerId: string city: string @@ -1522,7 +1520,7 @@ await test('aggregates', async (t) => { // min // SELECT MIN(unit_price) // FROM products; - deepEqual(await db.query('products').min('unitPrice').get(), { + deepEqual(await db.query2('products').min('unitPrice').get(), { unitPrice: { min: 2.5 }, }) @@ -1531,7 +1529,7 @@ await test('aggregates', async (t) => { // FROM products // GROUP BY category_id; deepEqual( - await db.query('products').min('unitPrice').groupBy('category').get(), + await db.query2('products').min('unitPrice').groupBy('category').get(), { '1': { unitPrice: { min: 4.5 } }, '2': { unitPrice: { min: 10 } }, @@ -1547,20 +1545,20 @@ await test('aggregates', async (t) => { // max // SELECT MAX(unit_price) // FROM products; - deepEqual(await db.query('products').max('unitPrice').get(), { + deepEqual(await db.query2('products').max('unitPrice').get(), { unitPrice: { max: 263.5 }, }) // count // SELECT COUNT(*) // FROM products; - deepEqual(await db.query('products').count().get(), { count: 77 }) + deepEqual(await db.query2('products').count().get(), { count: 77 }) // count group by // SELECT COUNT(*) AS [number of products], category_id // FROM products // GROUP BY category_id; - deepEqual(await db.query('products').count().groupBy('category').get(), { + deepEqual(await db.query2('products').count().groupBy('category').get(), { '1': { count: 12 }, '2': { count: 12 }, '3': { count: 13 }, @@ -1574,7 +1572,7 @@ await test('aggregates', async (t) => { // sum // SELECT SUM(quantity) // FROM order_details; - deepEqual(await db.query('orderDetails').sum('quantity').get(), { + deepEqual(await db.query2('orderDetails').sum('quantity').get(), { quantity: { sum: 51317 }, }) @@ -1584,7 +1582,7 @@ await test('aggregates', async (t) => { // WHERE product_id = 11; deepEqual( await db - .query('orderDetails') + .query2('orderDetails') .sum('quantity') .filter('product.id', '=', 11) .get(), @@ -1597,7 +1595,7 @@ await test('aggregates', async (t) => { // GROUP BY order_id; deepEqual( await db - .query('orderDetails') + .query2('orderDetails') .sum('quantity') .groupBy('order') .range(0, 10) @@ -1619,7 +1617,7 @@ await test('aggregates', async (t) => { // avg // SELECT AVG(unit_price) // FROM products; - deepEqual(await db.query('products').avg('unitPrice').get(), { + deepEqual(await db.query2('products').avg('unitPrice').get(), { unitPrice: { average: 28.833896103896105 }, }) @@ -1629,7 +1627,7 @@ await test('aggregates', async (t) => { // WHERE category_id = 1; deepEqual( await db - .query('products') + .query2('products') .avg('unitPrice') .filter('category.id', '=', 1) .get(), @@ -1641,7 +1639,7 @@ await test('aggregates', async (t) => { // FROM products // GROUP BY category_id; deepEqual( - await db.query('products').avg('unitPrice').groupBy('category').get(), + await db.query2('products').avg('unitPrice').groupBy('category').get(), { '1': { unitPrice: { average: 37.979166666666664 } }, '2': { unitPrice: { average: 22.854166666666668 } }, @@ -1679,7 +1677,7 @@ await test('hooks', async (t) => { await createNorthwindDb(db, schema as SchemaIn) // SELECT Avg(unit_price * discount) AS [Average discount] FROM [order_details]; - deepEqual(await db.query('orderDetails').avg('discountAmount').get(), { + deepEqual(await db.query2('orderDetails').avg('discountAmount').get(), { discountAmount: { average: 1.4448364269141538 }, }) }) diff --git a/test/scenarios/nycTaxi.ts b/test/scenarios/nycTaxi.ts index 7e8672f5fe..7a71a676eb 100644 --- a/test/scenarios/nycTaxi.ts +++ b/test/scenarios/nycTaxi.ts @@ -448,7 +448,7 @@ await test.skip('taxi', async (t) => { name: rates[i + 1], }) } - //await db.query('rate').include('*').get().inspect() + //await db.query2('rate').include('*').get().inspect() db.create('vendor', { vendorId: '1', @@ -477,25 +477,24 @@ await test.skip('taxi', async (t) => { const createTrip = async (trip: any) => { // TODO toObject() shouldn't be needed const { id: vendor = null } = await db - .query('vendor', { vendorId: trip.VendorID }) + .query2('vendor', { vendorId: trip.VendorID }) .include('id') .get() - + const { id: rate = null } = await db - .query('rate', { rateCodeId: trip.RatecodeID ?? '99' }) + .query2('rate', { rateCodeId: trip.RatecodeID ?? '99' }) .include('id') .get() - + const { id: pickupLoc = null } = await db - .query('zone', { locationId: trip.PULocationID ?? '264' }) + .query2('zone', { locationId: trip.PULocationID ?? '264' }) .include('id') .get() - + const { id: dropoffLoc = null } = await db - .query('zone', { locationId: trip.DOLocationID ?? '264' }) + .query2('zone', { locationId: trip.DOLocationID ?? '264' }) .include('id') .get() - const pickup = new Date(trip.tpep_pickup_datetime) const dropoff = new Date(trip.tpep_dropoff_datetime) @@ -549,16 +548,16 @@ await test.skip('taxi', async (t) => { process.stderr.write('\n') await db.drain() - await db.query('zone').include('borough').get().inspect() - // await db.query('zone').include('*').get().inspect() - // await db.query('vendor').include('trips').get().inspect() + await db.query2('zone').include('borough').get().inspect() + // await db.query2('zone').include('*').get().inspect() + // await db.query2('vendor').include('trips').get().inspect() // await db - // .query('trip') + // .query2('trip') // .include('pickupLoc', 'dropoffLoc', 'paymentType') // .get() // .inspect() await db - .query('trip') + .query2('trip') .include( 'pickup', 'tripDistance', @@ -567,12 +566,12 @@ await test.skip('taxi', async (t) => { ) .get() .inspect() - // await db.query('trip').count().groupBy('dropoffLoc.borough').get().inspect() // TBD: nested prop in groupBy - await db.query('trip').count().groupBy('dropoffLoc').get().inspect() - await db.query('trip').count().groupBy('paymentType').get().inspect() + // await db.query2('trip').count().groupBy('dropoffLoc.borough').get().inspect() // TBD: nested prop in groupBy + await db.query2('trip').count().groupBy('dropoffLoc').get().inspect() + await db.query2('trip').count().groupBy('paymentType').get().inspect() console.log('trip count') - await db.query('trip').count().get().inspect() - // await db.query('vendor').sum('trips').get().inspect() BUG: requires validation or // TBD: group by all + await db.query2('trip').count().get().inspect() + // await db.query2('vendor').sum('trips').get().inspect() BUG: requires validation or // TBD: group by all // const makeDays = (startYear: number, endYear: number) => { // const days: Date[] = [] @@ -587,7 +586,7 @@ await test.skip('taxi', async (t) => { // const res = await Promise.all( // days.map((day) => // db - // .query('trip') + // .query2('trip') // .filter('pickup', '>=', day) // .filter('dropoff', '<=', new Date(day).setUTCHours(23, 59, 59, 0)) // //.count() @@ -601,7 +600,7 @@ await test.skip('taxi', async (t) => { // Yearly/Monthly/Daily revenue console.log('Yearly/Monthly/Daily revenue') await db - .query('trip') + .query2('trip') //.filter('pickupYear', '>=', new Date('2022-01-01')) //.filter('pickupYear', '<=', new Date('2024-05-31')) .filter('pickupYear', '>=', 2022) @@ -614,7 +613,7 @@ await test.skip('taxi', async (t) => { // Revenue Breakdown by Vendor console.log('Revenue Breakdown by Vendor') await db - .query('vendor') + .query2('vendor') .include('name', (select) => { select('trips') .groupBy('pickup', { step: 'year', timeZone: 'America/New_York' }) @@ -627,7 +626,7 @@ await test.skip('taxi', async (t) => { // Find the top 10 trips per day with the highest tip per mile. // TBD: need callback to combine functions tipAmount/tripDistance console.log('Top tippers') await db - .query('trip') + .query2('trip') // .include('pickup', 'fees.tipAmount', 'tripDistance') .groupBy('pickup', { step: 'day', timeZone: 'America/New_York' }) .max('fees.tipAmount') @@ -640,20 +639,20 @@ await test.skip('taxi', async (t) => { // Rush hour utilization console.log('Rush hour utilization') const rh1 = await db - .query('trip') + .query2('trip') .filter('pickupHour', '>=', 7) .filter('pickupHour', '<=', 10) .or((t) => t.filter('pickupHour', '>=', 16).filter('pickupHour', '<=', 19)) .groupBy('pickup', { step: 'dow', timeZone: 'America/New_York' }) .count() .get() - + const rh2 = await db - .query('trip') + .query2('trip') .groupBy('pickup', { step: 'dow', timeZone: 'America/New_York' }) .count() .get() - + console.log( Object.keys(day2enum).reduce( (prev, key) => ( @@ -666,7 +665,7 @@ await test.skip('taxi', async (t) => { // Most popular routes await db - .query('trip') + .query2('trip') .groupBy('pickupDropoffLocs') .count() .sort('pickupDropoffLocs') // TBD: has no effect yethas no effect yet @@ -677,7 +676,7 @@ await test.skip('taxi', async (t) => { // Avg rush hour speed between zones console.log('Avg rush hour speed between zones') await db - .query('trip') + .query2('trip') .filter('pickupHour', '>=', 7) .filter('pickupHour', '<=', 10) .or((t) => t.filter('pickupHour', '>=', 16).filter('pickupHour', '<=', 19)) diff --git a/test/scenarios/vote.ts b/test/scenarios/vote.ts index 71d9769ca4..32e3a2e725 100644 --- a/test/scenarios/vote.ts +++ b/test/scenarios/vote.ts @@ -152,7 +152,7 @@ await test('schema with many uint8 fields', async (t) => { const queueJob = async () => { const confirmation = async () => { const rdyForConfirmationToken = await db - .query('round', final) + .query2('round', final) .include((select) => { const t = select('payments') t.filter('status', '=', ['Requested']) @@ -171,7 +171,7 @@ await test('schema with many uint8 fields', async (t) => { const paymentIntent = async () => { const rdyForPaymentIntent = await db - .query('round', final) + .query2('round', final) .include((select) => { const t = select('payments') t.filter('status', '=', ['RequestedIntent']) @@ -300,7 +300,7 @@ await test('schema with many uint8 fields', async (t) => { const realIds = [...ids.keys()] const myThings = await db - .query('payment', realIds) + .query2('payment', realIds) .filter('status', '=', [ 'ReadyForConfirmationToken', 'ReadyForPaymentIntent', @@ -389,7 +389,7 @@ await test('schema with many uint8 fields', async (t) => { console.log( 'handled votes #', - (await db.query('vote').range(0, 1e6).get()).length, + (await db.query2('vote').range(0, 1e6).get()).length, ) await wait(100) diff --git a/test/scenarios/voteEdges.ts b/test/scenarios/voteEdges.ts index 43bc838339..3a9e2fca2e 100644 --- a/test/scenarios/voteEdges.ts +++ b/test/scenarios/voteEdges.ts @@ -129,6 +129,6 @@ await test('votesEdges', async (t) => { await db.drain() }, `Update random order id ${amount} votes, ${a}`) - await db.query('vote').get().inspect(1) - await db.query('round', final).include('*', '**').get().inspect(1) + await db.query2('vote').get().inspect(1) + await db.query2('round', final).include('*', '**').get().inspect(1) }) diff --git a/test/scenarios/voteLargeAmounts.perf.ts b/test/scenarios/voteLargeAmounts.perf.ts index 36ed546815..945f06362a 100644 --- a/test/scenarios/voteLargeAmounts.perf.ts +++ b/test/scenarios/voteLargeAmounts.perf.ts @@ -128,20 +128,20 @@ await test('schema with many uint8 fields', async (t) => { const timeActions = async () => { //console.log('\n------ Status ------') - // await db.query('vote').count().get().inspect() - // await db.query('payment').count().get().inspect() + // await db.query2('vote').count().get().inspect() + // await db.query2('payment').count().get().inspect() const d = performance.now() await db.save() const tSave = performance.now() - d //console.log('took', tSave.toFixed(2), 'ms to save') - const cnt = await db.query('vote').count().get() + const cnt = await db.query2('vote').count().get() // TODO This crashes the test runner if it fails //assert(tSave < 1e3) // TODO better assert //assert(cnt.execTime < 5) //await db - // .query('payment') + // .query2('payment') // .include('id') // .filter('status', '=', 'Requested') // .get() @@ -151,7 +151,7 @@ await test('schema with many uint8 fields', async (t) => { // 'group by on all', // ( // await db - // .query('vote') + // .query2('vote') // .groupBy('fromCountry') // .sum(...s) // .get() @@ -160,7 +160,7 @@ await test('schema with many uint8 fields', async (t) => { //) const n = cnt.count const grp = await db - .query('vote') + .query2('vote') .groupBy('fromCountry') .sum(...s) .get() diff --git a/test/scenarios/voteStorage.ts b/test/scenarios/voteStorage.ts index 259a622038..10e33e759d 100644 --- a/test/scenarios/voteStorage.ts +++ b/test/scenarios/voteStorage.ts @@ -188,8 +188,7 @@ const testVotes = (opts: { votes: any; amount: number }) => { }) deepEqual( - (await db.query('round', final).include('votes').get()).votes - .length, + (await db.query2('round', final).include('votes').get()).votes.length, 0, 'clear refs', ) @@ -198,18 +197,14 @@ const testVotes = (opts: { votes: any; amount: number }) => { for (let i = 0; i < len; i++) { const randomId = amount === 1 ? 1 : Math.ceil(Math.random() * amount) deepEqual( - await db.query('vote', randomId).include('round').get(), + await db.query2('vote', randomId).include('round').get(), { id: randomId, round: null }, `clears refs on the other side ${randomId}`, ) } - const votes = await db - .query('vote') - .range(0, 1e6) - .include('id') - .get() - + const votes = await db.query2('vote').range(0, 1e6).include('id').get() + let i = votes.length - 1 for (i = 0; i < votes.length; i++) { db.delete('vote', votes[i].id) diff --git a/test/search.ts b/test/search.ts index 5f4ed3b7a6..a03a21d426 100644 --- a/test/search.ts +++ b/test/search.ts @@ -31,7 +31,7 @@ await test('like filter', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'article') .include('id') .range(0, 1e3) @@ -43,7 +43,7 @@ await test('like filter', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'snurfelpants') .include('id') .range(0, 1e3) @@ -55,7 +55,7 @@ await test('like filter', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', ['snurfelpants', 'article']) .include('id') .range(0, 1e3) @@ -67,7 +67,7 @@ await test('like filter', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'kxngdom') .include('id') .range(0, 1e3) @@ -80,7 +80,7 @@ await test('like filter', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'derperp') .include('id') .range(0, 1e3) @@ -93,7 +93,7 @@ await test('like filter', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'kxngdom', { score: 0 }) .include('id') .range(0, 1e3) @@ -136,7 +136,7 @@ await test('compressed', async (t) => { // sort + search equal( await db - .query('article') + .query2('article') .search('Netherlands', { body: 0, title: 1 }) .include('id', 'date') .range(0, amount) @@ -148,7 +148,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('giraffe', { body: 0, title: 1 }) .include('id', 'date', 'title') .range(0, amount) @@ -160,7 +160,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('kingdom', { body: 0, title: 1 }) .include('id', 'date', 'title') .sort('date') @@ -173,7 +173,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('Netherlands', { body: 0, title: 1 }) .include('id', 'date') .sort('date') @@ -186,7 +186,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('giraffe', { body: 0, title: 1 }) .include('id', 'date', 'title') .sort('date') @@ -199,7 +199,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('derp', { body: 0, title: 1 }) .include('id', 'date', 'title') .sort('date') @@ -212,7 +212,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('giraffe first', { body: 0, title: 1 }) .include('id', 'date', 'title', 'body') .range(0, 1e3) @@ -224,7 +224,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('first', { body: 0, title: 1 }) .include('id', 'date', 'title', 'body') .sort('date') @@ -237,7 +237,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('second', { body: 0, title: 1 }) .include('id', 'date', 'title') .sort('date') @@ -250,7 +250,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('giraffe first', { body: 0, title: 1 }) .include('id', 'date', 'title') .sort('date') @@ -263,7 +263,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('italy netherlands', { body: 0, title: 1 }) .include('id', 'date', 'title') .sort('date') @@ -276,7 +276,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('italy netherlands', 'body', 'title') .include('id', 'date', 'title') .sort('date') @@ -289,7 +289,7 @@ await test('compressed', async (t) => { equal( await db - .query('article') + .query2('article') .search('italy netherlands', 'body', 'title') .include('id', 'date', 'title') .sort('date') @@ -328,7 +328,7 @@ await test('simple', async (t) => { equal( await db - .query('article') + .query2('article') .search('giraffe first', 'body') .include('id', 'date', 'title') .sort('date') @@ -341,7 +341,7 @@ await test('simple', async (t) => { equal( await db - .query('article') + .query2('article') .search('derp derp', 'body', 'title') .include('id', 'date', 'title') .sort('date') @@ -394,7 +394,7 @@ await test('search ids', async (t) => { equal( await db - .query('article', [first, second]) + .query2('article', [first, second]) .search('first', 'body') .include('id', 'date', 'title') .range(0, 1e3) @@ -406,7 +406,7 @@ await test('search ids', async (t) => { equal( await db - .query('article', [first, second]) + .query2('article', [first, second]) .search('first', 'body') .sort('date') .include('id', 'date', 'title') @@ -446,7 +446,7 @@ await test('like filter mbs', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'mihailovitsin') .include('id') .range(0, 1e3) @@ -457,7 +457,7 @@ await test('like filter mbs', async (t) => { equal( ( await db - .query('article') + .query2('article') .filter('body', 'like', 'mihailovitšin') .include('id') .range(0, 1e3) @@ -493,7 +493,7 @@ await test('giraffe first', async (t) => { equal( await db - .query('article') + .query2('article') .search('giraffe first', { body: 0, title: 1 }) .include('id', 'date', 'title', 'body') .range(0, 1e3) @@ -525,7 +525,7 @@ await test('first letter', async (t) => { name: 'Kavel Omval Naast De Poort', }) - deepEqual(await db.query('article').search('Kavel').get(), [ + deepEqual(await db.query2('article').search('Kavel').get(), [ { id: 1, $searchScore: 0, name: 'Kavel Omval Naast De Poort' }, ]) }) diff --git a/test/serializeQueryDef.ts b/test/serializeQueryDef.ts index 556796b219..327eb5fedc 100644 --- a/test/serializeQueryDef.ts +++ b/test/serializeQueryDef.ts @@ -30,25 +30,24 @@ await test('serialize', async (t) => { await db.drain() - deepEqual((await db.query('user').get()), [ + deepEqual(await db.query2('user').get(), [ { id: 1, isNice: false }, { id: 2, isNice: true }, { id: 3, isNice: false }, ]) - deepEqual( - (await db.query('user').filter('isNice', '=', true).get()), - [{ id: 2, isNice: true }], - ) + deepEqual(await db.query2('user').filter('isNice', '=', true).get(), [ + { id: 2, isNice: true }, + ]) - deepEqual((await db.query('user').filter('isNice').get()), [ + deepEqual(await db.query2('user').filter('isNice').get(), [ { id: 2, isNice: true }, ]) - deepEqual((await db.query('user').filter('isNice', false).get()), [ + deepEqual(await db.query2('user').filter('isNice', false).get(), [ { id: 1, isNice: false }, { id: 3, isNice: false }, ]) - // const def = db.query('user').filter('isNice', false).def + // const def = db.query2('user').filter('isNice', false).def }) diff --git a/test/shared/playground.ts b/test/shared/playground.ts index 7345a2ac2b..aea4d26a82 100644 --- a/test/shared/playground.ts +++ b/test/shared/playground.ts @@ -35,6 +35,6 @@ // console.log('set', await db.drain(), 'ms') -// await db.query('thing').range(0, 1e6).filter('isNice', false).get().inspect() +// await db.query2('thing').range(0, 1e6).filter('isNice', false).get().inspect() // await db.destroy() diff --git a/test/shared/test.ts b/test/shared/test.ts index fc9a480a4d..e34e4ec1d9 100644 --- a/test/shared/test.ts +++ b/test/shared/test.ts @@ -110,7 +110,7 @@ const test: { await db.stop() - const newDb = new BasedDb({ + const newDb = new DbServer({ path: t.tmp, }) @@ -140,7 +140,7 @@ const test: { deepEqual( b[di], a[di], - `Mismatch after backup (len:${b.length}) ${Object.keys(db.server.schema!.types)[di]}`, + `Mismatch after backup (len:${b.length}) ${Object.keys(db.schema!.types)[di]}`, ) } const ci = findFirstDiffPos(counts, c) @@ -148,7 +148,7 @@ const test: { deepEqual( c[ci], counts[ci], - `Mismatching count after backup (len:${b.length}) ${Object.keys(db.server.schema!.types)[ci]}`, + `Mismatching count after backup (len:${b.length}) ${Object.keys(db.schema!.types)[ci]}`, ) } } diff --git a/test/simpleQuery.ts b/test/simpleQuery.ts index 16bac8188d..3d424027a1 100644 --- a/test/simpleQuery.ts +++ b/test/simpleQuery.ts @@ -39,22 +39,16 @@ await test('query', async (t) => { await db.drain() - deepEqual( - (await db.query('user').include('id').get()), - [{ id: 1 }], - 'Id only', - ) + deepEqual(await db.query2('user').include('id').get(), [{ id: 1 }], 'Id only') deepEqual( - ( - await db.query('user').filter('age', '<', 20).include('id', 'age').get() - ), + await db.query2('user').filter('age', '<', 20).include('id', 'age').get(), [], ) deepEqual( - (await db.query('user').include('*').get()), - (await db.query('user').get()), + await db.query2('user').include('*').get(), + await db.query2('user').get(), 'include * works as "get all fields"', ) }) diff --git a/test/singleRef.ts b/test/singleRef.ts index 3e6702b134..e8fc0fd46f 100644 --- a/test/singleRef.ts +++ b/test/singleRef.ts @@ -105,7 +105,7 @@ await test('single simple', async (t) => { }), }) - deepEqual(await db.query('simple').include('user.name').get(), [ + deepEqual(await db.query2('simple').include('user.name').get(), [ { id: 1, user: { @@ -119,7 +119,7 @@ await test('single simple', async (t) => { user: null, }) - deepEqual(await db.query('simple').include('user.name').get(), [ + deepEqual(await db.query2('simple').include('user.name').get(), [ { id: 1, user: null, @@ -208,26 +208,26 @@ await test('simple nested', async (t) => { await db.drain() - deepEqual((await db.query('blup').include('flap').get()), [ + deepEqual(await db.query2('blup').include('flap').get(), [ { id: 1, flap: 'B', }, ]) - const result1 = await db.query('user').include('myBlup.flap').get() + const result1 = await db.query2('user').include('myBlup.flap').get() for (const r of result1) { equal(r.myBlup.flap, 'B') } - const result = await db.query('simple').include('user.myBlup.flap').get() + const result = await db.query2('simple').include('user.myBlup.flap').get() for (const r of result) { equal(r.user.myBlup.flap, 'B') } - deepEqual((await db.query('user').include('simple').get()), [ + deepEqual(await db.query2('user').include('simple').get(), [ { id: 1, simple: { id: 1 }, @@ -240,14 +240,14 @@ await test('simple nested', async (t) => { await db.drain() - deepEqual((await db.query('simple').include('user').get()), [ + deepEqual(await db.query2('simple').include('user').get(), [ { id: 1, user: null, }, ]) - deepEqual((await db.query('user').include('simple').get()), [ + deepEqual(await db.query2('user').include('simple').get(), [ { id: 1, simple: null, @@ -321,7 +321,7 @@ await test('single reference object', async (t) => { await db.drain() - deepEqual((await db.query('simple').include('admin.user').get()), [ + deepEqual(await db.query2('simple').include('admin.user').get(), [ { id: 1, admin: { @@ -429,13 +429,12 @@ await test('nested', async (t) => { await db.drain() - deepEqual( - (await db.query('simple').include('id').range(0, 1).get()), - [{ id: 1 }], - ) + deepEqual(await db.query2('simple').include('id').range(0, 1).get(), [ + { id: 1 }, + ]) deepEqual( - (await db.query('simple').include('user').range(0, 1).get()), + await db.query2('simple').include('user').range(0, 1).get(), [ { id: 1, @@ -446,9 +445,7 @@ await test('nested', async (t) => { ) deepEqual( - ( - await db.query('simple', lastRes).include('user.location').get() - ), + await db.query2('simple', lastRes).include('user.location').get(), { id: await lastRes, user: { @@ -460,7 +457,7 @@ await test('nested', async (t) => { ) deepEqual( - (await db.query('simple', lastRes).include('user').get()), + await db.query2('simple', lastRes).include('user').get(), { id: await lastRes, user: { @@ -478,13 +475,11 @@ await test('nested', async (t) => { ) deepEqual( - ( - await db - .query('simple') // lastRes - .include('user.myBlup') - .range((await lastRes!) - 1, await lastRes) - .get() - ), + await db + .query2('simple') // lastRes + .include('user.myBlup') + .range((await lastRes!) - 1, await lastRes) + .get(), [ { id: await lastRes, @@ -495,7 +490,7 @@ await test('nested', async (t) => { ) deepEqual( - (await db.query('simple', lastRes).include('user.myBlup').get()), + await db.query2('simple', lastRes).include('user.myBlup').get(), { id: await lastRes, user: { id: 1, myBlup: { id: 1, flap: 'A', name: 'blup !' } }, @@ -504,9 +499,7 @@ await test('nested', async (t) => { ) deepEqual( - ( - await db.query('simple', lastRes).include('user.myBlup', 'lilBlup').get() - ), + await db.query2('simple', lastRes).include('user.myBlup', 'lilBlup').get(), { id: await lastRes, user: { id: 1, myBlup: { id: 1, flap: 'A', name: 'blup !' } }, @@ -516,20 +509,18 @@ await test('nested', async (t) => { ) equal( - (await db.query('simple', lastRes).include('user.myBlup').get()).node().user - .myBlup.flap, + (await db.query2('simple', lastRes).include('user.myBlup').get()).node() + .user.myBlup.flap, 'A', 'Read nested field with getter', ) deepEqual( - ( - await db - .query('simple') - .include('user.myBlup', 'lilBlup', 'user.name') - .range((await lastRes!) - 1, await lastRes) - .get() - ), + await db + .query2('simple') + .include('user.myBlup', 'lilBlup', 'user.name') + .range((await lastRes!) - 1, await lastRes) + .get(), [ { id: await lastRes, @@ -545,30 +536,21 @@ await test('nested', async (t) => { ) deepEqual( - ( - await db.query('simple', lastRes).include('user.location.label').get() - ), + await db.query2('simple', lastRes).include('user.location.label').get(), { id: await lastRes, user: { id: 1, location: { label: 'BLA BLA' } } }, ) - deepEqual( - ( - await db.query('simple', lastRes).include('user.location').get() - ), - { - id: await lastRes, - user: { id: 1, location: { label: 'BLA BLA', x: 1, y: 2 } }, - }, - ) + deepEqual(await db.query2('simple', lastRes).include('user.location').get(), { + id: await lastRes, + user: { id: 1, location: { label: 'BLA BLA', x: 1, y: 2 } }, + }) deepEqual( - ( - await db - .query('simple') - .include('user.myBlup', 'lilBlup') - .range((await lastRes!) - 1, await lastRes) - .get() - ), + await db + .query2('simple') + .include('user.myBlup', 'lilBlup') + .range((await lastRes!) - 1, await lastRes) + .get(), [ { id: await lastRes, @@ -590,13 +572,11 @@ await test('nested', async (t) => { ) deepEqual( - ( - await db - .query('simple') - .include('user', 'user.myBlup') - .range((await lastRes!) - 1, await lastRes) - .get() - ), + await db + .query2('simple') + .include('user', 'user.myBlup') + .range((await lastRes!) - 1, await lastRes) + .get(), [ { id: await lastRes, @@ -617,12 +597,10 @@ await test('nested', async (t) => { ) deepEqual( - ( - await db - .query('simple', lastRes) - .include('user', 'user.myBlup', 'lilBlup') - .get() - ), + await db + .query2('simple', lastRes) + .include('user', 'user.myBlup', 'lilBlup') + .get(), { id: await lastRes, user: { @@ -708,7 +686,7 @@ await test('single reference multi refs strings', async (t) => { await db.drain() const result = await db - .query('simple') + .query2('simple') .include('user', 'user.myBlup', 'lilBlup') .get() @@ -723,7 +701,7 @@ await test('single reference multi refs strings', async (t) => { await db.drain() const result2 = await db - .query('simple') + .query2('simple') .filter('age', '=', 5) .include('user', 'user.myBlup', 'lilBlup') .get() diff --git a/test/singleRefQuery.ts b/test/singleRefQuery.ts index 3a06df2db1..ad96e103cd 100644 --- a/test/singleRefQuery.ts +++ b/test/singleRefQuery.ts @@ -110,7 +110,7 @@ await test('single reference query', async (t) => { await db.drain() const result2 = await db - .query('simple') + .query2('simple') .filter('user.myBlup.age', '=', 10) .get() @@ -125,7 +125,7 @@ await test('single reference query', async (t) => { ]) const result = await db - .query('simple') + .query2('simple') .filter('lilBlup.age', '=', 20) .filter('flap.power', '=', 10) .include('lilBlup', 'flap') diff --git a/test/sort/sort.perf.ts b/test/sort/sort.perf.ts index bb6a919e88..f3fcbb236c 100644 --- a/test/sort/sort.perf.ts +++ b/test/sort/sort.perf.ts @@ -41,7 +41,7 @@ await test('1M', async (t) => { ) const r = await db - .query('user') + .query2('user') .include('age', 'name', 'email') .range(0, 1e5) .sort('email') diff --git a/test/sort/sort.ts b/test/sort/sort.ts index 639d20b417..2b8df5f827 100644 --- a/test/sort/sort.ts +++ b/test/sort/sort.ts @@ -58,7 +58,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user').sort('age', 'desc').include('email', 'age').get(), + await db.query2('user').sort('age', 'desc').include('email', 'age').get(), [ { id: 1, email: 'blap@blap.blap.blap', age: 201 }, { id: 4, email: 'nurp@nurp.nurp.nurp', age: 200 }, @@ -70,7 +70,7 @@ await test('basic', async (t) => { ) deepEqual( - await db.query('user').sort('age', 'asc').include('email', 'age').get(), + await db.query2('user').sort('age', 'asc').include('email', 'age').get(), [ { id: 5, email: 'z@z.z', age: 1 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -84,7 +84,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user').sort('email', 'asc').include('email', 'age').get(), + await db.query2('user').sort('email', 'asc').include('email', 'age').get(), [ { id: 1, email: 'blap@blap.blap.blap', age: 201 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -96,7 +96,7 @@ await test('basic', async (t) => { ) deepEqual( - await db.query('user').sort('email', 'desc').include('email', 'age').get(), + await db.query2('user').sort('email', 'desc').include('email', 'age').get(), [ { id: 1, email: 'blap@blap.blap.blap', age: 201 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -116,7 +116,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user').sort('email').include('email', 'age').get(), + await db.query2('user').sort('email').include('email', 'age').get(), [ { id: 1, email: 'blap@blap.blap.blap', age: 201 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -129,7 +129,7 @@ await test('basic', async (t) => { ) deepEqual( - await db.query('user').sort('age').include('email', 'age').get(), + await db.query2('user').sort('age').include('email', 'age').get(), [ { id: 5, email: 'z@z.z', age: 1 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -148,7 +148,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user').sort('email').include('email', 'age').get(), + await db.query2('user').sort('email').include('email', 'age').get(), [ { id: 1, email: 'blap@blap.blap.blap', age: 201 }, { id: 6, email: 'dd@dd.dd', age: 999 }, @@ -167,7 +167,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user').sort('age').include('email', 'age').get(), + await db.query2('user').sort('age').include('email', 'age').get(), [ { id: 5, email: 'z@z.z', age: 1 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -186,7 +186,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user').sort('age').include('email', 'age').get(), + await db.query2('user').sort('age').include('email', 'age').get(), [ { id: 6, email: 'dd@dd.dd', age: 0 }, { id: 5, email: 'z@z.z', age: 1 }, @@ -198,7 +198,7 @@ await test('basic', async (t) => { 'update mrX to age 0', ) - deepEqual(await db.query('user').sort('age').include('email', 'age').get(), [ + deepEqual(await db.query2('user').sort('age').include('email', 'age').get(), [ { id: 6, email: 'dd@dd.dd', age: 0 }, { id: 5, email: 'z@z.z', age: 1 }, { id: 2, email: 'flap@flap.flap.flap', age: 50 }, @@ -220,7 +220,7 @@ await test('basic', async (t) => { await db.drain() deepEqual( - await db.query('user', ids).include('name', 'age').sort('age').get(), + await db.query2('user', ids).include('name', 'age').sort('age').get(), [ { id: 6, name: 'mr x', age: 0 }, { id: 5, name: 'mr z', age: 1 }, @@ -238,7 +238,7 @@ await test('basic', async (t) => { deepEqual( await db - .query('user', ids) + .query2('user', ids) .include('name', 'age') .sort('age', 'desc') .get(), @@ -264,7 +264,7 @@ await test('basic', async (t) => { deepEqual( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age') .sort('age', 'asc') .get(), @@ -294,7 +294,7 @@ await test('basic', async (t) => { deepEqual( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age') .sort('age', 'asc') .get(), @@ -324,7 +324,8 @@ await test('basic', async (t) => { await db.drain() equal( - (await db.query('user', ids2).include('name', 'age', 'email').get()).length, + (await db.query2('user', ids2).include('name', 'age', 'email').get()) + .length, 16, 'Check default query after remove', ) @@ -332,7 +333,7 @@ await test('basic', async (t) => { equal( ( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age', 'email') .sort('email') .get() @@ -344,7 +345,7 @@ await test('basic', async (t) => { equal( ( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age', 'email') .sort('name') .get() @@ -360,7 +361,7 @@ await test('basic', async (t) => { equal( ( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age', 'email') .sort('name') .get() @@ -378,7 +379,7 @@ await test('basic', async (t) => { equal( ( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age', 'email') .sort('email') .get() @@ -394,7 +395,7 @@ await test('basic', async (t) => { equal( ( await db - .query('user', ids2) + .query2('user', ids2) .include('name', 'age', 'email') .sort('email') .get() @@ -448,7 +449,7 @@ await test('sort - from start (1M items)', async (t) => { await db.drain() deepEqual( - await db.query('user').include('name').sort('age').range(0, 2).get(), + await db.query2('user').include('name').sort('age').range(0, 2).get(), [ { id: 2, name: 'mr flap' }, { id: 1, name: 'mr blap' }, @@ -456,7 +457,7 @@ await test('sort - from start (1M items)', async (t) => { ) deepEqual( - await db.query('user').include('name').sort('age').range(0, 2).get(), + await db.query2('user').include('name').sort('age').range(0, 2).get(), [ { id: 2, name: 'mr flap' }, { id: 1, name: 'mr blap' }, @@ -464,7 +465,7 @@ await test('sort - from start (1M items)', async (t) => { ) deepEqual( - await db.query('user').include('name').sort('name').range(0, 2).get(), + await db.query2('user').include('name').sort('name').range(0, 2).get(), [ { id: 3, @@ -488,7 +489,7 @@ await test('sort - from start (1M items)', async (t) => { t.after(() => newDb.destroy()) deepEqual( - await newDb.query('user').include('name').sort('name').range(0, 2).get(), + await newDb.query2('user').include('name').sort('name').range(0, 2).get(), [ { id: 3, @@ -523,7 +524,7 @@ await test('unset value on create', async (t) => { }, }) - await db.query('dialog').sort('fun', 'desc').get() + await db.query2('dialog').sort('fun', 'desc').get() const id1 = await db.create('dialog', { fun: '1', @@ -542,7 +543,7 @@ await test('unset value on create', async (t) => { const id5 = await db.create('dialog', {}) deepEqual( - await db.query('dialog').sort('fun', 'desc').get(), + await db.query2('dialog').sort('fun', 'desc').get(), [ { id: 3, @@ -568,7 +569,7 @@ await test('unset value on create', async (t) => { 'first', ) - deepEqual(await db.query('dialog').sort('fun', 'desc').get(), [ + deepEqual(await db.query2('dialog').sort('fun', 'desc').get(), [ { id: 3, fun: '3' }, { id: 2, fun: '2' }, { id: 1, fun: '1' }, @@ -580,7 +581,7 @@ await test('unset value on create', async (t) => { fun: '0', }) - deepEqual(await db.query('dialog').sort('fun', 'desc').get(), [ + deepEqual(await db.query2('dialog').sort('fun', 'desc').get(), [ { id: 3, fun: '3', @@ -606,7 +607,7 @@ await test('unset value on create', async (t) => { db.delete('dialog', id5) await db.drain() - deepEqual(await db.query('dialog').sort('fun', 'desc').get(), [ + deepEqual(await db.query2('dialog').sort('fun', 'desc').get(), [ { id: 3, fun: '3', diff --git a/test/sort/sortAlias.ts b/test/sort/sortAlias.ts index 8d2f69bd56..84f5703a81 100644 --- a/test/sort/sortAlias.ts +++ b/test/sort/sortAlias.ts @@ -25,7 +25,7 @@ await test('alias', async (t) => { await db.drain() isSorted( - await db.query('article').sort('email', 'desc').get(), + await db.query2('article').sort('email', 'desc').get(), 'email', 'desc', 'After create', @@ -41,7 +41,7 @@ await test('alias', async (t) => { await db.drain() isSorted( - await db.query('article').sort('email', 'desc').get(), + await db.query2('article').sort('email', 'desc').get(), 'email', 'desc', 'After update', @@ -54,7 +54,7 @@ await test('alias', async (t) => { await db.drain() isSorted( - await db.query('article').sort('email', 'desc').get(), + await db.query2('article').sort('email', 'desc').get(), 'email', 'desc', 'After delete', @@ -70,7 +70,7 @@ await test('alias', async (t) => { await db.drain() isSorted( - await db.query('article').sort('email', 'desc').get(), + await db.query2('article').sort('email', 'desc').get(), 'email', 'desc', 'After create (same values)', diff --git a/test/sort/sortBinary.ts b/test/sort/sortBinary.ts index dbb487a684..9badc74a3e 100644 --- a/test/sort/sortBinary.ts +++ b/test/sort/sortBinary.ts @@ -41,14 +41,14 @@ await test('binary sort', async (t) => { await db.drain() - deepEqual(await db.query('binary').include('name', 'data').get(), [ + deepEqual(await db.query2('binary').include('name', 'data').get(), [ { id: 1, name: 'first', data: buffer1 }, { id: 2, name: 'second', data: buffer2 }, { id: 3, name: 'third', data: buffer3 }, ]) deepEqual( - await db.query('binary').sort('data').include('name', 'data').get(), + await db.query2('binary').sort('data').include('name', 'data').get(), [ { id: 3, name: 'third', data: buffer3 }, { id: 1, name: 'first', data: buffer1 }, @@ -58,7 +58,11 @@ await test('binary sort', async (t) => { ) deepEqual( - await db.query('binary').sort('data', 'desc').include('name', 'data').get(), + await db + .query2('binary') + .sort('data', 'desc') + .include('name', 'data') + .get(), [ { id: 2, name: 'second', data: buffer2 }, { id: 1, name: 'first', data: buffer1 }, diff --git a/test/sort/sortById.ts b/test/sort/sortById.ts index 14c4695453..4d7802e002 100644 --- a/test/sort/sortById.ts +++ b/test/sort/sortById.ts @@ -44,13 +44,13 @@ await test('sort by id', async (t) => { const dbTime = await db.drain() isSorted( - await db.query('user').include('name').sort('id', 'asc').get(), + await db.query2('user').include('name').sort('id', 'asc').get(), 'id', 'asc', ) isSorted( - await db.query('user').include('name').sort('id', 'desc').get(), + await db.query2('user').include('name').sort('id', 'desc').get(), 'id', 'desc', ) @@ -62,7 +62,7 @@ await test('sort by id', async (t) => { } isSorted( - await db.query('user').include('name', 'friends.name').range(0, 1).get(), + await db.query2('user').include('name', 'friends.name').range(0, 1).get(), 'id', 'asc', ) diff --git a/test/sort/sortEnum.ts b/test/sort/sortEnum.ts index f05698749b..9f234ce872 100644 --- a/test/sort/sortEnum.ts +++ b/test/sort/sortEnum.ts @@ -39,15 +39,19 @@ await test('sort Enum', async (t) => { const q: any[] = [] for (let i = 0; i < 500; i++) { - q.push(db.query('user', randoIds).get()) + q.push(db.query2('user', randoIds).get()) } q.push( - db.query('user').filter('status', '=', ['a', 'b', 'c']).range(0, 950).get(), + db + .query2('user') + .filter('status', '=', ['a', 'b', 'c']) + .range(0, 950) + .get(), ) q.push( - db.query('user').filter('status', '=', ['d']).range(0, 600).get(), + db.query2('user').filter('status', '=', ['d']).range(0, 600).get(), // .inspect(1000), ) @@ -63,6 +67,6 @@ await test('sort Enum', async (t) => { // 'creating string sort index should not take longer then 500ms', // ) - // const r = await db.query('user').range(0, 1e5).sort('status').get() + // const r = await db.query2('user').range(0, 1e5).sort('status').get() // filter }) diff --git a/test/sort/sortHll.ts b/test/sort/sortHll.ts index b19a784842..4ecc4e7a20 100644 --- a/test/sort/sortHll.ts +++ b/test/sort/sortHll.ts @@ -55,13 +55,11 @@ await test('sortCardinality', async (t) => { }) deepEqual( - ( - await db - .query('article') - .sort('brazilians', 'desc') - .include('count', 'brazilians') - .get() - ), + await db + .query2('article') + .sort('brazilians', 'desc') + .include('count', 'brazilians') + .get(), [ { id: 1, @@ -78,9 +76,7 @@ await test('sortCardinality', async (t) => { ) deepEqual( - ( - await db.query('article').sort('count', 'asc').include('derp').get() - ), + await db.query2('article').sort('count', 'asc').include('derp').get(), [ { id: 2, @@ -101,13 +97,11 @@ await test('sortCardinality', async (t) => { await db.drain() deepEqual( - ( - await db - .query('article') - .sort('count', 'asc') - .include('count', 'brazilians') - .get() - ), + await db + .query2('article') + .sort('count', 'asc') + .include('count', 'brazilians') + .get(), [ { id: 2, @@ -151,10 +145,9 @@ await test('sortCardinality', async (t) => { }) const result = await db - .query('article') + .query2('article') .filter('id', '=', testRecordId) .get() - const count = Math.abs(result[0].brazilians) const countError = count - num_brazos @@ -179,9 +172,7 @@ await test('sortCardinality', async (t) => { ) deepEqual( - ( - await db.query('article').sort('count', 'desc').include('count').get() - ), + await db.query2('article').sort('count', 'desc').include('count').get(), [ { id: 1, @@ -200,13 +191,11 @@ await test('sortCardinality', async (t) => { await db.drain() deepEqual( - ( - await db - .query('article') - .sort('brazilians', 'desc') - .include('derp', 'count') - .get() - ), + await db + .query2('article') + .sort('brazilians', 'desc') + .include('derp', 'count') + .get(), [ { id: 2, @@ -240,12 +229,7 @@ await test('sortCardinality', async (t) => { }) deepEqual( - await db - .query('article') - .sort('count', 'desc') - .include('count') - .get() - , + await db.query2('article').sort('count', 'desc').include('count').get(), [ { id: 1008, count: 3 }, { id: 2, count: 2 }, diff --git a/test/sort/sortIds.ts b/test/sort/sortIds.ts index 7c1dc61223..ee5febdd3c 100644 --- a/test/sort/sortIds.ts +++ b/test/sort/sortIds.ts @@ -42,12 +42,12 @@ await test('ids', async (t) => { await db.drain() const ids: number[] = await Promise.all(res) - isSorted(await db.query('user', ids).sort('age').get(), 'age') - isSorted(await db.query('user', ids).sort('name').get(), 'name') - isSorted(await db.query('user', ids).sort('flap').get(), 'flap') - isSorted(await db.query('user', ids).sort('blurf').get(), 'blurf') - isSorted(await db.query('user', ids).sort('bla').get(), 'bla') - isSorted(await db.query('user', ids).sort('mep').get(), 'mep') + isSorted(await db.query2('user', ids).sort('age').get(), 'age') + isSorted(await db.query2('user', ids).sort('name').get(), 'name') + isSorted(await db.query2('user', ids).sort('flap').get(), 'flap') + isSorted(await db.query2('user', ids).sort('blurf').get(), 'blurf') + isSorted(await db.query2('user', ids).sort('bla').get(), 'bla') + isSorted(await db.query2('user', ids).sort('mep').get(), 'mep') }) await test('references', async (t) => { @@ -104,7 +104,7 @@ await test('references', async (t) => { isSorted( ( await db - .query('article', id) + .query2('article', id) .include((s) => s('contributors').sort('flap')) .get() ).node().contributors, diff --git a/test/sort/sortNodeId.ts b/test/sort/sortNodeId.ts index 3a5ee8b6b1..8811ca98eb 100644 --- a/test/sort/sortNodeId.ts +++ b/test/sort/sortNodeId.ts @@ -29,12 +29,11 @@ await test.skip('basic sort by id', async (t) => { deepEqual( await db - .query('person') + .query2('person') .include('name') .sort('id', 'desc') .range(0, 5) - .get() - , + .get(), [ { id: 99, diff --git a/test/sort/sortNumber.ts b/test/sort/sortNumber.ts index da47c2dfc2..968a84cb33 100644 --- a/test/sort/sortNumber.ts +++ b/test/sort/sortNumber.ts @@ -56,27 +56,27 @@ await test('numbers', async (t) => { u32: { increment: 100 }, }) - isSorted(await db.query('example').sort('u32').include('u32').get(), 'u32') + isSorted(await db.query2('example').sort('u32').include('u32').get(), 'u32') isSorted( - await db.query('example').sort('boolean').include('boolean').get(), + await db.query2('example').sort('boolean').include('boolean').get(), 'boolean', ) - isSorted(await db.query('example').sort('u8').include('u8').get(), 'u8') - isSorted(await db.query('example').sort('i8').include('i8').get(), 'i8') - isSorted(await db.query('example').sort('i16').include('i16').get(), 'i16') - isSorted(await db.query('example').sort('i32').include('i32').get(), 'i32') + isSorted(await db.query2('example').sort('u8').include('u8').get(), 'u8') + isSorted(await db.query2('example').sort('i8').include('i8').get(), 'i8') + isSorted(await db.query2('example').sort('i16').include('i16').get(), 'i16') + isSorted(await db.query2('example').sort('i32').include('i32').get(), 'i32') isSorted( - await db.query('example').sort('number').include('number').get(), + await db.query2('example').sort('number').include('number').get(), 'number', ) isSorted( - await db.query('example').sort('timestamp').include('timestamp').get(), + await db.query2('example').sort('timestamp').include('timestamp').get(), 'timestamp', ) deepEqual( await db - .query('example') + .query2('example') .sort('enum') .include('enum') .get() @@ -84,10 +84,10 @@ await test('numbers', async (t) => { animalsResult.sort((a, b) => animals.indexOf(a) - animals.indexOf(b)), ) db.delete('example', 1) - isSorted(await db.query('example').sort('u32').include('u32').get(), 'u32') + isSorted(await db.query2('example').sort('u32').include('u32').get(), 'u32') await db - .query('example') + .query2('example') .include('enum') .get() .then((v) => v.map((v) => v.enum)) diff --git a/test/sort/sortString.ts b/test/sort/sortString.ts index 918ae35b7f..85a228f4d5 100644 --- a/test/sort/sortString.ts +++ b/test/sort/sortString.ts @@ -76,7 +76,7 @@ await test('compression / large strings', async (t) => { ) deepEqual( await db - .query('article') + .query2('article') .include('name', 'article', 'nr') .sort('article') .range(0, len) @@ -87,7 +87,7 @@ await test('compression / large strings', async (t) => { ) deepEqual( await db - .query('article') + .query2('article') .include('name', 'article', 'nr') .sort('article', 'desc') .range(0, len) @@ -148,7 +148,7 @@ await test('fixed len strings', async (t) => { await db.drain() isSorted( - await db.query('article').include('name', 'nr').sort('name', 'desc').get(), + await db.query2('article').include('name', 'nr').sort('name', 'desc').get(), 'name', 'desc', ) diff --git a/test/sort/sortTimestamp.ts b/test/sort/sortTimestamp.ts index c6fe6b8a3a..f78e446147 100644 --- a/test/sort/sortTimestamp.ts +++ b/test/sort/sortTimestamp.ts @@ -43,7 +43,7 @@ await test('sort timestamp', async (t) => { db.create('event', { name: 'Event Null' }) let ascResult = await db - .query('event') + .query2('event') .sort('startTime', 'asc') .include('startTime', 'name') .get() @@ -56,7 +56,7 @@ await test('sort timestamp', async (t) => { ) let descResult = await db - .query('event') + .query2('event') .sort('startTime', 'asc') .include('startTime', 'name') .get() @@ -72,7 +72,7 @@ await test('sort timestamp', async (t) => { await db.update('event', eventZeroId, { startTime: now - 1000 }) ascResult = await db - .query('event') + .query2('event') .sort('startTime', 'asc') .include('startTime', 'name') .get() @@ -85,7 +85,7 @@ await test('sort timestamp', async (t) => { ) descResult = await db - .query('event') + .query2('event') .sort('startTime', 'desc') .include('startTime', 'name') .get() @@ -101,7 +101,7 @@ await test('sort timestamp', async (t) => { await db.delete('event', eventDId) ascResult = await db - .query('event') + .query2('event') .sort('startTime', 'asc') .include('startTime', 'name') .get() @@ -114,7 +114,7 @@ await test('sort timestamp', async (t) => { ) descResult = await db - .query('event') + .query2('event') .sort('startTime', 'desc') .include('startTime', 'name') .get() @@ -157,7 +157,7 @@ await test('sort multicore', async (t) => { await db.drain() isSorted( - await db.query('event').sort('startTime', 'asc').get(), + await db.query2('event').sort('startTime', 'asc').get(), 'startTime', 'asc', ) @@ -167,7 +167,7 @@ await test('sort multicore', async (t) => { q.push( (async () => { isSorted( - await db.query('event').sort('startTime', 'asc').get(), + await db.query2('event').sort('startTime', 'asc').get(), 'startTime', 'asc', ) diff --git a/test/string.ts b/test/string.ts index 7583467a92..7915154dda 100644 --- a/test/string.ts +++ b/test/string.ts @@ -46,7 +46,7 @@ await test('simple', async (t) => { await db.drain() - deepEqual(await db.query('user').include('name', 'snurp').get(), [ + deepEqual(await db.query2('user').include('name', 'snurp').get(), [ { id: 1, snurp: 'derp derp', @@ -54,7 +54,7 @@ await test('simple', async (t) => { }, ]) - deepEqual(await db.query('user').include('name', 'snurp', 'age').get(), [ + deepEqual(await db.query2('user').include('name', 'snurp', 'age').get(), [ { id: 1, age: 99, @@ -65,7 +65,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include( 'name', 'snurp', @@ -91,7 +91,7 @@ await test('simple', async (t) => { ], ) - deepEqual(await db.query('user').include('location.label').get(), [ + deepEqual(await db.query2('user').include('location.label').get(), [ { id: 1, location: { @@ -100,7 +100,7 @@ await test('simple', async (t) => { }, ]) - deepEqual(await db.query('user').include('location').get(), [ + deepEqual(await db.query2('user').include('location').get(), [ { id: 1, location: { @@ -111,7 +111,7 @@ await test('simple', async (t) => { }, ]) - deepEqual(await db.query('user').include('location', 'burp').get(), [ + deepEqual(await db.query2('user').include('location', 'burp').get(), [ { id: 1, burp: 66, @@ -125,7 +125,7 @@ await test('simple', async (t) => { deepEqual( await db - .query('user') + .query2('user') .include( 'age', 'email', @@ -148,7 +148,7 @@ await test('simple', async (t) => { ], ) - deepEqual(await db.query('user').get(), [ + deepEqual(await db.query2('user').get(), [ { id: 1, name: '', @@ -267,7 +267,7 @@ await test('string + refs', async (t) => { deepEqual( await db - .query('simple') + .query2('simple') .include('user.name', 'user.myBlup.name') .range(0, 1) .get(), @@ -293,7 +293,7 @@ await test('string + refs', async (t) => { await db.drain() deepEqual( - await db.query('simple').include('user.name', 'user.myBlup.name').get(), + await db.query2('simple').include('user.name', 'user.myBlup.name').get(), [ { id: 1, @@ -338,7 +338,7 @@ await test('Big string disable compression', async (t) => { await db.drain() equal( - (await db.query('file', file).get()).node().contents, + (await db.query2('file', file).get()).node().contents, euobserver, 'Get single id', ) @@ -349,10 +349,10 @@ await test('Big string disable compression', async (t) => { await db.drain() - equal((await db.query('file').get()).size > 1000 * 1e3, true) + equal((await db.query2('file').get()).size > 1000 * 1e3, true) deepEqual( - await db.query('file').get(), + await db.query2('file').get(), [ { id: 1, @@ -379,7 +379,7 @@ await test('Big string disable compression', async (t) => { var mb = 0 let p: any = [] for (let i = 0; i < 9; i++) { - p.push(db.query('file').get()) + p.push(db.query2('file').get()) mb += 74 } await Promise.all(p) @@ -388,7 +388,7 @@ await test('Big string disable compression', async (t) => { mb = 0 p = [] for (let i = 0; i < 9; i++) { - p.push(db.query('file').get()) + p.push(db.query2('file').get()) mb += 74 } await Promise.all(p) @@ -420,7 +420,7 @@ await test('Big string', async (t) => { await db.drain() equal( - (await db.query('file', file).get()).node().contents, + (await db.query2('file', file).get()).node().contents, euobserver, 'Get single id', ) @@ -433,7 +433,7 @@ await test('Big string', async (t) => { await db.drain() deepEqual( - await db.query('file').get(), + await db.query2('file').get(), [ { id: 1, @@ -478,13 +478,13 @@ await test('schema compression prop', async (t) => { await db.drain() const uncompressedSize = await db - .query('file') + .query2('file') .include('contentsUncompressed') .get() .then((v) => v.size) const compressedSize = await db - .query('file') + .query2('file') .include('contentsCompressed') .get() .then((v) => v.size) @@ -527,7 +527,7 @@ await test('string compression - max buf size', async (t) => { await db.drain() - const items = await db.query('file').get() + const items = await db.query2('file').get() for (const item of items) { equal(item.contents, contents, 'contents are the same') diff --git a/test/subscription/subscription.perf.ts b/test/subscription/subscription.perf.ts index 361b5913b8..2ae05d65fd 100644 --- a/test/subscription/subscription.perf.ts +++ b/test/subscription/subscription.perf.ts @@ -33,7 +33,7 @@ await test('subscription perf', async (t) => { const dx = await db.drain() - const q = db.query('user', 1) + const q = db.query2('user', 1) const y = await q.get() console.log(q.buffer, y) @@ -87,7 +87,7 @@ await test('native single id perf', async (t) => { }, }) - const q = db.query('user', 1).include('flap') + const q = db.query2('user', 1).include('flap') registerQuery(q) registerSubscription(q) diff --git a/test/subscription/subscription.ts b/test/subscription/subscription.ts index 3277d11205..6f6de76073 100644 --- a/test/subscription/subscription.ts +++ b/test/subscription/subscription.ts @@ -42,7 +42,7 @@ await test('subscription', async (t) => { let cnt = 0 const close = clients[1] - .query('user') + .query2('user') .include('derp') .subscribe((q) => { cnt++ @@ -77,7 +77,7 @@ await test('subscription', async (t) => { }) const close2 = clients[1] - .query('user', l) + .query2('user', l) .include('lang') .subscribe((q) => { cnt++ @@ -110,7 +110,7 @@ await test('subscription', async (t) => { }, 200) const close3 = clients[1] - .query('user', l) + .query2('user', l) .include('location') .subscribe((q) => { equal(lastSet, q.node(0).location, 'equals to last set') @@ -145,7 +145,7 @@ await test('subscription error', async (t) => { }) const close = clients[1] - .query('user') + .query2('user') .include('derp') .subscribe( (q) => { @@ -190,7 +190,7 @@ await test('subscribe to refs', async (t) => { let updatesReceived = 0 let size = 0 const close = clients[1] - .query('queue', queueId) + .query2('queue', queueId) .include('items') .subscribe((q) => { updatesReceived++ diff --git a/test/subscription/subscriptionId.ts b/test/subscription/subscriptionId.ts index 4def65411f..aeee38f676 100644 --- a/test/subscription/subscriptionId.ts +++ b/test/subscription/subscriptionId.ts @@ -42,12 +42,12 @@ await test('subscriptionId', async (t) => { var idCounter = 0 var idFieldCounter = 0 - const close = clients[0].query('user', id).subscribe((d) => { + const close = clients[0].query2('user', id).subscribe((d) => { idCounter++ }) const close2 = clients[0] - .query('user', id) + .query2('user', id) .include('name') .subscribe((d) => { idFieldCounter++ @@ -104,11 +104,11 @@ await test('update after remove before subs loop', async (t) => { var cnt1 = 0 var cnt2 = 0 - const close = clients[0].query('user', id).subscribe((d) => { + const close = clients[0].query2('user', id).subscribe((d) => { cnt1++ }) const close2 = clients[0] - .query('user', id) + .query2('user', id) .include('name') .subscribe((d) => { cnt2++ diff --git a/test/subscription/subscriptionIdPartial.ts b/test/subscription/subscriptionIdPartial.ts index e2685b835b..9e028babbd 100644 --- a/test/subscription/subscriptionIdPartial.ts +++ b/test/subscription/subscriptionIdPartial.ts @@ -45,7 +45,7 @@ await test('filter', async (t) => { var idFieldCounter = 0 const close = clients[0] - .query('user', id) + .query2('user', id) .filter('x', '>', 5) .include('name') .subscribe((d) => { @@ -94,12 +94,12 @@ await test('partial update', async (t) => { var idCounter = 0 var idFieldCounter = 0 - const close = clients[0].query('user', id).subscribe((d) => { + const close = clients[0].query2('user', id).subscribe((d) => { idCounter++ }) const close2 = clients[0] - .query('user', id) + .query2('user', id) .include('x', 'gurk', 'rurp', 'flap') .subscribe((d) => { idFieldCounter++ diff --git a/test/subscription/subscriptionIdRemove.ts b/test/subscription/subscriptionIdRemove.ts index bac9f9f312..27d6b23735 100644 --- a/test/subscription/subscriptionIdRemove.ts +++ b/test/subscription/subscriptionIdRemove.ts @@ -68,7 +68,7 @@ await test('subscriptionIdRemove', async (t) => { }) // Subscribe to the user - const subscription = clients[0].query('user', id).subscribe(() => { + const subscription = clients[0].query2('user', id).subscribe(() => { const user = users.get(userId) if (user && user.active) { user.updateCount++ diff --git a/test/subscription/subscriptionMulti.perf.ts b/test/subscription/subscriptionMulti.perf.ts index 80e23b3457..8319c00cd8 100644 --- a/test/subscription/subscriptionMulti.perf.ts +++ b/test/subscription/subscriptionMulti.perf.ts @@ -43,7 +43,7 @@ await test('subscriptionMulti', async (t) => { await clients[1].drain() const close2 = clients[1] - .query('user') + .query2('user') .filter('derp', '>', 1e6 - 10) .subscribe((q) => { console.log(q) diff --git a/test/subscription/subscriptionNow.ts b/test/subscription/subscriptionNow.ts index 5571156e94..5cfc5ca058 100644 --- a/test/subscription/subscriptionNow.ts +++ b/test/subscription/subscriptionNow.ts @@ -66,7 +66,7 @@ await test('simple', async (t) => { var totalLen = 0 const close = clients[0] - .query('user', id) + .query2('user', id) .filter('date', '<', 'now - 2s') .subscribe((d) => { totalLen += d.length @@ -74,7 +74,7 @@ await test('simple', async (t) => { }) const close2 = clients[0] - .query('user') + .query2('user') .filter('date', '<', 'now - 2s') .subscribe((d) => { totalLen += d.length @@ -113,7 +113,7 @@ await test('multiFilter', async (t) => { var total = 0 const close = clients[0] - .query('sequence') + .query2('sequence') .locale('en') .filter('edition', '=', edition) .filter('startTime', '!=', 0) diff --git a/test/subscription/subscriptionSchemaChanges.ts b/test/subscription/subscriptionSchemaChanges.ts index 3699432230..96c88d46fc 100644 --- a/test/subscription/subscriptionSchemaChanges.ts +++ b/test/subscription/subscriptionSchemaChanges.ts @@ -48,7 +48,7 @@ await test('subscription schema changes', async (t) => { }) let cnt = 0 const q = clients[1] - .query('user') + .query2('user') .include('derp', 'lang') .include((s) => { s('friends').include('*') @@ -133,7 +133,7 @@ await test('better subscription schema changes', async (t) => { }) const results: any[] = [] - db.query('user').subscribe((res) => { + db.query2('user').subscribe((res) => { const obj = res results.push(obj) }) diff --git a/test/subscription/subscriptionWorkers.perf.ts b/test/subscription/subscriptionWorkers.perf.ts index 654a963837..18c5be1464 100644 --- a/test/subscription/subscriptionWorkers.perf.ts +++ b/test/subscription/subscriptionWorkers.perf.ts @@ -67,7 +67,7 @@ await test('subscriptionWorkers', async (t) => { let updates = 0 if (i % 2) { close = client - .query('vote') + .query2('vote') .filter('fromCountry', '=', ['AE', 'NL']) .subscribe((v) => { updates++ diff --git a/test/text/textFallback.ts b/test/text/textFallback.ts index d985575bb4..3c53a8b4ae 100644 --- a/test/text/textFallback.ts +++ b/test/text/textFallback.ts @@ -72,11 +72,11 @@ await test('textFallback', async (t) => { // local second argument // false (block all fallbacks) or lang fallback - // await db.query('project').locale('nl').get().inspect(10) + // await db.query2('project').locale('nl').get().inspect(10) deepEqual( await db - .query('project') + .query2('project') .locale('nl') .include('title') .filter('title', 'includes', 'English') @@ -92,7 +92,7 @@ await test('textFallback', async (t) => { deepEqual( await db - .query('project') + .query2('project') .locale('nl') .include('title') .search('English', 'title') diff --git a/test/text/textFilter.ts b/test/text/textFilter.ts index bea98b96fb..196d36627a 100644 --- a/test/text/textFilter.ts +++ b/test/text/textFilter.ts @@ -94,7 +94,7 @@ await test('textFilter', async (t) => { let searchTerms = ['a', 'ab', 'abc', 'abcd'] for (const term of searchTerms) { - await db.query('project').search(term, 'title', 'abstract').get() + await db.query2('project').search(term, 'title', 'abstract').get() // .inspect() } @@ -108,7 +108,7 @@ await test('textFilter', async (t) => { for (const term of searchTerms) { q.push( (async () => { - await db.query('project').search(term, 'title', 'abstract').get() + await db.query2('project').search(term, 'title', 'abstract').get() // .inspect() })(), ) @@ -201,5 +201,8 @@ await test('compressionFilter', async (t) => { msg: derp, }) - deepEqual(await db.query('event').filter('msg', 'includes', 'derp').get(), []) + deepEqual( + await db.query2('event').filter('msg', 'includes', 'derp').get(), + [], + ) }) diff --git a/test/text/textMany.ts b/test/text/textMany.ts index fbbb1b08c2..2ec143817a 100644 --- a/test/text/textMany.ts +++ b/test/text/textMany.ts @@ -67,5 +67,5 @@ await test('Many text props', async (t) => { //console.log(JSON.stringify(db.server.schemaTypesParsed, null, 2)) await db.create('dialog', setTextProps()) - //console.log(await db.query('dialog').include('*').get()) + //console.log(await db.query2('dialog').include('*').get()) }) diff --git a/test/validation/validation.ts b/test/validation/validation.ts index 39a2652c9f..a376e376e7 100644 --- a/test/validation/validation.ts +++ b/test/validation/validation.ts @@ -46,7 +46,7 @@ await test('update', async (t) => { }) await throws(async () => { - await db.query('derp', { flap: 'snru' }).get() + await db.query2('derp', { flap: 'snru' }).get() }, true) db.create('user', { @@ -153,7 +153,7 @@ await test('update', async (t) => { }) }) - deepEqual(await db.query('user', cId).include('cardinality').get(), { + deepEqual(await db.query2('user', cId).include('cardinality').get(), { id: await cId, cardinality: 2, }) @@ -263,7 +263,7 @@ await test('update', async (t) => { await db.drain() deepEqual( - await db.query('user').include('name', 'friend').get(), + await db.query2('user').include('name', 'friend').get(), [ { id: 1, friend: null, name: '' }, { id: 2, friend: null, name: '' }, @@ -527,53 +527,57 @@ await test('query', async (t) => { await throws( // @ts-ignore - () => db.query('user', '1').get(), + () => db.query2('user', '1').get(), false, 'throw on string as id', ) - await throws(() => db.query('derp').get(), false, 'non existing type') + await throws(() => db.query2('derp').get(), false, 'non existing type') // @ts-ignore - await throws(() => db.query('user', 'derp derp').get(), false, 'incorrect id') + await throws( + () => db.query2('user', 'derp derp').get(), + false, + 'incorrect id', + ) await throws( - () => db.query('user', [1, 1221.11, 0]).get(), + () => db.query2('user', [1, 1221.11, 0]).get(), false, 'incorrect ids', ) await throws( // @ts-ignore - () => db.query('user', [1, 'X', {}]).get(), + () => db.query2('user', [1, 'X', {}]).get(), false, 'incorrect ids 2', ) const x = new Uint32Array(new Array(2e6).map((v) => 1)) - await throws(() => db.query('user', x).get(), false, 'incorrect ids 2') + await throws(() => db.query2('user', x).get(), false, 'incorrect ids 2') await throws( - () => db.query('user').include('derp').get(), + () => db.query2('user').include('derp').get(), false, 'non existing field in include', ) await throws( // @ts-ignore - () => db.query('user', { $id: 1 }).get(), + () => db.query2('user', { $id: 1 }).get(), false, 'incorrect alias', ) await throws( - () => db.query('user').filter('derp', '=', true).get(), + () => db.query2('user').filter('derp', '=', true).get(), false, 'non existing field in filter', ) await db - .query('user') + .query2('user') .filter('friend.description.en', '=', 'nice') .get() .catch((err) => { @@ -581,77 +585,79 @@ await test('query', async (t) => { }) await throws( - () => db.query('user').filter('friend.description.flap', '=', 'nice').get(), + () => + db.query2('user').filter('friend.description.flap', '=', 'nice').get(), false, 'non existing lang in filter', ) await throws( - () => db.query('user').filter('friend.description.flap', '=', 'nice').get(), + () => + db.query2('user').filter('friend.description.flap', '=', 'nice').get(), false, 'non existing lang in filter', ) await throws( - () => db.query('user').filter('friend.description.fr', '=', 'nice').get(), + () => db.query2('user').filter('friend.description.fr', '=', 'nice').get(), false, 'non existing lang in filter', ) await throws( - () => db.query('user').include('friend.description.flap').get(), + () => db.query2('user').include('friend.description.flap').get(), false, 'non existing lang in include #1', ) await throws( - () => db.query('user').include('friend.description.fr').get(), + () => db.query2('user').include('friend.description.fr').get(), false, 'non existing lang in include #2', ) await throws( // @ts-ignore - () => db.query('user').filter('friend.description.fr', 'derp', 1).get(), + () => db.query2('user').filter('friend.description.fr', 'derp', 1).get(), false, 'Filter non existing operator', ) await throws( // @ts-ignore - () => db.query('user').filter('friend.description.en', '>', 1).get(), + () => db.query2('user').filter('friend.description.en', '>', 1).get(), false, 'Filter incorrect operator on text', ) await throws( // @ts-ignore - () => db.query('user').filter('rating', 'includes', 1).get(), + () => db.query2('user').filter('rating', 'includes', 1).get(), false, 'Filter incorrect operator on uint32', ) await throws( // @ts-ignore - () => db.query('user').filter('isOn', 'includes', 1).get(), + () => db.query2('user').filter('isOn', 'includes', 1).get(), false, 'Filter incorrect operator on bool', ) - await db.query('user').filter('isOn', true).get() - await db.query('user').filter('isOn').get() - await db.query('user').filter('isOn', false).get() + await db.query2('user').filter('isOn', true).get() + await db.query2('user').filter('isOn').get() + await db.query2('user').filter('isOn', false).get() await throws( // @ts-ignore - () => db.query('user').filter('friend', 'includes', 1).get(), + () => db.query2('user').filter('friend', 'includes', 1).get(), false, 'Filter incorrect operator on reference', ) await throws( // @ts-ignore - () => db.query('user').filter('connections', 'like', 1).get(), + () => db.query2('user').filter('connections', 'like', 1).get(), false, 'Filter incorrect operator on references', ) @@ -671,7 +677,7 @@ await test('query', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('name', 'includes', '') .include('name') .get(), @@ -681,7 +687,7 @@ await test('query', async (t) => { deepEqual( await db - .query('user', []) + .query2('user', []) .filter('name', 'includes', '') .include('name') .get(), @@ -691,7 +697,7 @@ await test('query', async (t) => { deepEqual( await db - .query('user') + .query2('user') .filter('friend.description.en', '=', undefined) .include('name') .get(), @@ -701,7 +707,7 @@ await test('query', async (t) => { await throws( // @ts-ignore - () => db.query('user').filter('friend.description', 'like', 999).get(), + () => db.query2('user').filter('friend.description', 'like', 999).get(), false, 'Filter incorrect value on text', ) @@ -711,13 +717,13 @@ await test('query', async (t) => { () => db // @ts-ignore - .query({ id: 1, rating: 'derp' }) + .query2({ id: 1, rating: 'derp' }) .get(), false, 'Incorrect payload', ) - const q = db.query('flap') + const q = db.query2('flap') for (let i = 0; i < 2; i++) { await throws( async () => { @@ -733,17 +739,17 @@ await test('query', async (t) => { () => db // @ts-ignore - .query({ id: 1, rating: 'derp' }) + .query2({ id: 1, rating: 'derp' }) .get(), false, 'Incorrect payload', ) - await db.query('user').sort('drip', 'desc').get() + await db.query2('user').sort('drip', 'desc').get() await throws( async () => { - await db.query('user').sort('flurp').get() + await db.query2('user').sort('flurp').get() }, false, 'Non existing field on sort', @@ -751,58 +757,58 @@ await test('query', async (t) => { await throws(async () => { // @ts-ignore - await db.query('user').sort('drip', 'gurk').get() + await db.query2('user').sort('drip', 'gurk').get() }, false) await throws(async () => { - await db.query('user').sort('connections').get() + await db.query2('user').sort('connections').get() }, false) await throws(async () => { - await db.query('user').sort('friend').get() + await db.query2('user').sort('friend').get() }, false) await throws(async () => { - await db.query('user', 1).sort('drip').get() + await db.query2('user', 1).sort('drip').get() }, false) - await db.query('user', []).sort('drip').get() + await db.query2('user', []).sort('drip').get() - await db.query('user', [1, 2, 3]).sort('drip').get() + await db.query2('user', [1, 2, 3]).sort('drip').get() await throws(async () => { - await db.query('user').sort('drip').range(0, -10).get() + await db.query2('user').sort('drip').range(0, -10).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').sort('drip').range('derp', -100).get() + await db.query2('user').sort('drip').range('derp', -100).get() }, false) await throws(async () => { - await db.query('user').locale('az').get() + await db.query2('user').locale('az').get() }, false) await throws(async () => { - await db.query('user').search('xyz', 'derpderp').get() + await db.query2('user').search('xyz', 'derpderp').get() }, false) await throws(async () => { - await db.query('user').search('xyz', 'derpderp').get() + await db.query2('user').search('xyz', 'derpderp').get() }, false) await throws(async () => { - await db.query('user').search('xyz', 'blap').get() + await db.query2('user').search('xyz', 'blap').get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').search([1, 2, 3, 4], 'blap').get() + await db.query2('user').search([1, 2, 3, 4], 'blap').get() }, false) await throws(async () => { const envs = await db - .query('user') + .query2('user') .filter('connections', 'includes', 0) .get() }, false) @@ -830,11 +836,11 @@ await test('query - no schema', async (t) => { }, 100) await throws(async () => { - await db.query('ploink').get() + await db.query2('ploink').get() }, false) await db.schemaIsSet() - deepEqual(await db.query('user').get(), []) + deepEqual(await db.query2('user').get(), []) }) await test('minmax', async (t) => { @@ -865,7 +871,7 @@ await test('minmax', async (t) => { number: 0.5, }) - deepEqual(await db.query('user', id).get(), { + deepEqual(await db.query2('user', id).get(), { name: 'luigi', number: 0.5, id, @@ -937,110 +943,110 @@ await test('range validation', async (t) => { }) } - await db.query('user').range(0, 5).get() - await db.query('user').range(1, 10).get() - await db.query('user').range(0, 1).get() - await db.query('user').range(100, 101).get() - await db.query('user').range(1000, 1001).get() - await db.query('user').range(0, undefined).get() + await db.query2('user').range(0, 5).get() + await db.query2('user').range(1, 10).get() + await db.query2('user').range(0, 1).get() + await db.query2('user').range(100, 101).get() + await db.query2('user').range(1000, 1001).get() + await db.query2('user').range(0, undefined).get() await throws(async () => { - await db.query('user').range(0, 0).get() + await db.query2('user').range(0, 0).get() }, false) await throws(async () => { - await db.query('user').range(5, 5).get() + await db.query2('user').range(5, 5).get() }, false) await throws(async () => { - await db.query('user').range(4294967295, 4294967295).get() + await db.query2('user').range(4294967295, 4294967295).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range('invalid', 5).get() + await db.query2('user').range('invalid', 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(-1, 5).get() + await db.query2('user').range(-1, 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(1.5, 5).get() + await db.query2('user').range(1.5, 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(null, 5).get() + await db.query2('user').range(null, 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(undefined, 5).get() + await db.query2('user').range(undefined, 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range({}, 5).get() + await db.query2('user').range({}, 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range([], 5).get() + await db.query2('user').range([], 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(4294967296, 5).get() + await db.query2('user').range(4294967296, 5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, 'invalid').get() + await db.query2('user').range(0, 'invalid').get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, -1).get() + await db.query2('user').range(0, -1).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, 1.5).get() + await db.query2('user').range(0, 1.5).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, null).get() + await db.query2('user').range(0, null).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, {}).get() + await db.query2('user').range(0, {}).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, []).get() + await db.query2('user').range(0, []).get() }, false) await throws(async () => { // @ts-ignore - await db.query('user').range(0, 4294967296).get() + await db.query2('user').range(0, 4294967296).get() }, false) await throws(async () => { - await db.query('user').range(5, 3).get() + await db.query2('user').range(5, 3).get() }, false) - await db.query('user').filter('rating', '>', 0).range(0, 5).get() - await db.query('user').sort('rating').range(0, 5).get() - await db.query('user').include('name').range(0, 5).get() + await db.query2('user').filter('rating', '>', 0).range(0, 5).get() + await db.query2('user').sort('rating').range(0, 5).get() + await db.query2('user').include('name').range(0, 5).get() const result = await db - .query('user') + .query2('user') .range(0, 5) .include('name', 'rating') .get() @@ -1086,28 +1092,28 @@ await test('binary validation', async (t) => { }) await db - .query('user') + .query2('user') .filter('binaryData', '=', Buffer.from([1, 2, 3, 4])) .get() - await db.query('user').filter('binaryData', '=', 'binary string').get() + await db.query2('user').filter('binaryData', '=', 'binary string').get() await db - .query('user') + .query2('user') .filter('binaryData', '=', new Uint8Array([5, 6, 7, 8])) .get() await throws(async () => { - await db.query('user').filter('binaryData', '=', 123).get() + await db.query2('user').filter('binaryData', '=', 123).get() }) await throws(async () => { - await db.query('user').filter('binaryData', '=', {}).get() + await db.query2('user').filter('binaryData', '=', {}).get() }) await throws(async () => { - await db.query('user').filter('binaryData', '=', { some: 'object' }).get() + await db.query2('user').filter('binaryData', '=', { some: 'object' }).get() }) await throws(async () => { - await db.query('user').filter('binaryData', '=', [1, 2, 3]).get() + await db.query2('user').filter('binaryData', '=', [1, 2, 3]).get() }) }) diff --git a/test/validation/validationReferences.ts b/test/validation/validationReferences.ts index 710b2dab5b..5874b030b3 100644 --- a/test/validation/validationReferences.ts +++ b/test/validation/validationReferences.ts @@ -44,7 +44,7 @@ await test('update', async (t) => { }) await throws(async () => { - return db.query('flap').include('x.$derp').get() + return db.query2('flap').include('x.$derp').get() }, 'Non existing reference on flap') const user1 = await db.create('user', { name: 'user1' }) @@ -71,7 +71,7 @@ await test('update', async (t) => { deepEqual( await db - .query('user', userWithConn) + .query2('user', userWithConn) .include('name', 'connections.id') .get(), { @@ -170,7 +170,7 @@ await test('update', async (t) => { }) deepEqual( - await db.query('user', userWithFriends).include('name', 'friends').get(), + await db.query2('user', userWithFriends).include('name', 'friends').get(), { id: 5, name: 'friendlyUser', @@ -190,7 +190,7 @@ await test('update', async (t) => { deepEqual( await db - .query('user', userWithFriends) + .query2('user', userWithFriends) .include( 'name', 'friends.$bestFriend', @@ -233,7 +233,7 @@ await test('update', async (t) => { deepEqual( await db - .query('user', userWithFriends) + .query2('user', userWithFriends) .include( 'name', 'friends.$bestFriend', diff --git a/test/youzi.ts b/test/youzi.ts index c34b830c48..4fafc72491 100644 --- a/test/youzi.ts +++ b/test/youzi.ts @@ -8,7 +8,7 @@ // const useQuery: any = {} // await test('browser things', async t => { -// const { data } = useQuery('cms', () => b.query('user').include('name'), []) +// const { data } = useQuery('cms', () => b.query2('user').include('name'), []) // }) // // a.tsx From 119bfaff80fce52ba500285c9ab4281331b81e78 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Tue, 24 Feb 2026 15:45:13 +0100 Subject: [PATCH 418/449] Northwind fix --- test/scenarios/northwind.ts | 58 +++++-------------------------------- test/shared/index.ts | 2 +- test/shared/northwindDb.ts | 15 ++++++---- 3 files changed, 19 insertions(+), 56 deletions(-) diff --git a/test/scenarios/northwind.ts b/test/scenarios/northwind.ts index 4429d24843..5c56aed952 100644 --- a/test/scenarios/northwind.ts +++ b/test/scenarios/northwind.ts @@ -1,4 +1,3 @@ -import { BasedDb } from '../../src/index.js' // import { mermaid } from '@based/schema-diagram' import { deepCopy } from '../../src/utils/index.js' import test from '../shared/test.js' @@ -7,12 +6,7 @@ import { deepEqual } from '../shared/assert.js' import type { SchemaIn } from '../../src/schema/index.js' await test('Basic SQL', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db = await createNorthwindDb(t) // 1. Retrieve all columns in the Region table. const r1 = await db.query('region').include('*').get() @@ -1119,12 +1113,7 @@ await test('Basic SQL', async (t) => { }) await test('insert and update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db =await createNorthwindDb(t) // INSERT INTO customers (company_name, contact_name, address, city, postal_code, country) // VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway'); @@ -1216,12 +1205,7 @@ await test('insert and update', async (t) => { }) await test('inner join', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db = await createNorthwindDb(t) // SELECT orders.order_id, customers.company_name, orders.order_date // FROM orders @@ -1288,12 +1272,7 @@ await test('inner join', async (t) => { }) await test('left join', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db = await createNorthwindDb(t) // SELECT customers.company_name, orders.order_id // FROM customers @@ -1388,12 +1367,7 @@ await test.skip('right join', async (t) => { }) await test('full join', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db = await createNorthwindDb(t) db.delete( 'customers', @@ -1470,12 +1444,7 @@ await test('full join', async (t) => { }) await test('self join', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db = await createNorthwindDb(t) // SELECT A.company_name AS CustomerName1, B.company_name AS CustomerName2, A.City // FROM customers A, customers B @@ -1512,12 +1481,7 @@ await test('self join', async (t) => { }) await test('aggregates', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await createNorthwindDb(db) + const db = await createNorthwindDb(t) // min // SELECT MIN(unit_price) @@ -1656,12 +1620,6 @@ await test('aggregates', async (t) => { }) await test('hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const schema = deepCopy(defaultSchema) schema.types.orderDetails.props['discountAmount'] = 'number' schema.types.orderDetails['hooks'] = { @@ -1676,7 +1634,7 @@ await test('hooks', async (t) => { } }, } - await createNorthwindDb(db, schema as SchemaIn) + const db = await createNorthwindDb(t, schema as SchemaIn) // SELECT Avg(unit_price * discount) AS [Average discount] FROM [order_details]; deepEqual(await db.query('orderDetails').avg('discountAmount').get(), { diff --git a/test/shared/index.ts b/test/shared/index.ts index 58ccd6b287..f0177b04f9 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -1,7 +1,7 @@ import { createHash } from 'node:crypto' import { getBlockHash, getBlockStatuses } from '../../src/db-server/blocks.js' import type { ResolveSchema, SchemaIn, StrictSchema } from '../../src/schema.js' -import { BasedDb, DbClient, DbServer, getDefaultHooks } from '../../src/sdk.js' +import { DbClient, DbServer, getDefaultHooks } from '../../src/sdk.js' import test from './test.js' export * from './assert.js' export * from './examples.js' diff --git a/test/shared/northwindDb.ts b/test/shared/northwindDb.ts index a9a30729b6..9922d6e220 100644 --- a/test/shared/northwindDb.ts +++ b/test/shared/northwindDb.ts @@ -1,5 +1,8 @@ -import { Schema, type SchemaIn } from '../../src/schema/index.js' -import { BasedDb } from '../../src/index.js' +import {ResolveSchema} from '../../dist/schema.js' +import { type SchemaIn } from '../../src/schema/index.js' +import { DbClient } from '../../src/sdk.js' +import { testDb } from './index.js' +import test from './test.js' const schCompanyName = { type: 'string', maxBytes: 40 } as const const schContactName = { type: 'string', maxBytes: 30 } as const @@ -302,10 +305,10 @@ export const defaultSchema = Object.freeze({ }) export default async function createNorthwindDb( - db: BasedDb, + t: Parameters[1]>[0], schema: SchemaIn = defaultSchema as SchemaIn, -) { - await db.setSchema(schema) +): Promise>> { + const db = await testDb(t, schema) // categories ;[ @@ -3924,4 +3927,6 @@ export default async function createNorthwindDb( { unsafe: true }, ), ) + + return db } From 0f28f3d23df0a6d5d91a5aac62265db767924bcf Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 15:45:30 +0100 Subject: [PATCH 419/449] update types --- src/db-client/index.ts | 2 ++ src/db-client/query2/index.ts | 6 +++--- test/update.ts | 2 +- test/upsert.ts | 33 --------------------------------- test/vector.ts | 25 +++++++++++++------------ test/youzi.ts | 29 ----------------------------- 6 files changed, 19 insertions(+), 78 deletions(-) delete mode 100644 test/youzi.ts diff --git a/src/db-client/index.ts b/src/db-client/index.ts index aa1582e795..708273bdf7 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -100,6 +100,7 @@ export class DbClientClass< query2( type: T, + id?: number[], ): BasedQuery2 query2( @@ -112,6 +113,7 @@ export class DbClientClass< type: T, id?: | number + | number[] | (Partial> & { [Symbol.toStringTag]?: never }), ): BasedQuery2 { return new BasedQuery2(this, type, id) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index fc7f70abbf..5b953475c7 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -671,10 +671,10 @@ export function query< T extends keyof S['types'] & string = keyof S['types'] & string, >( type: T, - id?: number | Partial>, + target?: number | number[] | Partial>, ): Query { const ast: any = { type } - if (id) ast.target = id + if (target) ast.target = target return new Query(ast) } @@ -694,7 +694,7 @@ export class BasedQuery2< constructor( db: DbClient, type: T, - target?: number | Partial>, + target?: number | number[] | Partial>, ) { super({}) this.ast.type = type as string diff --git a/test/update.ts b/test/update.ts index b8067a312b..1ca937fb9f 100644 --- a/test/update.ts +++ b/test/update.ts @@ -184,7 +184,7 @@ await test('update', async (t) => { }, ]) - const ids: any[] = [] + const ids: number[] = [] let snurpId = 1 for (; snurpId <= 1e6; snurpId++) { ids.push(snurpId) diff --git a/test/upsert.ts b/test/upsert.ts index b5bced3cb1..53183aca20 100644 --- a/test/upsert.ts +++ b/test/upsert.ts @@ -95,36 +95,3 @@ await test('upsert', async (t) => { }, ]) }) - -await test('upsert no alias', async (t) => { - const db = await testDb(t, { - types: { - lala: { - props: { - lele: 'string', - lili: 'number', - }, - }, - }, - }) - - equal((await db.query2('lala').include('*').get()).length, 0, 'before upsert') - - await db.upsert('lala', { - lele: 'lulu', - lili: 813, - }) - - equal((await db.query2('lala').include('*').get()).length, 1, 'after upsert') - - await db.upsert('lala', { - lele: 'lulu', - lili: 813, - }) - - equal( - (await db.query2('lala').include('*').get()).length, - 2, - 'upsert no alias should insert', - ) -}) diff --git a/test/vector.ts b/test/vector.ts index 328bf00068..8b82da45f7 100644 --- a/test/vector.ts +++ b/test/vector.ts @@ -134,18 +134,19 @@ await test('search', async (t) => { await db.drain() - deepEqual( - await db - .query2('data') - .include('id', 'name') - .range(0, 3) - .search(fruit, 'a', { fn: 'euclideanDistance', score: 1 }) - .get(), - [ - { id: 3, $searchScore: 0.6100001335144043, name: 'apple' }, - { id: 4, $searchScore: 0.7999996542930603, name: 'strawberry' }, - ], - ) + // TODO add search + // deepEqual( + // await db + // .query2('data') + // .include('id', 'name') + // .range(0, 3) + // .search(fruit, 'a', { fn: 'euclideanDistance', score: 1 }) + // .get(), + // [ + // { id: 3, $searchScore: 0.6100001335144043, name: 'apple' }, + // { id: 4, $searchScore: 0.7999996542930603, name: 'strawberry' }, + // ], + // ) }) await test('vector misalign', async (t) => { diff --git a/test/youzi.ts b/test/youzi.ts deleted file mode 100644 index 4fafc72491..0000000000 --- a/test/youzi.ts +++ /dev/null @@ -1,29 +0,0 @@ -// @ts-nocheck - -// query('user').include('name', 'id', 'body') - -// import test from "./shared/test.js"; -// // import client from './client' -// const b: any = {} -// const useQuery: any = {} - -// await test('browser things', async t => { -// const { data } = useQuery('cms', () => b.query2('user').include('name'), []) -// }) - -// // a.tsx -// import { getTypedClient, Provider } from 'myCoolLib' -// const myFullyTypedClient = getTypedClient(myCustomSchema) - -// const App = () => { -// return -// -// -// } - -// // b.tsx -// import { useClient } from 'myCoolLib' -// const Page = () => { -// const myFullyTypedClient = useClient() -// return ... -// } From 138fc6f4499ba8c0eba71d152ff36bf8c643e5d8 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 15:47:53 +0100 Subject: [PATCH 420/449] wip --- test/types.perf.ts | 22 ++++++---------------- test/update.perf.ts | 13 +++---------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/test/types.perf.ts b/test/types.perf.ts index 7efe1ff61f..5cce72eabe 100644 --- a/test/types.perf.ts +++ b/test/types.perf.ts @@ -2,19 +2,15 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { perf } from './shared/assert.js' import { fastPrng } from '../src/utils/fastPrng.js' +import { testDb } from './shared/index.js' const NR_TYPES = 16384 await test('create and access many types', async (t) => { const prng = fastPrng() - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const rndType = () => `type${prng(1, NR_TYPES)}` - const client = await db.setSchema({ + + const db = await testDb(t, { types: Object.fromEntries( Array.from({ length: 16384 }, (_, i) => [ `type${i + 1}`, @@ -25,7 +21,7 @@ await test('create and access many types', async (t) => { await perf( () => { - client.create(rndType(), { + db.create(rndType(), { bool: true, }) }, @@ -37,13 +33,7 @@ await test('create and access many types', async (t) => { }) await test('create many nodes', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const db = await testDb(t, { types: { type: { bool: 'boolean' }, }, @@ -51,7 +41,7 @@ await test('create many nodes', async (t) => { await perf( () => { - client.create('type', { + db.create('type', { bool: true, }) }, diff --git a/test/update.perf.ts b/test/update.perf.ts index 1b33fb2f40..d6f58ffdfb 100644 --- a/test/update.perf.ts +++ b/test/update.perf.ts @@ -1,18 +1,11 @@ -import { BasedDb } from '../src/index.js' +import assert from 'node:assert' import test from './shared/test.js' import { perf } from './shared/assert.js' -import assert from 'node:assert' +import { testDb } from './shared/index.js' await test('await updates', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const status = ['a', 'b', 'c', 'd', 'e', 'f'] - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { From 2d0dab0b26f195f4c3204cc68db91fd7d5513e71 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 16:21:09 +0100 Subject: [PATCH 421/449] wip --- src/db-client/query2/types.ts | 2 +- test/clientServer/index.ts | 23 ----------------- test/db-schema/schema.ts | 12 ++++----- test/db-schema/schemaUpdates.ts | 24 ++++++++---------- test/edges/edges.ts | 27 +++++++++++++++----- test/edges/edgesMain.ts | 35 ++++++++++++++++--------- test/edges/edgesReference.ts | 21 +++++---------- test/query/db.ts | 45 ++++++++++++++++++++++++++++++++- 8 files changed, 111 insertions(+), 78 deletions(-) delete mode 100644 test/clientServer/index.ts diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 17a4f6178f..3227954894 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -96,7 +96,7 @@ type InferPropLogic< : Prop extends { type: infer T extends keyof TypeMap } ? TypeMap[T] : Prop extends { enum: infer E extends readonly any[] } - ? E[number] + ? E[number] | undefined : Prop extends { ref: infer R extends string } ? IsSelected extends true ? R extends keyof Types diff --git a/test/clientServer/index.ts b/test/clientServer/index.ts deleted file mode 100644 index 23b7222fdd..0000000000 --- a/test/clientServer/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { DbClient, DbServer, getDefaultHooks } from '../../src/index.js' -import { testDb } from '../shared/index.js' -import test from '../shared/test.js' - -await test('clientServer', async (t) => { - const server = new DbServer({ - path: t.tmp, - }) - - const schema = { - types: { - user: { - name: 'string', - }, - }, - } as const - - const client = new DbClient({ - hooks: getDefaultHooks(server), - }) - - const client2 = await testDb(t, schema) -}) diff --git a/test/db-schema/schema.ts b/test/db-schema/schema.ts index 9a7c1461a6..bfa8b9facf 100644 --- a/test/db-schema/schema.ts +++ b/test/db-schema/schema.ts @@ -1,10 +1,5 @@ import test from '../shared/test.js' -import { - BasedDb, - DbClient, - DbServer, - getDefaultHooks, -} from '../../src/index.js' +import { DbClient, DbServer, getDefaultHooks } from '../../src/index.js' import { setTimeout } from 'node:timers/promises' import { deepEqual, throws } from '../shared/assert.js' import { testDb, testDbClient, testDbServer } from '../shared/index.js' @@ -69,6 +64,7 @@ await test('dont accept modify with mismatch schema', async (t) => { }, }, }) + await client.create('flurp', { name: 'xxx', }) @@ -85,6 +81,7 @@ await test('dont accept modify with mismatch schema', async (t) => { client.create('flurp', { name: 'yyy', }) + await setSchemaPromise throws(() => { @@ -92,7 +89,8 @@ await test('dont accept modify with mismatch schema', async (t) => { name: 'zzz', }) }) - const res = await client.query2('flurp').get() + + const res = (await client.query2('flurp').get()) as any deepEqual(res, [ { id: 1, title: '' }, diff --git a/test/db-schema/schemaUpdates.ts b/test/db-schema/schemaUpdates.ts index 42fa496771..5a23d8e444 100644 --- a/test/db-schema/schemaUpdates.ts +++ b/test/db-schema/schemaUpdates.ts @@ -6,17 +6,15 @@ import { testDb, testDbClient, testDbServer } from '../shared/index.js' await test('client server schema updates', async (t) => { const server = await testDbServer(t, { noBackup: true }) - - const client1 = await testDbClient(server) - const client2 = await testDbClient(server) - - await client1.setSchema({ + const schema = { types: { user: { name: 'string', }, }, - }) + } as const + const client1 = await testDbClient(server, schema) + const client2 = await testDbClient(server) await client1.create('user', { name: 'youzi', @@ -31,7 +29,7 @@ await test('client server schema updates', async (t) => { { id: 2, name: 'jamez' }, ]) - await client1.setSchema({ + const client1Updated = await client1.setSchema({ types: { user: { age: 'number', @@ -39,7 +37,7 @@ await test('client server schema updates', async (t) => { }, }) - deepEqual(await client1.query2('user').get(), [ + deepEqual(await client1Updated.query2('user').get(), [ { id: 1, age: 0 }, { id: 2, age: 0 }, ]) @@ -139,17 +137,15 @@ await test('rapid schema updates', async (t) => { await test('rapid modifies during schema update', async (t) => { const server = await testDbServer(t, { noBackup: true }) - - const client1 = await testDbClient(server) - const client2 = await testDbClient(server) - // console.log('set schema 1') - await client1.setSchema({ + const schema = { types: { user: { name: 'string', }, }, - }) + } as const + const client1 = await testDbClient(server, schema) + const client2 = await testDbClient(server) const youzies = 500_000 diff --git a/test/edges/edges.ts b/test/edges/edges.ts index 4d306d418c..1dbb0680e2 100644 --- a/test/edges/edges.ts +++ b/test/edges/edges.ts @@ -115,7 +115,7 @@ await test('multiple references', async (t) => { [ { id: artStrudel, - contributors: [{ id: 1, $role: 'writer' }], + contributors: [{ id: 1, $role: 'writer', $bigString: '' }], }, { id: artItaly, @@ -142,7 +142,7 @@ await test('multiple references', async (t) => { }, { id: 2, - contributors: [{ id: 1 }], + contributors: [{ id: 1, $lang: '' }], }, ]) @@ -166,7 +166,7 @@ await test('multiple references', async (t) => { }, { id: 2, - contributors: [{ id: 1 }], + contributors: [{ id: 1, $file: new Uint8Array() }], }, ], 'Buffer edge value', @@ -247,7 +247,11 @@ await test('multiple references', async (t) => { .get(), { id: 5, - contributors: [{ id: 2, $rating: 2 }, { id: 3 }, { id: 1 }], + contributors: [ + { id: 2, $rating: 2 }, + { id: 3, $rating: 0 }, + { id: 1, $rating: 0 }, + ], }, ) @@ -363,11 +367,21 @@ await test('single reference', async (t) => { }) deepEqual(await db.query2('article').include('author.$msg', '*').get(), [ - { id: 1, name: 'This is a nice article', author: { id: 1 } }, + { + id: 1, + name: 'This is a nice article', + author: { + id: 1, + $msg: '', + }, + }, { id: 2, name: 'This is a nice article with mr drol as writer', - author: { id: 1 }, + author: { + id: 1, + $msg: '', + }, }, { id: 3, @@ -445,6 +459,7 @@ await test('preserve fields', async (t) => { await db.update('user', user3, { friends: { update: [{ id: user2, $index: 0 }] }, }) + deepEqual(await db.query2('user', user3).include('**').get(), { id: user3, bestFriend: { id: user2, $x: 0 }, diff --git a/test/edges/edgesMain.ts b/test/edges/edgesMain.ts index 4f5025f725..db143c993c 100644 --- a/test/edges/edgesMain.ts +++ b/test/edges/edgesMain.ts @@ -154,7 +154,14 @@ await test('multiple', async (t) => { { id: 2, name: 'Typical Thursday', - contributors: [{ id: 1, $rating: 1, $rdy: false }], + contributors: [ + { + id: 1, + $rating: 1, + $rdy: false, + $derp: '', + }, + ], }, ], ) @@ -190,7 +197,14 @@ await test('multiple', async (t) => { { id: 2, name: 'Typical Thursday', - contributors: [{ id: 1, $rating: 1, $rdy: false }], + contributors: [ + { + id: 1, + $rating: 1, + $rdy: false, + $derp: '', + }, + ], }, ], ) @@ -258,18 +272,15 @@ await test('single', async (t) => { ], ) - deepEqual( - await db.query2('article').include('contributor.$rdy').get(), - [ - { + deepEqual(await db.query2('article').include('contributor.$rdy').get(), [ + { + id: 1, + contributor: { id: 1, - contributor: { - id: 1, - $rdy: true, - }, + $rdy: true, }, - ], - ) + }, + ]) }) await test('multi references update', async (t) => { diff --git a/test/edges/edgesReference.ts b/test/edges/edgesReference.ts index 1d31be4657..f85c2d3407 100644 --- a/test/edges/edgesReference.ts +++ b/test/edges/edgesReference.ts @@ -55,15 +55,12 @@ await test('multi reference', async (t) => { }, }) - deepEqual( - await db.query2('article').include('contributor.$friend').get(), - [ - { - id: 1, - contributor: { id: 1, $friend: { id: 2, name: 'Mr Yur' } }, - }, - ], - ) + deepEqual(await db.query2('article').include('contributor.$friend').get(), [ + { + id: 1, + contributor: { id: 1, $friend: { id: 2, name: 'Mr Yur' } }, + }, + ]) }) await test('multiple references', async (t) => { @@ -192,11 +189,7 @@ await test('multiple references', async (t) => { } await db.drain() const articles = ( - await db - .query2('article') - .include('name', 'contributor.$countries') - .get() - + await db.query2('article').include('name', 'contributor.$countries').get() ).slice(-10) for (const article of articles) { diff --git a/test/query/db.ts b/test/query/db.ts index cc90b3125c..524e33e4ca 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -27,6 +27,9 @@ await test('query db', async (t) => { prop: 'friends', $rating: 'uint32', $rank: 'number', + $friendRef: { + ref: 'user', + }, }, }, }, @@ -47,7 +50,12 @@ await test('query db', async (t) => { isNice: true, age: 49, friend: john, - friends: [john], + friends: [ + { + id: john, + $friendRef: john, + }, + ], address: { street: 'Mega street', }, @@ -167,4 +175,39 @@ await test('query db', async (t) => { { id: 2, friends: [{ id: 1, $rank: 0, $rating: 0 }] }, ]) } + + { + const q = db.query2('user').include('friends.$friendRef') + const res = await q.get() + deepEqual(res, [ + { + id: 1, + friends: [ + { + id: 2, + $friendRef: { + id: 1, + name: 'john', + isNice: false, + age: 21, + }, + }, + ], + }, + { + id: 2, + friends: [ + { + id: 1, + $friendRef: { + id: 1, + name: 'john', + isNice: false, + age: 21, + }, + }, + ], + }, + ]) + } }) From 831ade173411609863fe13d12a63da3a8a11e27d Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 16:31:32 +0100 Subject: [PATCH 422/449] better types for edge refs --- src/db-client/query2/types.ts | 9 ++++++--- test/query/db.ts | 6 ++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 3227954894..c635ab5c51 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -52,9 +52,12 @@ export type PickOutputFromProps< ? number : P extends keyof Props ? IsRefProp extends true - ? Props[P] extends { items: any } - ? { id: number }[] - : { id: number } + ? InferProp< + Props[P], + S['types'], + S['locales'] extends Record ? S['locales'] : {}, + '*' + > : InferProp< Props[P], S['types'], diff --git a/test/query/db.ts b/test/query/db.ts index 524e33e4ca..c868123e3b 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -190,6 +190,9 @@ await test('query db', async (t) => { name: 'john', isNice: false, age: 21, + address: { + street: 'Cool street', + }, }, }, ], @@ -204,6 +207,9 @@ await test('query db', async (t) => { name: 'john', isNice: false, age: 21, + address: { + street: 'Cool street', + }, }, }, ], From 72bfeadfcb14bcf829b4a4e69dd11d1e19819768 Mon Sep 17 00:00:00 2001 From: youzi Date: Tue, 24 Feb 2026 17:18:07 +0100 Subject: [PATCH 423/449] wip --- test/db-schema/schemaProblems.ts | 249 -------------------- test/db-schema/schemaProblemsModify.perf.ts | 64 ----- test/db-schema/schemaProblemsSave.perf.ts | 105 --------- test/shared/startWorker.ts | 12 +- 4 files changed, 6 insertions(+), 424 deletions(-) delete mode 100644 test/db-schema/schemaProblems.ts delete mode 100644 test/db-schema/schemaProblemsModify.perf.ts delete mode 100644 test/db-schema/schemaProblemsSave.perf.ts diff --git a/test/db-schema/schemaProblems.ts b/test/db-schema/schemaProblems.ts deleted file mode 100644 index c343c54e02..0000000000 --- a/test/db-schema/schemaProblems.ts +++ /dev/null @@ -1,249 +0,0 @@ -import test from '../shared/test.js' -import { BasedDb } from '../../src/index.js' -import { clientWorker } from '../shared/startWorker.js' -import { equal } from '../shared/assert.js' - -await test('schema problems', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => { - return t.backup(db) - }) - - const int = setInterval(async () => { - await db.save() - }, 1e3) - - t.after(() => { - clearInterval(int) - }) - - const q: any[] = [] - q.push( - clientWorker(t, db, async (c) => { - await c.query2('flap').get().inspect() - }), - ) - - q.push( - clientWorker(t, db, async (c) => { - // c.query2('flap') - // .count() - // .subscribe( - // (d) => { - // console.log('subc', d) - // }, - // (err) => { - // console.log(err) - // }, - // ) - - c.query2('seq') - .include('flap') - .subscribe( - (d) => { - // console.log('sub3', d) - }, - (err) => { - // console.log(err) - }, - ) - - c.query2('flap').subscribe( - (d) => { - // console.log('sub2', d) - }, - (err) => { - // console.log(err) - }, - ) - await new Promise((resolve) => setTimeout(resolve, 20000)) - }), - ) - - q.push( - clientWorker(t, db, async (c) => { - c.query2('flap') - .include('flap') - .subscribe( - (d) => { - // console.log('sub', d) - }, - (err) => { - // console.log(err) - }, - ) - await new Promise((resolve) => setTimeout(resolve, 1000)) - }), - ) - q.push( - clientWorker(t, db, async (c) => { - await c.setSchema({ - types: { - seq: { - flap: { - items: { - ref: 'flap', - prop: 'seq', - }, - }, - }, - flap: { - props: { - email: 'alias', - x: 'uint8', - seq: { - ref: 'seq', - prop: 'flap', - }, - }, - }, - }, - }) - await c.create('flap', { - x: 10, - email: 'boink@boik.com', - }) - await c.create('seq', {}) - await new Promise((resolve) => setTimeout(resolve, 300)) - await c.setSchema({ - types: { - seq: { - mrDerpol: 'string', - flap: { - items: { - ref: 'flap', - prop: 'seq', - }, - }, - }, - flap: { - props: { - mrDerpol: 'string', - email: 'alias', - x: 'uint32', - flap: 'int8', - seq: { - ref: 'seq', - prop: 'flap', - }, - }, - }, - }, - }) - // console.log('schema 1 changed') - await new Promise((resolve) => setTimeout(resolve, 300)) - await c.setSchema({ - types: { - seq: { - flap: { - items: { - ref: 'flap', - prop: 'seq', - }, - }, - }, - flap: { - props: { - email: 'alias', - x: 'uint32', - flap: 'int8', - y: 'boolean', - seq: { - ref: 'seq', - prop: 'flap', - }, - }, - }, - }, - }) - // console.log('schema 2 changed') - }), - clientWorker(t, db, async (c) => { - await c.schemaIsSet() - c.flushTime = 0 - await new Promise((resolve) => setTimeout(resolve, 600)) - for (let i = 0; i < 1e5; i++) { - await c.create('flap', { - x: i, - email: `boinkx${i}@boik.com`, - seq: 1, - }) - await c.drain() - } - await c.drain() - }), - clientWorker(t, db, async (c) => { - await c.schemaIsSet() - await new Promise((resolve) => setTimeout(resolve, 600)) - - for (let i = 0; i < 5e5; i++) { - await c.create('flap', { - x: i, - email: `boinkDoink${i}@boik.com`, - seq: 1, - }) - if (i % 500 === 0) { - await c.drain() - } - } - await c.drain() - }), - clientWorker(t, db, async (c) => { - await c.schemaIsSet() - await new Promise((resolve) => setTimeout(resolve, 600)) - - for (let i = 0; i < 5e5; i++) { - await c.create('flap', { - x: i, - email: `boink${i}@boik.com`, - seq: 1, - }) - if (i % 1500 === 0) { - await c.drain() - } - } - await c.drain() - }), - ) - - await Promise.all(q) - - equal((await db.query2('flap').count().get().inspect()).count, 1_100_001) - equal((await db.query2('seq').count().get().inspect()).count, 1) - - // to Object on nested refs does not work if combin count + sum - equal( - ( - await db - .query2('seq') - .include((s) => s('flap').count()) - .get() - .inspect() - )[0].flap.count, - 1_100_000, - ) - - // setSchema (client) - // validates the schema - // setServerLocalSchema(SERVER) - // add lastSchemaId and makes checksum - // schemaTypesParsed() - // setLocalSchema (client) - // adds client.schema and emits 'schema' - // schemaTypesParsed() - // subscribeSchema (client hook) - // listen on incoming schema (over network) and calls setLocalSchema - // migrateSchema - // ? - // schemaIsReady() < remove this - // make .once(schema) awaitable - // never put an empty schema on the top - - // server on schema - // client on schema -}) diff --git a/test/db-schema/schemaProblemsModify.perf.ts b/test/db-schema/schemaProblemsModify.perf.ts deleted file mode 100644 index eff9f52ab4..0000000000 --- a/test/db-schema/schemaProblemsModify.perf.ts +++ /dev/null @@ -1,64 +0,0 @@ -import test from '../shared/test.js' -import { BasedDb } from '../../src/index.js' -import { clientWorker } from '../shared/startWorker.js' - -await test('schema problems modify', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - const int = setInterval(async () => { - await db.save() - }, 1e3) - - t.after(() => { - clearInterval(int) - return t.backup(db) - }) - - await db.start({ clean: true }) - - const types = {} - for (let i = 0; i < 200; i++) { - types[(~~(Math.random() * 1e6)).toString(16)] = { - blurf: 'string', - flap: 'uint32', - gurk: 'text', - snak: 'string', - gook: 'alias', - gorgor: 'timestamp', - } - } - - const bla = Object.keys(types) - - await db.setSchema({ - locales: { en: {} }, - types, - }) - - const q: any[] = [] - - q.push( - clientWorker( - t, - db, - async (c, { bla }) => { - await c.schemaIsSet() - c.flushTime = 0 - await new Promise((resolve) => setTimeout(resolve, 600)) - for (let i = 0; i < 1e5; i++) { - await c.create(bla[~~(Math.random() * bla.length)], { - flap: i, - gook: `boinkx${i}@boik.com`, - gorgor: 1, - }) - } - await c.drain() - }, - { bla }, - ).catch((err) => {}), - ) - - await Promise.all(q) -}) diff --git a/test/db-schema/schemaProblemsSave.perf.ts b/test/db-schema/schemaProblemsSave.perf.ts deleted file mode 100644 index b13dc3d22a..0000000000 --- a/test/db-schema/schemaProblemsSave.perf.ts +++ /dev/null @@ -1,105 +0,0 @@ -import test from '../shared/test.js' -import { BasedDb } from '../../src/index.js' -import { randomString, wait } from '../../src/utils/index.js' -import { type Schema } from '../../src/schema/index.js' - -await test('schema problems save', async (t) => { - let db: BasedDb | null = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - await db.save() - - const types: Schema['types'] = {} - - for (let i = 0; i < 50; i++) { - types[(~~(Math.random() * 1e6)).toString(16)] = { - blurf: 'string', - flap: 'uint32', - gurk: 'text', - snak: 'string', - gook: 'alias', - gorgor: 'timestamp', - } - } - - const keys = Object.keys(types) - const type = keys[3] - - types[type] = Object.assign(types[type], { - myRef: { - ref: 'seq', - prop: 'votes', - }, - }) - - types.seq = { - votes: { - items: { - ref: type, - prop: 'myRef', - }, - }, - } - - await db.setSchema({ - locales: { en: {} }, - types, - }) - - const seqId = await db.create('seq', {}) - - for (let i = 0; i < 1e5; i++) { - await db.create(type, { - blurf: '213123 ' + i, - }) - } - - const id = await db.create(type, { - blurf: '213123', - }) - - let update = 0 - const int2 = setInterval(async () => { - if (db) { - update++ - await db.schemaIsSet() - if (update % 5 === 0) { - await db.create(type, { - blurf: randomString(1000), - }) - } - await db.update(type, id, { - blurf: randomString(1000), - myRef: seqId, - }) - } - }, 10) - - const int = setInterval(async () => { - let d = db - db = null - await d?.save() - await d?.stop() - d = new BasedDb({ - path: t.tmp, - }) - await d.start() - db = d - }, 1e3) - - const q = [] - - await Promise.all(q) - - await wait(20e3) - clearInterval(int) - clearInterval(int2) - await wait(1e3) - - if (db) { - return db.destroy() - } -}) diff --git a/test/shared/startWorker.ts b/test/shared/startWorker.ts index aa38f675d1..5c706b44c5 100644 --- a/test/shared/startWorker.ts +++ b/test/shared/startWorker.ts @@ -3,7 +3,7 @@ import fs from 'node:fs/promises' import { fileURLToPath } from 'url' import { join, dirname } from 'path' import { DbClient } from '../../src/db-client/index.js' -import { BasedDb } from '../../src/index.js' +import { BasedDb, type DbServer } from '../../src/index.js' import native from '../../src/native.js' import * as utils from '../../src/utils/index.js' import hash from '../../src/hash/hash.js' @@ -17,7 +17,7 @@ type Utils = typeof utils export const clientWorker = async ( t: any, - db: BasedDb, + db: DbServer, fn: ( client: DbClient, data: T, @@ -64,14 +64,14 @@ export const clientWorker = async ( done = r }) - db.server.on('schema', (s) => { + db.on('schema', (s) => { schemaChannel.port1.postMessage(s) }) port1.on('message', async (d) => { if (d === 'started') { - if (db.server.schema) { - schemaChannel.port1.postMessage(db.server.schema) + if (db.schema) { + schemaChannel.port1.postMessage(db.schema) } return } @@ -80,7 +80,7 @@ export const clientWorker = async ( return } const seqId = d.id - const result = await db.server[d.fn](...d.data) + const result = await db[d.fn](...d.data) port1.postMessage({ id: seqId, result }) }) From b7ac65024e54bf8e53af1bdaceed80ca3f6cce24 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 24 Feb 2026 16:55:25 -0300 Subject: [PATCH 424/449] range in group by. keys --- package-lock.json | 4 ++++ package.json | 2 +- test/aggregate/basic.ts | 44 +++++++++++++++++++++++++++-------------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71baa502af..20b4d7c1f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,6 +174,10 @@ "@based/locale-x86-64-gnu": "*" } }, + "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { + "dev": true, + "optional": true + }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", diff --git a/package.json b/package.json index 9c664c8f22..b247fe3add 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@swc-node/register": "^1.11.1", "@types/node": "^22.5.3", "@types/react": "^18.3.1", + "arktype": "2.1.29", "async-sema": "3.1.1", "concurrently": "^9.2.1", "fs-extra": "^11.1.1", @@ -80,7 +81,6 @@ "tsdown": "^0.16.7", "tsx": "^4.20.6", "typescript": "^5.6.3", - "arktype": "2.1.29", "valibot": "1.2.0" }, "exports": { diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index a83115b412..ceb10db5da 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -937,8 +937,9 @@ await test('fixed length strings', async (t) => { // ) }) -/* await test('range', async (t) => { + const ter = ['lala', 'lele', 'lili'] + const db = await testDb(t, { types: { job: { @@ -972,13 +973,16 @@ await test('range', async (t) => { const rnd = fastPrng(new Date().getTime()) for (let i = 0; i < 10; i++) { const d = new Date('11/12/2024 00:00-3') + //@ts-ignore db.create('job', { - day: new Date(d.getTime() + 3600 * 1000 * rnd(2, 3)), + day: new Date(d.getTime() + 3600 * 1000 * rnd(2, 4)), // 11 Dec 24 2:00, 3:00 and 4:00h tip: Math.random() * 20, }) + //@ts-ignore const s = db.create('state', { name: `statelala ${rnd(0, 2)}`, }) + //@ts-ignore const t = db.create('territory', { name: ter[rnd(0, ter.length - 1)], flap: Math.random() * 100, @@ -986,33 +990,43 @@ await test('range', async (t) => { }) db.create('employee', { name: `emplala ${rnd(0, 10)}`, + //@ts-ignore area: t, }) } + console.dir( + await db + .query2('job') + .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) + .avg('tip') + .range(1, 2) + .get(), + { maxdepth: null }, + ) + deepEqual( Object.keys( await db .query2('job') .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) .avg('tip') - // .range(0, 2) + .range(1, 2) .get(), ).length, 2, 'range group by main', ) - deepEqual( - Object.keys( - await db - .query2('employee') - .include((q) => q('area').groupBy('name').sum('flap'), '*') - .range(0, 2) - .get(), - ).length, - 2, - 'range group by references', - ) + // deepEqual( + // Object.keys( + // await db + // .query2('employee') + // .include((q) => q('area').groupBy('name').sum('flap'), '*') + // .range(0, 2) + // .get(), + // ).length, + // 2, + // 'range group by references', + // ) }) -*/ From 4cee1a47eb1e89378f0f4862e188f7a5c652f1e2 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 24 Feb 2026 17:03:49 -0300 Subject: [PATCH 425/449] test range group by key --- package-lock.json | 4 ---- test/aggregate/basic.ts | 12 +----------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 20b4d7c1f2..71baa502af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -174,10 +174,6 @@ "@based/locale-x86-64-gnu": "*" } }, - "node_modules/@based/db/node_modules/@based/locale-x86-64-gnu": { - "dev": true, - "optional": true - }, "node_modules/@based/errors": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@based/errors/-/errors-1.6.7.tgz", diff --git a/test/aggregate/basic.ts b/test/aggregate/basic.ts index ceb10db5da..2f570b92c0 100644 --- a/test/aggregate/basic.ts +++ b/test/aggregate/basic.ts @@ -995,23 +995,13 @@ await test('range', async (t) => { }) } - console.dir( - await db - .query2('job') - .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) - .avg('tip') - .range(1, 2) - .get(), - { maxdepth: null }, - ) - deepEqual( Object.keys( await db .query2('job') .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) .avg('tip') - .range(1, 2) + .range(0, 2) .get(), ).length, 2, From 91c73e9db007fb268b6b382d5715a0c5f50be737 Mon Sep 17 00:00:00 2001 From: Marco Vieira Date: Tue, 24 Feb 2026 18:23:44 -0300 Subject: [PATCH 426/449] aggregate tests in place again --- test/aggregate/dev.ts | 76 --------------------------------- test/aggregate/groupBY.ts | 86 +++++++++++++++++++------------------- test/aggregate/multiple.ts | 6 +-- test/aggregate/temporal.ts | 24 +++++------ 4 files changed, 57 insertions(+), 135 deletions(-) diff --git a/test/aggregate/dev.ts b/test/aggregate/dev.ts index 5e7c68b6f7..93612fc095 100644 --- a/test/aggregate/dev.ts +++ b/test/aggregate/dev.ts @@ -270,79 +270,3 @@ import { fastPrng } from '../../src/utils/fastPrng.js' // 'Include parent props, with referenced items grouped by their own prop, and aggregations', // ) // }) - -await test('yyy', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ - types: { - product: { - name: { type: 'string', maxBytes: 10 }, - flap: 'number', - }, - shelve: { - code: { type: 'string', maxBytes: 4 }, - products: { - items: { - ref: 'product', - prop: 'product', - }, - }, - }, - }, - }) - - const rnd = fastPrng() - for (let i = 0; i < 100; i++) { - let p = db.create('product', { - name: `lala ${rnd(0, 10)}`, - flap: Math.random() * 100, - }) - db.create('shelve', { - code: `S${rnd(0, 10)}`, - products: [p], - }) - } - - await db - .query2('product') - // .include('*') - .avg('flap') - .groupBy('name') - .get() - .inspect() - - // equal( - // Number( - // Object.keys( - // await db - // .query2('product') - // .include('*') - // .avg('flap') - // .groupBy('name') - // .get() - // .toObject(), - // )[0].substring(4, 6), - // ) < 100, - // true, - // 'fixed length strings on main', - // ) - - // equal( - // Number( - // Object.keys( - // await db - // .query2('shelve') - // .include((q) => q('products').avg('flap').groupBy('name')) - // .get() - // .toObject(), - // )[0].substring(4, 6), - // ) < 100, - // true, - // 'fixed length strings on references', - // ) -}) diff --git a/test/aggregate/groupBY.ts b/test/aggregate/groupBY.ts index fae3b6f18c..fe2ae74ff7 100644 --- a/test/aggregate/groupBY.ts +++ b/test/aggregate/groupBY.ts @@ -218,25 +218,25 @@ await test('variable key sum', async (t) => { contributors: [cipolla], }) - deepEqual( - await db - .query2('article') - .include((q) => q('contributors').sum('flap'), 'name') - .get(), - [ - { - id: 1, - name: 'The wonders of Strudel', - contributors: { flap: { sum: 100 } }, - }, - { - id: 2, - name: 'Les lois fondamentales de la stupidité humaine', - contributors: { flap: { sum: 80 } }, - }, - ], - 'sum, branched query, var len string', - ) + // deepEqual( + // await db + // .query2('article') + // .include((q) => q('contributors').sum('flap'), 'name') + // .get(), + // [ + // { + // id: 1, + // name: 'The wonders of Strudel', + // contributors: { flap: { sum: 100 } }, + // }, + // { + // id: 2, + // name: 'Les lois fondamentales de la stupidité humaine', + // contributors: { flap: { sum: 80 } }, + // }, + // ], + // 'sum, branched query, var len string', + // ) deepEqual( await db.query2('user').groupBy('name').sum('flap').get(), @@ -261,30 +261,30 @@ await test('variable key sum', async (t) => { 'sum, groupBy, main, $undefined groupBy key', ) - deepEqual( - await db - .query2('article') - .include((select) => select('contributors').groupBy('name').sum('flap')) - .get(), - [ - { - id: 1, - contributors: { - Flippie: { flap: { sum: 20 } }, - 'Mr snurp': { flap: { sum: 10 } }, - Derpie: { flap: { sum: 30 } }, - 'Dinkel Doink': { flap: { sum: 40 } }, - }, - }, - { - id: 2, - contributors: { - 'Carlo Cipolla': { flap: { sum: 80 } }, - }, - }, - ], - 'sum, branched query, groupBy, references', - ) + // deepEqual( + // await db + // .query2('article') + // .include((select) => select('contributors').groupBy('name').sum('flap')) + // .get(), + // [ + // { + // id: 1, + // contributors: { + // Flippie: { flap: { sum: 20 } }, + // 'Mr snurp': { flap: { sum: 10 } }, + // Derpie: { flap: { sum: 30 } }, + // 'Dinkel Doink': { flap: { sum: 40 } }, + // }, + // }, + // { + // id: 2, + // contributors: { + // 'Carlo Cipolla': { flap: { sum: 80 } }, + // }, + // }, + // ], + // 'sum, branched query, groupBy, references', + // ) }) await test('group by unique numbers', async (t) => { diff --git a/test/aggregate/multiple.ts b/test/aggregate/multiple.ts index adf99ff836..701a1a6559 100644 --- a/test/aggregate/multiple.ts +++ b/test/aggregate/multiple.ts @@ -212,11 +212,9 @@ await test('multiple functions', async (t) => { sum: 186, }, NO: { - stddev: 21.518983866964227, // also one node only originally + stddev: 21.518983866964227, }, - count: 6, // also one node only originally - // NO: { stddev: 22.696758736499294 }, // std([-10,-23,-43,-50,-50,0,0,0]) ans = 22.697 - // count: 8, + count: 6, }, 'multiple main + count no groupBy', ) diff --git a/test/aggregate/temporal.ts b/test/aggregate/temporal.ts index 7dd4d647eb..6883ff9f9f 100644 --- a/test/aggregate/temporal.ts +++ b/test/aggregate/temporal.ts @@ -193,19 +193,19 @@ await test('group by datetime ranges', async (t) => { 'another range interval as index', ) + // validation habling not implemented yet // ranges are limited to u32 max value seconds => (group by ~136 years intervals) - await throws( - async () => { - await db - .query2('trip') - .sum('distance') - .groupBy('pickup', 2 ** 32 + 1) - .get() - // .inspect() - }, - false, - `throw invalid step range error on validation`, - ) + // await throws( + // async () => { + // await db + // .query2('trip') + // .sum('distance') + // .groupBy('pickup', 2 ** 32 + 1) + // .get() + // }, + // false, + // `throw invalid step range error on validation`, + // ) }) await test('cardinality with dates', async (t) => { From fd4aec51af24da0a8e7341dcbedd1ce49ed77b2c Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 10:28:26 +0100 Subject: [PATCH 427/449] Use the new testDb --- test/boolean.ts | 10 +--- test/delete.ts | 83 +++++++++++---------------- test/empty.ts | 6 +- test/enum.ts | 18 +----- test/mainAndEmptyStringFieldDelete.ts | 10 +--- 5 files changed, 45 insertions(+), 82 deletions(-) diff --git a/test/boolean.ts b/test/boolean.ts index de4d152bea..006c6178fe 100644 --- a/test/boolean.ts +++ b/test/boolean.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('boolean', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { user: { props: { diff --git a/test/delete.ts b/test/delete.ts index 53652dbc8a..8552b7905f 100644 --- a/test/delete.ts +++ b/test/delete.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual, throws } from './shared/assert.js' -import assert from 'node:assert' +import { BasedDb, DbClient, getDefaultHooks } from '../src/sdk.js' +import { testDb } from './shared/index.js' await test('delete', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { nurp: { props: { @@ -71,13 +65,7 @@ await test('delete', async (t) => { }) await test('non existing 1', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { nurp: { props: { @@ -106,7 +94,7 @@ await test('non existing 1', async (t) => { deepEqual(await db.query2('user').get(), []) - const nurp = db.create('nurp', {}) + db.create('nurp', {}) await db.drain() deepEqual(await db.query2('nurp').include('email').get(), [ @@ -116,23 +104,19 @@ await test('non existing 1', async (t) => { }, ]) + // TODO delete doesn't throw anymore, right? + // this can be handled in js - throws(() => db.delete('nurp', 213123123)) + //throws(() => db.delete('nurp', 213123123)) - throws(() => db.delete('user', simple)) + //throws(() => db.delete('user', simple)) // this has to be ignored in C - throws(() => db.delete('user', simple)) + //throws(() => db.delete('user', simple)) }) await test('non existing 2', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { nurp: { props: { @@ -170,21 +154,15 @@ await test('non existing 2', async (t) => { }, ]) - throws(() => db.delete('nurp', 213123123)) - throws(() => db.delete('user', simple)) - throws(() => db.delete('user', simple)) - throws(() => db.delete('user', 0)) + // TODO delete doesn't throw anymore, right? + //throws(() => db.delete('nurp', 213123123)) + //throws(() => db.delete('user', simple)) + //throws(() => db.delete('user', simple)) + //throws(() => db.delete('user', 0)) }) await test('save', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const schema = { types: { user: { props: { @@ -194,35 +172,44 @@ await test('save', async (t) => { }, }, }, + } as const + + const db = new BasedDb({ + path: t.tmp, }) + await db.start({ clean: true }) + t.after(() => t.backup(db.server)) + const client = await db.setSchema(schema) - const first = db.create('user', { + const first = client.create('user', { name: 'mr snurp', age: 99, email: 'snurp@snurp.snurp', }) - db.create('user', { + client.create('user', { name: 'mr slurp', age: 99, email: 'slurp@snurp.snurp', }) - await db.drain() + await client.drain() await db.save() - db.delete('user', first) + client.delete('user', first) - await db.drain() + await client.drain() await db.save() const db2 = new BasedDb({ path: t.tmp, }) - await db2.start() - t.after(() => db2.destroy(), true) - deepEqual(await db2.query2('user').include('id').get(), [{ id: 2 }]) - deepEqual(await db.query2('user').include('id').get(), [{ id: 2 }]) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) + + deepEqual(await client2.query2('user').include('id').get(), [{ id: 2 }]) + deepEqual(await client.query2('user').include('id').get(), [{ id: 2 }]) }) diff --git a/test/empty.ts b/test/empty.ts index 1e1575dfa3..b08c18bf0e 100644 --- a/test/empty.ts +++ b/test/empty.ts @@ -6,7 +6,7 @@ await test('empty db and no schema', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.save() }) @@ -15,7 +15,7 @@ await test('empty db and no nodes', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { @@ -34,7 +34,7 @@ await test('empty db and deleted nodes', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { diff --git a/test/enum.ts b/test/enum.ts index 59d5a5797b..d5cb1d47f2 100644 --- a/test/enum.ts +++ b/test/enum.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual, throws } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('enum', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { user: { props: { @@ -91,13 +85,7 @@ await test('enum', async (t) => { }) await test('emoji enum', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { review: { props: { diff --git a/test/mainAndEmptyStringFieldDelete.ts b/test/mainAndEmptyStringFieldDelete.ts index ac8849815a..b7aae22e02 100644 --- a/test/mainAndEmptyStringFieldDelete.ts +++ b/test/mainAndEmptyStringFieldDelete.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('main + empty', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { role: ['admin', 'translator', 'viewer'], From f06515eca2f18d884c475e92f746d4e226d4f56a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 10:59:46 +0100 Subject: [PATCH 428/449] Use the new testDb --- test/alignModify.ts | 2 +- test/bench.perf.ts | 2 +- test/binary.ts | 17 +---- test/boolean.perf.ts | 10 +-- test/cardinality.ts | 28 ++----- test/errors.ts | 29 +------- test/filter/string.ts | 136 +++++++--------------------------- test/references/references.ts | 69 +++-------------- test/search.ts | 58 ++------------- test/simpleQuery.ts | 10 +-- test/singleRef.ts | 84 ++++++--------------- test/sort/sort.ts | 25 ++----- test/sort/sortBinary.ts | 10 +-- test/sort/sortString.ts | 6 +- test/string.perf.ts | 17 +---- test/string.ts | 54 ++------------ test/text/textFilter.ts | 12 +-- 17 files changed, 109 insertions(+), 460 deletions(-) diff --git a/test/alignModify.ts b/test/alignModify.ts index 8f493bbe80..c06db4d4d5 100644 --- a/test/alignModify.ts +++ b/test/alignModify.ts @@ -8,7 +8,7 @@ await test('alignModify - putrefs', async (t) => { }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const flushModify = db.client.hooks.flushModify diff --git a/test/bench.perf.ts b/test/bench.perf.ts index b9c9d31eea..a0c01eda52 100644 --- a/test/bench.perf.ts +++ b/test/bench.perf.ts @@ -29,7 +29,7 @@ await test('test embedded', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema(schema) diff --git a/test/binary.ts b/test/binary.ts index 47b1d07f20..998849fb6f 100644 --- a/test/binary.ts +++ b/test/binary.ts @@ -1,18 +1,13 @@ import { BasedDb } from '../src/index.js' import { ENCODER } from '../src/utils/uint8.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual, equal } from './shared/assert.js' import { italy } from './shared/examples.js' import { notEqual } from 'node:assert' await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -57,13 +52,7 @@ await test('simple', async (t) => { }) await test('binary and crc32', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { article: { diff --git a/test/boolean.perf.ts b/test/boolean.perf.ts index 06956e73ea..fb2aeddc5d 100644 --- a/test/boolean.perf.ts +++ b/test/boolean.perf.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { perf } from './shared/assert.js' await test('create 1m booleans', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { boolean: 'boolean', diff --git a/test/cardinality.ts b/test/cardinality.ts index 8cc5c10bfa..2a15ae245d 100644 --- a/test/cardinality.ts +++ b/test/cardinality.ts @@ -1,17 +1,11 @@ -import { BasedDb, xxHash64 } from '../src/index.js' +import { xxHash64 } from '../src/index.js' import { ENCODER } from '../src/utils/uint8.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual } from './shared/assert.js' -import { getTypeDefs } from '../src/db-client/modify/index.js' await test('hll', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { derp: 'number', @@ -328,13 +322,7 @@ await test('hll', async (t) => { }) await test('switches', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { store: { name: 'string', @@ -386,13 +374,7 @@ await test('switches', async (t) => { }) await test('defaultPrecision', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { store: { name: 'string', diff --git a/test/errors.ts b/test/errors.ts index 7d0cf9802a..3ace96ff7b 100644 --- a/test/errors.ts +++ b/test/errors.ts @@ -1,16 +1,9 @@ -import { BasedDb } from '../src/index.js' import { equal, throws } from './shared/assert.js' +import { testDb } from './shared/index.js' import test from './shared/test.js' await test('handle errors - references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { friends: { @@ -36,14 +29,7 @@ await test('handle errors - references', async (t) => { }) await test('handle errors - single ref', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { friend: { @@ -67,14 +53,7 @@ await test('handle errors - single ref', async (t) => { }) await test('handle errors - non existent id', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { name: 'string', diff --git a/test/filter/string.ts b/test/filter/string.ts index 8be4bb7785..f83f751606 100644 --- a/test/filter/string.ts +++ b/test/filter/string.ts @@ -1,6 +1,6 @@ -import { BasedDb } from '../../src/index.js' import { ENCODER } from '../../src/utils/uint8.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { equal, deepEqual } from '../shared/assert.js' import { italy, sentence, readBible } from '../shared/examples.js' import { decompress } from '../../src/protocol/index.js' @@ -29,14 +29,7 @@ const capitals = 'AAAAAAAAAA AAAAAAAAAAA AAAAAAAAAAAAAAAAAAAA AAA A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' await test('variable size (string/binary)', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -188,12 +181,7 @@ await test('variable size (string/binary)', async (t) => { }) await test('has compressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -227,15 +215,7 @@ await test('has compressed', async (t) => { }) await test('has uncompressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -354,12 +334,7 @@ await test('has uncompressed', async (t) => { }) await test('main has (string/binary)', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -393,13 +368,7 @@ await test('main has (string/binary)', async (t) => { }) await test('has normalized uncompressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -431,13 +400,7 @@ await test('has normalized uncompressed', async (t) => { }) await test('has normalized compressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -467,15 +430,7 @@ await test('has normalized compressed', async (t) => { }) await test('has OR uncompressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -540,15 +495,7 @@ await test('has OR uncompressed', async (t) => { }) await test('has OR compressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -584,15 +531,7 @@ await test('has OR compressed', async (t) => { }) await test('OR equal', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -626,15 +565,7 @@ await test('OR equal', async (t) => { }) await test('OR equal main', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { italy: { props: { @@ -663,12 +594,7 @@ await test('OR equal main', async (t) => { }) await test('includes and neq', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const db = await testDb(t, { types: { ent: { props: { @@ -722,12 +648,7 @@ await test('includes and neq', async (t) => { }) await test('empty string', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -736,20 +657,20 @@ await test('empty string', async (t) => { }, }, }) - const user1 = db.create('user', { + await db.create('user', { potato: 'cool', }) - const user2 = db.create('user', {}) - const user3 = db.create('user', { potato: '' }) + const user2 = await db.create('user', {}) + const user3 = await db.create('user', { potato: '' }) deepEqual( await db.query2('user').filter('potato', '=', '').get(), [ { - id: 2, + id: user2, potato: '', }, { - id: 3, + id: user3, potato: '', }, ], @@ -758,12 +679,7 @@ await test('empty string', async (t) => { }) await test('empty string fixed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -774,17 +690,17 @@ await test('empty string fixed', async (t) => { }, }, }) - const user1 = db.create('user', { + const user1 = await db.create('user', { potato: 'cool', region: 'AA', }) - const user2 = db.create('user', { region: 'XX', city: 'Amsterdam' }) - const user3 = db.create('user', { potato: 'flap', city: 'Rome' }) + const user2 = await db.create('user', { region: 'XX', city: 'Amsterdam' }) + const user3 = await db.create('user', { potato: 'flap', city: 'Rome' }) deepEqual( await db.query2('user').filter('potato', '!=', '').get(), [ - { id: 1, region: 'AA', potato: 'cool', city: '' }, - { id: 3, region: '', potato: 'flap', city: 'Rome' }, + { id: user1, region: 'AA', potato: 'cool', city: '' }, + { id: user3, region: '', potato: 'flap', city: 'Rome' }, ], '!Empty string filter', ) @@ -799,7 +715,7 @@ await test('empty string fixed', async (t) => { 'Empty string filter + region', ) - const user4 = db.create('user', { region: 'YY', city: 'Denver' }) + db.create('user', { region: 'YY', city: 'Denver' }) deepEqual( await db .query2('user') @@ -807,7 +723,7 @@ await test('empty string fixed', async (t) => { .filter('region', '!=', '') .filter('city', '=', 'Amsterdam') .get(), - [{ id: 2, region: 'XX', potato: '', city: 'Amsterdam' }], + [{ id: user2, region: 'XX', potato: '', city: 'Amsterdam' }], 'Empty string filter + region + city', ) }) diff --git a/test/references/references.ts b/test/references/references.ts index f412bdbb5a..bd4ab80ea5 100644 --- a/test/references/references.ts +++ b/test/references/references.ts @@ -2,15 +2,10 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' import { wait } from '../../src/utils/index.js' +import {testDb} from '../shared/index.js' await test('references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - t.after(() => t.backup(db)) - await db.start({ clean: true }) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -89,13 +84,7 @@ await test('references', async (t) => { }) await test('one to many', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -191,13 +180,7 @@ await test('one to many', async (t) => { }) await test('one to many really', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -441,13 +424,7 @@ await test('one to many really', async (t) => { }) await test('update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -511,13 +488,7 @@ await test('update', async (t) => { }) await test('filter', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -643,15 +614,7 @@ await test('filter', async (t) => { }) // await test('cross reference', async (t) => { -// const db = new BasedDb({ -// path: t.tmp, -// }) - -// await db.start({ clean: true }) - -// t.after(() =>db.destroy()) - -// await db.setSchema({ +// const db = await testDb(t, { // locales: { // en: { required: true }, // fr: { required: true }, @@ -858,15 +821,7 @@ await test('single ref save and load', async (t) => { }) await test('single2many - update refs', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { product: { props: { @@ -937,13 +892,7 @@ await test('single2many - update refs', async (t) => { }) await test('reference to a non-existing node', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - t.after(() => t.backup(db)) - await db.start({ clean: true }) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/search.ts b/test/search.ts index a03a21d426..465781ba2b 100644 --- a/test/search.ts +++ b/test/search.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual, equal } from './shared/assert.js' import { italy } from './shared/examples.js' await test('like filter', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -105,13 +99,7 @@ await test('like filter', async (t) => { }) await test('compressed', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -302,13 +290,7 @@ await test('compressed', async (t) => { ) }) await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -354,13 +336,7 @@ await test('simple', async (t) => { }) await test('search ids', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -419,13 +395,7 @@ await test('search ids', async (t) => { }) await test('like filter mbs', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -468,13 +438,7 @@ await test('like filter mbs', async (t) => { }) await test('giraffe first', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { @@ -505,13 +469,7 @@ await test('giraffe first', async (t) => { }) await test('first letter', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { diff --git a/test/simpleQuery.ts b/test/simpleQuery.ts index 3d424027a1..03b7e9e61f 100644 --- a/test/simpleQuery.ts +++ b/test/simpleQuery.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual } from './shared/assert.js' await test('query', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/singleRef.ts b/test/singleRef.ts index e8fc0fd46f..420b7c598b 100644 --- a/test/singleRef.ts +++ b/test/singleRef.ts @@ -2,18 +2,10 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual, equal } from './shared/assert.js' import { setTimeout } from 'timers/promises' -import { wait } from '../src/utils/index.js' +import { testDb } from './shared/index.js' await test('single special cases', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -68,9 +60,9 @@ await test('single simple', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) - await db.setSchema({ + const client = await db.setSchema({ types: { user: { props: { @@ -99,13 +91,13 @@ await test('single simple', async (t) => { }, }) - db.create('simple', { - user: db.create('user', { + client.create('simple', { + user: client.create('user', { name: 'Mr snurp', }), }) - deepEqual(await db.query2('simple').include('user.name').get(), [ + deepEqual(await client.query2('simple').include('user.name').get(), [ { id: 1, user: { @@ -115,56 +107,50 @@ await test('single simple', async (t) => { }, ]) - db.update('simple', 1, { + client.update('simple', 1, { user: null, }) - deepEqual(await db.query2('simple').include('user.name').get(), [ + deepEqual(await client.query2('simple').include('user.name').get(), [ { id: 1, user: null, }, ]) - const x = db.create('user', { + const x = client.create('user', { name: 'Mr snurp', }) - const blax = await db.create('simple', { + const blax = await client.create('simple', { user: x, }) const ids: any[] = [] for (let i = 0; i < 1e5; i++) { - ids.push(db.create('simple', {})) + ids.push(client.create('simple', {})) } - const bla2 = await db.create('simple', { + await client.create('simple', { user: x, mySelf: blax, }) - await db.isModified() + await client.isModified() for (let i = 0; i < 1e5; i++) { - ids.push(db.delete('simple', ids[i])) + ids.push(client.delete('simple', ids[i])) } await db.save() - db.update('simple', blax, { + client.update('simple', blax, { mySelf: null, }) }) await test('simple nested', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -256,13 +242,7 @@ await test('simple nested', async (t) => { }) await test('single reference object', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -334,13 +314,7 @@ await test('single reference object', async (t) => { }) await test('nested', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -716,16 +690,10 @@ await test('single reference multi refs strings', async (t) => { }) await test('update same value', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { - en: { required: true }, - fr: { required: true }, + en: { }, + fr: { }, }, types: { country: { @@ -746,15 +714,11 @@ await test('update same value', async (t) => { name: 'Country X', }) - await db.update('contestant', { - id, + await db.update('contestant', id, { country: countryId, }) - await db.update('contestant', { - id, + await db.update('contestant', id, { country: countryId, }) - - await wait(1e3) }) diff --git a/test/sort/sort.ts b/test/sort/sort.ts index 2b8df5f827..71a116bca4 100644 --- a/test/sort/sort.ts +++ b/test/sort/sort.ts @@ -1,15 +1,10 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual, equal } from '../shared/assert.js' await test('basic', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -506,13 +501,7 @@ await test('sort - from start (1M items)', async (t) => { }) await test('unset value on create', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { dialog: { props: { @@ -526,19 +515,19 @@ await test('unset value on create', async (t) => { await db.query2('dialog').sort('fun', 'desc').get() - const id1 = await db.create('dialog', { + await db.create('dialog', { fun: '1', }) - const id2 = await db.create('dialog', { + await db.create('dialog', { fun: '2', }) - const id3 = await db.create('dialog', { + await db.create('dialog', { fun: '3', }) - const id4 = await db.create('dialog', {}) + await db.create('dialog', {}) const id5 = await db.create('dialog', {}) diff --git a/test/sort/sortBinary.ts b/test/sort/sortBinary.ts index 9badc74a3e..b77cbfd95c 100644 --- a/test/sort/sortBinary.ts +++ b/test/sort/sortBinary.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test('binary sort', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { binary: { props: { diff --git a/test/sort/sortString.ts b/test/sort/sortString.ts index 85a228f4d5..e2d5247636 100644 --- a/test/sort/sortString.ts +++ b/test/sort/sortString.ts @@ -1,5 +1,6 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual, equal, isSorted } from '../shared/assert.js' import { text } from '../shared/examples.js' import { randomString } from '../../src/utils/index.js' @@ -124,10 +125,7 @@ await test('compression / large strings', async (t) => { }) await test('fixed len strings', async (t) => { - const db = new BasedDb({ path: t.tmp }) - t.after(() => db.destroy()) - await db.start({ clean: true }) - await db.setSchema({ + const db = await testDb(t, { types: { article: { props: { diff --git a/test/string.perf.ts b/test/string.perf.ts index c9164e6f9b..137f93356d 100644 --- a/test/string.perf.ts +++ b/test/string.perf.ts @@ -1,15 +1,10 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { perf } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('create 1m 2char strings', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { string: 'string', @@ -29,13 +24,7 @@ await test('create 1m 2char strings', async (t) => { }) await test('create 1m 1000char strings', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { string: 'string', diff --git a/test/string.ts b/test/string.ts index 7915154dda..91cd9cfe89 100644 --- a/test/string.ts +++ b/test/string.ts @@ -1,18 +1,11 @@ import { ENCODER, fastPrng } from '../src/utils/index.js' -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual, equal } from './shared/assert.js' import { euobserver } from './shared/examples.js' await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e4, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -163,13 +156,7 @@ await test('simple', async (t) => { }) await test('string + refs', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -315,13 +302,7 @@ await test('string + refs', async (t) => { }) await test('Big string disable compression', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { file: { props: { @@ -396,13 +377,7 @@ await test('Big string disable compression', async (t) => { }) await test('Big string', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { file: { props: { @@ -451,13 +426,7 @@ await test('Big string', async (t) => { }) await test('schema compression prop', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { file: { props: { @@ -498,16 +467,7 @@ await test('schema compression prop', async (t) => { await test('string compression - max buf size', async (t) => { const contents = 'a'.repeat(201) // min size for compression - const db = new BasedDb({ - maxModifySize: Buffer.byteLength(contents) * 2 + 100, - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { file: { props: { diff --git a/test/text/textFilter.ts b/test/text/textFilter.ts index 196d36627a..30804ed5b6 100644 --- a/test/text/textFilter.ts +++ b/test/text/textFilter.ts @@ -1,5 +1,6 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { join } from 'path' import { deepEqual } from '../shared/assert.js' @@ -16,7 +17,7 @@ await test('textFilter', async (t) => { }) await dbX.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) t.after(() => dbX.destroy()) await db.setSchema({ @@ -117,14 +118,7 @@ await test('textFilter', async (t) => { }) await test('compressionFilter', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e3 * 1e3, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { function: { name: 'alias', From a88e9208acc74767b3c9aabbf555390c55c6b24b Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 11:33:36 +0100 Subject: [PATCH 429/449] Use the new testDb --- test/singleRef.ts | 8 +------- test/singleRefQuery.ts | 10 ++-------- test/sort/sortHll.ts | 11 +++-------- test/sort/sortIds.ts | 18 +++--------------- test/sort/sortNumber.ts | 11 ++--------- test/text/textFallback.ts | 12 ++---------- 6 files changed, 13 insertions(+), 57 deletions(-) diff --git a/test/singleRef.ts b/test/singleRef.ts index 420b7c598b..f329d2bd34 100644 --- a/test/singleRef.ts +++ b/test/singleRef.ts @@ -594,13 +594,7 @@ await test('nested', async (t) => { }) await test('single reference multi refs strings', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/singleRefQuery.ts b/test/singleRefQuery.ts index ad96e103cd..616ce692eb 100644 --- a/test/singleRefQuery.ts +++ b/test/singleRefQuery.ts @@ -1,15 +1,9 @@ import { deepEqual } from './shared/assert.js' -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' await test('single reference query', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/sort/sortHll.ts b/test/sort/sortHll.ts index 4ecc4e7a20..0026081b00 100644 --- a/test/sort/sortHll.ts +++ b/test/sort/sortHll.ts @@ -1,16 +1,11 @@ -import { BasedDb, xxHash64 } from '../../src/index.js' +import { xxHash64 } from '../../src/index.js' import { ENCODER } from '../../src/utils/uint8.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' import { deepEqual, equal } from 'node:assert' await test('sortCardinality', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { article: { derp: 'number', diff --git a/test/sort/sortIds.ts b/test/sort/sortIds.ts index ee5febdd3c..8aac82e22d 100644 --- a/test/sort/sortIds.ts +++ b/test/sort/sortIds.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { isSorted } from '../shared/assert.js' await test('ids', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -51,13 +45,7 @@ await test('ids', async (t) => { }) await test('references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/sort/sortNumber.ts b/test/sort/sortNumber.ts index 968a84cb33..0ca6f93de0 100644 --- a/test/sort/sortNumber.ts +++ b/test/sort/sortNumber.ts @@ -1,17 +1,10 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual, isSorted } from '../shared/assert.js' await test('numbers', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const animals = ['pony', 'whale', 'dolphin', 'dog'] - - await db.setSchema({ + const db = await testDb(t, { types: { example: { props: { diff --git a/test/text/textFallback.ts b/test/text/textFallback.ts index 3c53a8b4ae..78f2bbde07 100644 --- a/test/text/textFallback.ts +++ b/test/text/textFallback.ts @@ -1,16 +1,8 @@ -import { BasedDb } from '../../src/index.js' -import { deepEqual } from '../shared/index.js' +import { deepEqual, testDb } from '../shared/index.js' import test from '../shared/test.js' await test('textFallback', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e6, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: true, nl: { fallback: ['en'] }, From 0d0e3666b3555a9910d4c36c8d96a1b680b801fe Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 11:34:12 +0100 Subject: [PATCH 430/449] Remove mixed test --- test/mixed.ts | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 test/mixed.ts diff --git a/test/mixed.ts b/test/mixed.ts deleted file mode 100644 index c816c4b90d..0000000000 --- a/test/mixed.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { setTimeout } from 'timers/promises' -import { BasedDb } from '../src/index.js' -import test from './shared/test.js' - -await test.skip('mixed', async (t) => { - try { - // const populate = await import('./shared/tmp/populate/index.js') - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - // await populate.default(db) - await setTimeout(1e3) - await db.update('phase', 1, { - scenarios: { - add: [ - { - id: 1, - $sequence: 1, - }, - ], - }, - }) - await setTimeout(1e3) - } catch (e) { - console.info('skipping mixed test') - } -}) From a77065613b2fc647eeea374dcbb5354849f3ca13 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 12:16:23 +0100 Subject: [PATCH 431/449] Use the new testDb --- test/bigNode.perf.ts | 10 +- test/concurrency.perf.ts | 11 +- test/copy.ts | 2 +- test/crc32c.ts | 10 +- test/db-schema/schemaProblems.ts | 2 +- test/db-schema/schemaProblemsModify.perf.ts | 2 +- test/delete.perf.ts | 2 +- test/expire.ts | 4 +- test/filter/api.ts | 10 +- test/filter/filter.ts | 54 ++----- test/filter/or.ts | 10 +- test/filter/references.ts | 9 +- test/filter/referencesField.ts | 10 +- test/flush.ts | 11 +- test/hooks.ts | 133 ++++-------------- test/idOffset.ts | 11 +- test/include/include.ts | 18 +-- test/include/includeMeta.ts | 10 +- test/include/includeNested.ts | 10 +- test/include/includeSlice.ts | 10 +- test/include/referencesField.ts | 10 +- test/include/thread.perf.ts | 9 +- test/isModified.perf.ts | 10 +- test/json.ts | 21 +-- test/locales.ts | 10 +- test/mem.ts | 13 +- test/migration.ts | 2 +- test/number.perf.ts | 10 +- test/queryResponse.ts | 10 +- test/range.ts | 20 +-- test/raw.ts | 19 +-- test/references/create.perf.ts | 6 +- test/references/reference.perf.ts | 10 +- test/references/referencesIndex.ts | 21 +-- test/references/referencesModify.ts | 36 +---- test/save/save.ts | 95 +++++++------ test/save/saveEdge.ts | 2 +- test/save/saveRange.ts | 10 +- test/scenarios/e-commerce.ts | 2 +- test/scenarios/nycTaxi.ts | 2 +- test/scenarios/vote.ts | 2 +- test/scenarios/voteLargeAmounts.perf.ts | 2 +- test/scenarios/voteStorage.ts | 4 +- test/serializeQueryDef.ts | 10 +- test/singleRef.ts | 4 +- test/sort/sort.perf.ts | 12 +- test/sort/sortById.ts | 18 +-- test/sort/sortEnum.ts | 15 +- test/sort/sortNodeId.ts | 10 +- test/sort/sortTimestamp.ts | 14 +- test/subscription/subscription.perf.ts | 4 +- test/subscription/subscriptionWorkers.perf.ts | 2 +- test/validation/validation.ts | 59 ++------ test/validation/validationAdvanced.ts | 53 +------ test/validation/validationCustom.ts | 12 +- test/validation/validationReferences.ts | 12 +- 56 files changed, 215 insertions(+), 675 deletions(-) diff --git a/test/bigNode.perf.ts b/test/bigNode.perf.ts index 8493a9a407..62acefb4e1 100644 --- a/test/bigNode.perf.ts +++ b/test/bigNode.perf.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' import { SchemaProps, serialize } from '../src/schema/index.js' import { deSerializeSchema, resultToObject } from '../src/protocol/index.js' +import { testDb } from './shared/index.js' await test('big nodes', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const makeALot = (n: number) => { const props: SchemaProps = {} for (let i = 0; i < n; i++) { @@ -19,7 +13,7 @@ await test('big nodes', async (t) => { return props } - await db.setSchema({ + const db = await testDb(t, { types: { mega: { props: { diff --git a/test/concurrency.perf.ts b/test/concurrency.perf.ts index 5bf5e0e21d..b3d16f09ba 100644 --- a/test/concurrency.perf.ts +++ b/test/concurrency.perf.ts @@ -2,15 +2,10 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { italy } from './shared/examples.js' import { setTimeout as setTimeoutAsync } from 'timers/promises' +import { testDb } from './shared/index.js' await test('concurrency', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -88,7 +83,7 @@ await test('many instances', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) return db }), ) diff --git a/test/copy.ts b/test/copy.ts index 185706a711..0c5412c6fc 100644 --- a/test/copy.ts +++ b/test/copy.ts @@ -7,7 +7,7 @@ await test('copy', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { diff --git a/test/crc32c.ts b/test/crc32c.ts index 002f5a366e..863b6e5378 100644 --- a/test/crc32c.ts +++ b/test/crc32c.ts @@ -1,10 +1,10 @@ -import { BasedDb } from '../src/index.js' import { ENCODER } from '../src/utils/uint8.js' import test from './shared/test.js' import { equal } from './shared/assert.js' import { crc32 as nativeCrc32 } from '../src/index.js' import crc32c from '../src/hash/crc32c.js' import { LangCode } from '../src/zigTsExports.js' +import { testDb } from './shared/index.js' await test('Comparing hash generation collision', async (t) => { let crc32set = new Set() @@ -89,13 +89,7 @@ await test('Comparing hash generation collision', async (t) => { }) await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { transaction: { props: { diff --git a/test/db-schema/schemaProblems.ts b/test/db-schema/schemaProblems.ts index c343c54e02..f597e04751 100644 --- a/test/db-schema/schemaProblems.ts +++ b/test/db-schema/schemaProblems.ts @@ -11,7 +11,7 @@ await test('schema problems', async (t) => { await db.start({ clean: true }) t.after(() => { - return t.backup(db) + return t.backup(db.server) }) const int = setInterval(async () => { diff --git a/test/db-schema/schemaProblemsModify.perf.ts b/test/db-schema/schemaProblemsModify.perf.ts index eff9f52ab4..95f953adae 100644 --- a/test/db-schema/schemaProblemsModify.perf.ts +++ b/test/db-schema/schemaProblemsModify.perf.ts @@ -13,7 +13,7 @@ await test('schema problems modify', async (t) => { t.after(() => { clearInterval(int) - return t.backup(db) + return t.backup(db.server) }) await db.start({ clean: true }) diff --git a/test/delete.perf.ts b/test/delete.perf.ts index 6f43d14785..4474e5804e 100644 --- a/test/delete.perf.ts +++ b/test/delete.perf.ts @@ -8,7 +8,7 @@ await test('delete performance', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { diff --git a/test/expire.ts b/test/expire.ts index 010ca0715e..da34d931a4 100644 --- a/test/expire.ts +++ b/test/expire.ts @@ -9,7 +9,7 @@ await test('expire', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const schema = { types: { @@ -74,7 +74,7 @@ await test('refresh', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { diff --git a/test/filter/api.ts b/test/filter/api.ts index 871d2d199f..7d5b6d90cf 100644 --- a/test/filter/api.ts +++ b/test/filter/api.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test('filter api: object', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { bestFriend: { diff --git a/test/filter/filter.ts b/test/filter/filter.ts index 90aedee064..139bc027e3 100644 --- a/test/filter/filter.ts +++ b/test/filter/filter.ts @@ -1,17 +1,11 @@ import { BasedDb } from '../../src/index.js' -import test from '../shared/test.js' import { equal, deepEqual } from '../shared/assert.js' +import test from '../shared/test.js' +import { testDb } from '../shared/index.js' await test('single', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const status = ['error', 'danger', 'ok', '🦄'] - - await db.setSchema({ + const db = await testDb(t, { types: { org: { props: { @@ -96,15 +90,8 @@ await test('single', async (t) => { }) await test('simple', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const status = ['error', 'danger', 'ok', '🦄'] - - await db.setSchema({ + const db = await testDb(t, { types: { org: { props: { @@ -564,15 +551,8 @@ await test('simple', async (t) => { }) await test('or', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - const status = ['error', 'danger', 'ok', '🦄'] - - await db.setSchema({ + const db = await testDb(t, { types: { machine: { props: { @@ -700,13 +680,7 @@ await test('or', async (t) => { }) await test('or numerical', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { machine: { props: { @@ -797,13 +771,7 @@ await test('or numerical', async (t) => { }) await test.skip('includes', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -875,13 +843,7 @@ await test.skip('includes', async (t) => { }) await test('lt x leq', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { bucket: { red: 'uint8', diff --git a/test/filter/or.ts b/test/filter/or.ts index 202688f6ba..2e628fa584 100644 --- a/test/filter/or.ts +++ b/test/filter/or.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test('filter or', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/filter/references.ts b/test/filter/references.ts index 7882b514ec..9ebe982b7d 100644 --- a/test/filter/references.ts +++ b/test/filter/references.ts @@ -1,15 +1,10 @@ import test from '../shared/test.js' import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' +import {testDb} from '../shared/index.js' await test('filter references drones', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db= await testDb(t, { types: { workspace: { props: { diff --git a/test/filter/referencesField.ts b/test/filter/referencesField.ts index ce9006ebbd..1cf2738d8e 100644 --- a/test/filter/referencesField.ts +++ b/test/filter/referencesField.ts @@ -1,15 +1,9 @@ import test from '../shared/test.js' -import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' +import {testDb} from '../shared/index.js' await test('filter references shortcut', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db= await testDb(t, { types: { user: { props: { diff --git a/test/flush.ts b/test/flush.ts index f605a53487..3322e7d1d9 100644 --- a/test/flush.ts +++ b/test/flush.ts @@ -1,15 +1,8 @@ -import { BasedDb } from '../src/index.js' +import { testDb } from './shared/index.js' import test from './shared/test.js' await test('too large payload should throw, correct size should not', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 256, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { user: { props: { diff --git a/test/hooks.ts b/test/hooks.ts index 81692ef3b2..fbeb44cc07 100644 --- a/test/hooks.ts +++ b/test/hooks.ts @@ -1,18 +1,11 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual, equal, notEqual, throws } from './shared/assert.js' import { wait } from '../src/utils/index.js' await test('hooks - undefined values', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -120,15 +113,7 @@ await test('hooks - undefined values', async (t) => { }) await test('hooks - private nodes', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -198,15 +183,7 @@ await test('hooks - private nodes', async (t) => { }) await test('hooks - as SQL CHECK constraints', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -237,15 +214,7 @@ await test('hooks - as SQL CHECK constraints', async (t) => { }) await test('property modify hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -309,15 +278,7 @@ await test('property modify hooks', async (t) => { }) await test('property read hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -368,15 +329,7 @@ await test('property read hooks', async (t) => { }) await test('aggregate hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -418,15 +371,7 @@ await test('aggregate hooks', async (t) => { }) await test('search hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -468,15 +413,7 @@ await test('search hooks', async (t) => { }) await test('groupBy hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -520,15 +457,7 @@ await test('groupBy hooks', async (t) => { }) await test('filter hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb({ types: { user: { hooks: { @@ -576,15 +505,7 @@ await test('filter hooks', async (t) => { }) await test('include hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -628,15 +549,7 @@ await test('include hooks', async (t) => { }) await test('upsert calls create and/or update hooks', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { hooks: { @@ -657,10 +570,13 @@ await test('upsert calls create and/or update hooks', async (t) => { }, }) - await db.upsert('user', { - name: 'youzi', - age: 21, - }) + await db.upsert( + 'user', + { name: 'youzi' }, + { + age: 21, + }, + ) const results1 = await db.query2('user').get() @@ -670,10 +586,13 @@ await test('upsert calls create and/or update hooks', async (t) => { equal(results1[0].updatedString != 0, true) await wait(1) - await db.upsert('user', { - name: 'youzi', - age: 45, - }) + await db.upsert( + 'user', + { name: 'youzi' }, + { + age: 45, + }, + ) const results2 = await db.query2('user').get() equal(results2.length, 1) diff --git a/test/idOffset.ts b/test/idOffset.ts index 74b6e90731..d377420d22 100644 --- a/test/idOffset.ts +++ b/test/idOffset.ts @@ -1,15 +1,8 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' await test('idOffset', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 100, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/include/include.ts b/test/include/include.ts index 45f177eaaa..9c2e0cdd7f 100644 --- a/test/include/include.ts +++ b/test/include/include.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual, equal } from '../shared/assert.js' await test('include ', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -51,13 +45,7 @@ await test('include ', async (t) => { }) await test('main', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/include/includeMeta.ts b/test/include/includeMeta.ts index 80dad62563..9bcd71fafb 100644 --- a/test/include/includeMeta.ts +++ b/test/include/includeMeta.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' import { italy } from '../shared/examples.js' await test('meta for selva string', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, diff --git a/test/include/includeNested.ts b/test/include/includeNested.ts index 32238223e5..7b0654fa2e 100644 --- a/test/include/includeNested.ts +++ b/test/include/includeNested.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test('include */**', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/include/includeSlice.ts b/test/include/includeSlice.ts index 49b48b5652..d93f21c29b 100644 --- a/test/include/includeSlice.ts +++ b/test/include/includeSlice.ts @@ -1,16 +1,10 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { italy } from '../shared/examples.js' import { deepEqual, equal } from '../shared/assert.js' await test('slice string / text', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, diff --git a/test/include/referencesField.ts b/test/include/referencesField.ts index 2ae03d48bc..87dd1445b4 100644 --- a/test/include/referencesField.ts +++ b/test/include/referencesField.ts @@ -1,15 +1,9 @@ import test from '../shared/test.js' -import { BasedDb } from '../../src/index.js' +import {testDb} from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test('references shortcut', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/include/thread.perf.ts b/test/include/thread.perf.ts index 67f97fef89..1e0f9e33e4 100644 --- a/test/include/thread.perf.ts +++ b/test/include/thread.perf.ts @@ -13,7 +13,6 @@ await test('include', async (t) => { console.log('STOP SERVER') await db.stop(true) }) - //t.after(() => t.backup(db)) t.after(() => db.stop(true)) // single ref + edge @@ -225,9 +224,9 @@ await test('include', async (t) => { or: { props: { x: 10}} } } - + } - + { filter: { props: {nr" '>' 10001} @@ -235,7 +234,7 @@ await test('include', async (t) => { props: { nr: [10,20]} } } - + } { @@ -246,7 +245,7 @@ await test('include', async (t) => { } } } - + } */ diff --git a/test/isModified.perf.ts b/test/isModified.perf.ts index d837af166a..ae2353e25d 100644 --- a/test/isModified.perf.ts +++ b/test/isModified.perf.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { deepEqual } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('isModified', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/json.ts b/test/json.ts index afda9746e8..f88b2455fa 100644 --- a/test/json.ts +++ b/test/json.ts @@ -1,16 +1,10 @@ import { notEqual } from 'node:assert' -import { BasedDb } from '../src/index.js' import { deepEqual } from './shared/assert.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' await test('json', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { jsonDerulo: { name: 'string', @@ -56,8 +50,7 @@ await test('json', async (t) => { 'after empty object', ) - await db.update('jsonDerulo', { - id: jay, + await db.update('jsonDerulo', jay, { myJson: null, }) @@ -72,13 +65,7 @@ await test('json', async (t) => { }) await test('json and crc32', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { article: { diff --git a/test/locales.ts b/test/locales.ts index f7011fec36..dd52b96f1f 100644 --- a/test/locales.ts +++ b/test/locales.ts @@ -1,5 +1,5 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { LangCode } from '../src/zigTsExports.js' const langs = [...Object.keys(LangCode)].filter((val) => val !== 'none') @@ -8,13 +8,7 @@ const locales = Object.fromEntries( ) await test('locales', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { locales, types: { thing: { diff --git a/test/mem.ts b/test/mem.ts index b7130856fb..3a07ce2181 100644 --- a/test/mem.ts +++ b/test/mem.ts @@ -1,18 +1,10 @@ import { fastPrng } from '../src/utils/index.js' -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { equal } from './shared/assert.js' await test('mem', async (t) => { - const db = new BasedDb({ - path: t.tmp, - // low amount to force many flushes - maxModifySize: 10000, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - const client = await db.setSchema({ + const client = await testDb(t, { types: { data: { props: { @@ -27,7 +19,6 @@ await test('mem', async (t) => { const amount = 1e3 const repeat = 1e3 - // 2M inserts rmeoves const rnd = fastPrng() for (let j = 0; j < repeat; j++) { diff --git a/test/migration.ts b/test/migration.ts index 7fb8b20f94..729bbae438 100644 --- a/test/migration.ts +++ b/test/migration.ts @@ -10,7 +10,7 @@ await test('migration', async (t) => { await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ version: '1.0.0', diff --git a/test/number.perf.ts b/test/number.perf.ts index 1451359166..64944f67c8 100644 --- a/test/number.perf.ts +++ b/test/number.perf.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' import { perf } from './shared/assert.js' +import { testDb } from './shared/index.js' await test('create 1m uint32', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { uint32: 'uint32', diff --git a/test/queryResponse.ts b/test/queryResponse.ts index c3a3c1e72b..d295386265 100644 --- a/test/queryResponse.ts +++ b/test/queryResponse.ts @@ -1,17 +1,11 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { equal } from './shared/assert.js' import { notEqual } from 'assert' import { extractNumber } from '../src/utils/index.js' await test('correct version', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/range.ts b/test/range.ts index 7da4b1ce63..1c81a6d6bf 100644 --- a/test/range.ts +++ b/test/range.ts @@ -1,16 +1,10 @@ import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual, equal } from './shared/assert.js' await test('range', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - // schema - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -72,15 +66,7 @@ await test('range', async (t) => { }) await test('default range: 1000', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - // schema - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/raw.ts b/test/raw.ts index 0945b6716a..8ef867e275 100644 --- a/test/raw.ts +++ b/test/raw.ts @@ -1,17 +1,11 @@ import { BasedDb } from '../src/index.js' import { deepEqual } from './shared/assert.js' import { italy } from './shared/examples.js' +import { testDb } from './shared/index.js' import test from './shared/test.js' await test.skip('cardinality', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { uniqueSkills: 'cardinality', @@ -36,14 +30,7 @@ await test.skip('cardinality', async (t) => { }) await test('string', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { name: 'string', diff --git a/test/references/create.perf.ts b/test/references/create.perf.ts index 9b274da2c0..2e09445b1e 100644 --- a/test/references/create.perf.ts +++ b/test/references/create.perf.ts @@ -1,13 +1,13 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { deepEqual, equal, throws, perf } from '../shared/assert.js' +import { equal, perf } from '../shared/assert.js' await test('create 1m items with 1 reference(s)', async (t) => { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { @@ -41,7 +41,7 @@ await test('create 1m items with 100 reference(s)', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { diff --git a/test/references/reference.perf.ts b/test/references/reference.perf.ts index df84e4ca06..ad7f427104 100644 --- a/test/references/reference.perf.ts +++ b/test/references/reference.perf.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { perf } from '../shared/assert.js' +import {testDb} from '../shared/index.js' await test('create 1m single refs', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { ref: { diff --git a/test/references/referencesIndex.ts b/test/references/referencesIndex.ts index 1bd82db78c..155b29d0b2 100644 --- a/test/references/referencesIndex.ts +++ b/test/references/referencesIndex.ts @@ -1,15 +1,10 @@ import { BasedDb } from '../../src/index.js' -import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' +import test from '../shared/test.js' +import { testDb } from '../shared/index.js' await test('references modify', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -33,7 +28,7 @@ await test('references modify', async (t) => { name: 'marie', }) - const john = db.create('user', { + const john = await db.create('user', { name: 'john', friends: [bob, marie], }) @@ -180,13 +175,7 @@ await test('references modify', async (t) => { }) await test('index>len', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/references/referencesModify.ts b/test/references/referencesModify.ts index 907dda0cf2..1955c6da48 100644 --- a/test/references/referencesModify.ts +++ b/test/references/referencesModify.ts @@ -1,15 +1,10 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test('references modify', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db= await testDb(t, { types: { user: { props: { @@ -101,13 +96,7 @@ await test('references modify', async (t) => { }) await test('references modify 2', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { a: { name: 'string', @@ -148,13 +137,7 @@ await test('references modify 2', async (t) => { }) await test('reference move', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { a: { name: 'string', @@ -212,14 +195,7 @@ await test('reference move', async (t) => { // https://linear.app/1ce/issue/FDN-1735 await test('try to modify undefined refs', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { movie: { name: 'string', @@ -252,7 +228,7 @@ await test('try to modify undefined refs', async (t) => { genre: 'Crime', }) const a1 = db.create('actor', { name: 'Uma Thurman', movies: [m1, m2] }) - const a2 = db.create('actor', { name: 'Jonh Travolta', movies: [m2] }) + db.create('actor', { name: 'John Travolta', movies: [m2] }) //await db.query2('movie').include('*', '**').get().inspect() diff --git a/test/save/save.ts b/test/save/save.ts index 3ccbc77f20..a33b692031 100644 --- a/test/save/save.ts +++ b/test/save/save.ts @@ -133,13 +133,7 @@ await test('empty root', async (t) => { }) await test('refs', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const schema = { types: { group: { props: { @@ -163,24 +157,32 @@ await test('refs', async (t) => { }, }, }, + } as const + + const db = new BasedDb({ + path: t.tmp, }) + await db.start({ clean: true }) + t.after(() => db.destroy()) + + const client = await db.setSchema(schema) - const grp = db.create('group', { + const grp = client.create('group', { name: 'best', }) - db.create('user', { + client.create('user', { name: 'youzi', email: 'youzi@yazi.yo', group: grp, }) - db.create('user', { + client.create('user', { name: 'youri', email: 'youri@yari.yo', group: grp, }) - await db.drain() + await client.drain() await db.save() const db2 = new BasedDb({ @@ -188,9 +190,12 @@ await test('refs', async (t) => { }) t.after(() => db2.destroy()) await db2.start() + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - const users1 = await db.query2('user').include('group').get() - const users2 = await db2.query2('user').include('group').get() + const users1 = await client.query2('user').include('group').get() + const users2 = await client2.query2('user').include('group').get() deepEqual(users1, users2) }) @@ -230,13 +235,7 @@ await test('auto save', async (t) => { }) await test('text', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const schema = { locales: { en: {}, fi: { fallback: ['en'] }, @@ -249,10 +248,17 @@ await test('text', async (t) => { }, }, }, + } as const + const db = new BasedDb({ + path: t.tmp, }) + await db.start({ clean: true }) + t.after(() => db.destroy()) + + const client = await db.setSchema(schema) // Text: Wikipedia CC BY-SA 4.0 - db.create('article', { + client.create('article', { title: { en: 'Galileo Galilei', fi: 'Galileo Galilei', @@ -262,7 +268,7 @@ await test('text', async (t) => { fi: 'Galileo Galilei (15. helmikuuta 1564 Pisa, Firenzen herttuakunta – 8. tammikuuta 1642 Arcetri, Toscanan suurherttuakunta) oli italialainen tähtitieteilijä, filosofi ja fyysikko. Hänen merkittävimmät saavutuksensa liittyvät tieteellisen menetelmän kehitykseen aristoteelisesta nykyiseen muotoonsa. Häntä on kutsuttu tieteen, klassisen fysiikan ja tähtitieteen isäksi.', }, }) - db.create('article', { + client.create('article', { title: { en: 'Pope Urban VIII', fi: 'Urbanus VIII', @@ -273,7 +279,7 @@ await test('text', async (t) => { }, }) - await db.drain() + await client.drain() await db.save() const db2 = new BasedDb({ @@ -281,9 +287,12 @@ await test('text', async (t) => { }) t.after(() => db2.destroy()) await db2.start() + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - const articles1 = await db.query2('article').get() - const articles2 = await db2.query2('article').get() + const articles1 = await client.query2('article').get() + const articles2 = await client2.query2('article').get() deepEqual(articles1, articles2) }) @@ -360,12 +369,7 @@ await test.skip('db is drained before save', async (t) => { }) await test('create', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - await db.setSchema({ + const schema = { types: { person: { props: { @@ -374,31 +378,37 @@ await test('create', async (t) => { }, }, }, + } as const + const db = new BasedDb({ + path: t.tmp, }) + await db.start({ clean: true }) + const client = await db.setSchema(schema) + t.after(() => db.destroy()) - db.create('person', { + client.create('person', { name: 'Joe', }) - await db.drain() + await client.drain() await db.save() - db.create('person', { + client.create('person', { name: 'John', }) - await db.drain() + await client.drain() await db.save() - db.create('person', { + client.create('person', { name: 'Neo', alias: 'haxor', }) - await db.drain() + await client.drain() await db.save() - db.create('person', { + client.create('person', { name: 'trinity', alias: 'haxor', }) - await db.drain() + await client.drain() await db.save() // load the same db into a new instance @@ -407,8 +417,11 @@ await test('create', async (t) => { }) await db2.start() t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) - deepEqual(await db2.query2('person').get(), await db.query2('person').get()) + deepEqual(await client2.query2('person').get(), await client.query2('person').get()) }) await test('upsert', async (t) => { @@ -690,7 +703,7 @@ await test('edge val', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { diff --git a/test/save/saveEdge.ts b/test/save/saveEdge.ts index 3c4006a9dc..4ed16881e5 100644 --- a/test/save/saveEdge.ts +++ b/test/save/saveEdge.ts @@ -7,7 +7,7 @@ await test('save edge', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { diff --git a/test/save/saveRange.ts b/test/save/saveRange.ts index 7e60564bb9..ab75fdcfa5 100644 --- a/test/save/saveRange.ts +++ b/test/save/saveRange.ts @@ -164,7 +164,7 @@ await test('reference changes', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { @@ -253,7 +253,7 @@ await test('ref block moves', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { @@ -296,7 +296,7 @@ await test('ref removal', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { @@ -332,7 +332,7 @@ await test('refs removal with delete', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { @@ -364,7 +364,7 @@ await test('large block gap', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const client = await db.setSchema({ types: { diff --git a/test/scenarios/e-commerce.ts b/test/scenarios/e-commerce.ts index 4d7d8f06f4..771d6f1852 100644 --- a/test/scenarios/e-commerce.ts +++ b/test/scenarios/e-commerce.ts @@ -34,7 +34,7 @@ await test('E-commerce Simulation', async (t) => { clearInterval(intervalId) }) - t.after(async () => t.backup(db)) + t.after(async () => t.backup(db.server) await db.setSchema({ locales: { en: {}, de: {} }, // Add locales for text fields diff --git a/test/scenarios/nycTaxi.ts b/test/scenarios/nycTaxi.ts index 7a71a676eb..89a3f9dc0c 100644 --- a/test/scenarios/nycTaxi.ts +++ b/test/scenarios/nycTaxi.ts @@ -348,7 +348,7 @@ await test.skip('taxi', async (t) => { await db.start({ clean: true }) // FIXME - //t.after(() => t.backup(db)) + //t.after(() => t.backup(db.server)) t.after(() => db.stop()) await db.setSchema({ diff --git a/test/scenarios/vote.ts b/test/scenarios/vote.ts index 32e3a2e725..a697cbc7cd 100644 --- a/test/scenarios/vote.ts +++ b/test/scenarios/vote.ts @@ -13,7 +13,7 @@ await test('schema with many uint8 fields', async (t) => { // maxModifySize: 1000 * 1000 * 1000, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) // t.after(() => db.destroy()) const maxPaymentsPerHub = 10000 diff --git a/test/scenarios/voteLargeAmounts.perf.ts b/test/scenarios/voteLargeAmounts.perf.ts index 945f06362a..be783d5c19 100644 --- a/test/scenarios/voteLargeAmounts.perf.ts +++ b/test/scenarios/voteLargeAmounts.perf.ts @@ -58,7 +58,7 @@ await test('schema with many uint8 fields', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const voteCountrySchema: any = countrySchema diff --git a/test/scenarios/voteStorage.ts b/test/scenarios/voteStorage.ts index 10e33e759d..36e698d4d5 100644 --- a/test/scenarios/voteStorage.ts +++ b/test/scenarios/voteStorage.ts @@ -51,7 +51,7 @@ await test('vote including round', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) const voteCountrySchema: any = countrySchema @@ -148,7 +148,7 @@ const testVotes = (opts: { votes: any; amount: number }) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { diff --git a/test/serializeQueryDef.ts b/test/serializeQueryDef.ts index 327eb5fedc..cb90f0a80b 100644 --- a/test/serializeQueryDef.ts +++ b/test/serializeQueryDef.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../src/index.js' import test from './shared/test.js' +import { testDb } from './shared/index.js' import { deepEqual } from './shared/assert.js' await test('serialize', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/singleRef.ts b/test/singleRef.ts index f329d2bd34..587604dc77 100644 --- a/test/singleRef.ts +++ b/test/singleRef.ts @@ -686,8 +686,8 @@ await test('single reference multi refs strings', async (t) => { await test('update same value', async (t) => { const db = await testDb(t, { locales: { - en: { }, - fr: { }, + en: {}, + fr: {}, }, types: { country: { diff --git a/test/sort/sort.perf.ts b/test/sort/sort.perf.ts index f3fcbb236c..9ff74b572d 100644 --- a/test/sort/sort.perf.ts +++ b/test/sort/sort.perf.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' -import test from '../shared/test.js' import { deepEqual, equal } from '../shared/assert.js' +import test from '../shared/test.js' +import { testDb } from '../shared/index.js' await test('1M', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/sort/sortById.ts b/test/sort/sortById.ts index 4d7802e002..b6e552ad02 100644 --- a/test/sort/sortById.ts +++ b/test/sort/sortById.ts @@ -1,5 +1,5 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { isSorted } from '../shared/assert.js' const schema = { @@ -17,22 +17,8 @@ const schema = { }, } as const -// import { Schema} f - -// type Schema = typeof schema - await test('sort by id', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => { - return t.backup(db) - }) - - await db.setSchema(schema) + const db = await testDb(t, schema) for (let i = 0; i < 1e6; i++) { db.create('user', { diff --git a/test/sort/sortEnum.ts b/test/sort/sortEnum.ts index 9f234ce872..38f8399560 100644 --- a/test/sort/sortEnum.ts +++ b/test/sort/sortEnum.ts @@ -1,20 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { deepEqual, equal } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('sort Enum', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => { - return t.backup(db) - }) - const status = ['a', 'b', 'c', 'd', 'e', 'f'] - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { diff --git a/test/sort/sortNodeId.ts b/test/sort/sortNodeId.ts index 8811ca98eb..e21a1192b2 100644 --- a/test/sort/sortNodeId.ts +++ b/test/sort/sortNodeId.ts @@ -1,15 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { deepEqual } from '../shared/assert.js' await test.skip('basic sort by id', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { person: { props: { diff --git a/test/sort/sortTimestamp.ts b/test/sort/sortTimestamp.ts index f78e446147..0dc84ee8a2 100644 --- a/test/sort/sortTimestamp.ts +++ b/test/sort/sortTimestamp.ts @@ -1,13 +1,9 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' import { isSorted } from '../shared/assert.js' await test('sort timestamp', async (t) => { - const db = new BasedDb({ path: t.tmp }) - t.after(() => t.backup(db)) - await db.start({ clean: true }) - - await db.setSchema({ + const db = await testDb(t, { types: { event: { props: { @@ -128,11 +124,7 @@ await test('sort timestamp', async (t) => { }) await test('sort multicore', async (t) => { - const db = new BasedDb({ path: t.tmp }) - t.after(() => t.backup(db)) - await db.start({ clean: true }) - - await db.setSchema({ + const db = await testDb(t, { types: { event: { props: { diff --git a/test/subscription/subscription.perf.ts b/test/subscription/subscription.perf.ts index 2ae05d65fd..a5b48816db 100644 --- a/test/subscription/subscription.perf.ts +++ b/test/subscription/subscription.perf.ts @@ -12,7 +12,7 @@ await test('subscription perf', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { @@ -76,7 +76,7 @@ await test('native single id perf', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) await db.setSchema({ types: { diff --git a/test/subscription/subscriptionWorkers.perf.ts b/test/subscription/subscriptionWorkers.perf.ts index 18c5be1464..f8992275ff 100644 --- a/test/subscription/subscriptionWorkers.perf.ts +++ b/test/subscription/subscriptionWorkers.perf.ts @@ -19,7 +19,7 @@ await test('subscriptionWorkers', async (t) => { path: t.tmp, }) await db.start({ clean: true }) - t.after(() => t.backup(db)) + t.after(() => t.backup(db.server)) // TODO fix this type const voteCountrySchema: any = countrySchema diff --git a/test/validation/validation.ts b/test/validation/validation.ts index a376e376e7..ddce7a39ac 100644 --- a/test/validation/validation.ts +++ b/test/validation/validation.ts @@ -1,17 +1,10 @@ import { BasedDb } from '../../src/index.js' import { deepEqual, throws } from '../shared/assert.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, de: {} }, types: { user: { @@ -469,17 +462,8 @@ await test('update', async (t) => { }) await test('query', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - const drip = ['dope', 'cringe', 'meh'] - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: { fallback: ['en'] }, @@ -814,13 +798,13 @@ await test('query', async (t) => { }, false) }) -await test('query - no schema', async (t) => { +// TODO This is pointless with the new initialization +await test.skip('query - no schema', async (t) => { const db = new BasedDb({ path: t.tmp, }) await db.start({ clean: true }) - t.after(() => db.destroy()) setTimeout(async () => { @@ -844,15 +828,7 @@ await test('query - no schema', async (t) => { }) await test('minmax', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -879,13 +855,7 @@ await test('minmax', async (t) => { }) await test('set text without locale', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db)) // commenting this out fixes the crash part - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, it: {}, @@ -918,14 +888,7 @@ await test('set text without locale', async (t) => { }) await test('range validation', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { types: { user: { props: { @@ -1054,11 +1017,7 @@ await test('range validation', async (t) => { }) await test('binary validation', async (t) => { - const db = new BasedDb({ path: t.tmp }) - await db.start({ clean: true }) - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t , { types: { user: { props: { diff --git a/test/validation/validationAdvanced.ts b/test/validation/validationAdvanced.ts index 23c02b64c6..a8e85bd24b 100644 --- a/test/validation/validationAdvanced.ts +++ b/test/validation/validationAdvanced.ts @@ -1,18 +1,10 @@ import { convertToTimestamp } from '../../src/utils/index.js' -import { BasedDb } from '../../src/index.js' import { throws } from '../shared/assert.js' import test from '../shared/test.js' +import { testDb } from '../shared/index.js' await test('simple min / max validation', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, de: {} }, types: { user: { @@ -137,15 +129,7 @@ await test('simple min / max validation', async (t) => { }) await test('step validation', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, de: {} }, types: { user: { @@ -222,15 +206,7 @@ await test('step validation', async (t) => { }) await test('min / max validation on reference edges', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { thing: {}, edgeUser: { @@ -534,15 +510,7 @@ await test('min / max validation on reference edges', async (t) => { }) await test('step validation on reference edges', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { types: { thing: {}, edgeUser: { @@ -708,20 +676,11 @@ await test('step validation on reference edges', async (t) => { }) await test('min / max / step validation on reference edges timestamp + string format', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => db.destroy()) - const minDateStr = '01/01/2000' const minTs = convertToTimestamp(minDateStr) const maxOffsetSeconds = 10 const stepMs = 1 // 1 second - - await db.setSchema({ + const db = await testDb(t, { types: { thing: {}, edgeUser: { diff --git a/test/validation/validationCustom.ts b/test/validation/validationCustom.ts index b2aabdf1cf..e315095684 100644 --- a/test/validation/validationCustom.ts +++ b/test/validation/validationCustom.ts @@ -1,17 +1,9 @@ -import { BasedDb } from '../../src/index.js' import { throws } from '../shared/assert.js' +import {testDb} from '../shared/index.js' import test from '../shared/test.js' await test('custom', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db = await testDb(t, { locales: { en: {}, de: {} }, types: { user: { diff --git a/test/validation/validationReferences.ts b/test/validation/validationReferences.ts index 5874b030b3..1953b71b14 100644 --- a/test/validation/validationReferences.ts +++ b/test/validation/validationReferences.ts @@ -1,17 +1,9 @@ -import { BasedDb } from '../../src/index.js' import { deepEqual, throws } from '../shared/assert.js' +import {testDb} from '../shared/index.js' import test from '../shared/test.js' await test('update', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - t.after(() => t.backup(db)) - - await db.setSchema({ + const db= await testDb(t, { locales: { en: {}, de: {} }, types: { flap: { From f3e628ea9513da1fed76713fc4885e7b1e4ed762 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:36:19 +0100 Subject: [PATCH 432/449] Allow passing db path --- test/shared/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/shared/index.ts b/test/shared/index.ts index f0177b04f9..017f398ba0 100644 --- a/test/shared/index.ts +++ b/test/shared/index.ts @@ -38,7 +38,7 @@ export function logMemoryUsage() { export const testDb = async ( t: Parameters[1]>[0], schema: StrictSchema, - opts: { noBackup?: boolean, noClean?: boolean } = {}, + opts: { noBackup?: boolean, noClean?: boolean, path?: string } = {}, ): Promise>> => { const server = await testDbServer(t, opts) return testDbClient(server, schema) @@ -59,9 +59,9 @@ export const testDbClient = async ( export const testDbServer = async ( t: Parameters[1]>[0], - opts: { noBackup?: boolean, noClean?: boolean } = {}, + opts: { noBackup?: boolean, noClean?: boolean, path?: string } = {}, ): Promise => { - const db = new DbServer({ path: t.tmp }) + const db = new DbServer({ path: opts.path ?? t.tmp }) await db.start({ clean: !opts.noClean }) if (opts.noBackup) { t.after(() => db.destroy()) From 5d6213958f243cc28ac78944c9a42d5785974911 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:36:46 +0100 Subject: [PATCH 433/449] Use the new client --- test/sort/sort.ts | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/test/sort/sort.ts b/test/sort/sort.ts index 71a116bca4..d8091201fa 100644 --- a/test/sort/sort.ts +++ b/test/sort/sort.ts @@ -1,4 +1,4 @@ -import { BasedDb } from '../../src/index.js' +import { BasedDb, DbClient, getDefaultHooks } from '../../src/index.js' import test from '../shared/test.js' import { testDb } from '../shared/index.js' import { deepEqual, equal } from '../shared/assert.js' @@ -401,15 +401,7 @@ await test('basic', async (t) => { }) await test('sort - from start (1M items)', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - - // db.blockSize = 1e5 - - await db.setSchema({ + const schema = { types: { user: { props: { @@ -420,31 +412,36 @@ await test('sort - from start (1M items)', async (t) => { }, }, }, + } as const + const db = new BasedDb({ + path: t.tmp, }) + await db.start({ clean: true }) + const client = await db.setSchema(schema) - db.create('user', { + client.create('user', { name: 'mr blap', age: 100, email: 'blap@blap.blap.blap', }) - db.create('user', { + client.create('user', { name: 'mr flap', age: 50, email: 'flap@flap.flap.flap', }) for (let i = 0; i < 1000e3; i++) { - db.create('user', { + client.create('user', { name: 'mr ' + i, age: i + 101, }) } - await db.drain() + await client.drain() deepEqual( - await db.query2('user').include('name').sort('age').range(0, 2).get(), + await client.query2('user').include('name').sort('age').range(0, 2).get(), [ { id: 2, name: 'mr flap' }, { id: 1, name: 'mr blap' }, @@ -452,7 +449,7 @@ await test('sort - from start (1M items)', async (t) => { ) deepEqual( - await db.query2('user').include('name').sort('age').range(0, 2).get(), + await client.query2('user').include('name').sort('age').range(0, 2).get(), [ { id: 2, name: 'mr flap' }, { id: 1, name: 'mr blap' }, @@ -460,7 +457,7 @@ await test('sort - from start (1M items)', async (t) => { ) deepEqual( - await db.query2('user').include('name').sort('name').range(0, 2).get(), + await client.query2('user').include('name').sort('name').range(0, 2).get(), [ { id: 3, @@ -475,16 +472,17 @@ await test('sort - from start (1M items)', async (t) => { await db.stop() - const newDb = new BasedDb({ + const db2 = new BasedDb({ path: t.tmp, }) - - await newDb.start() - - t.after(() => newDb.destroy()) + await db2.start() + t.after(() => db2.destroy()) + const client2 = new DbClient({ + hooks: getDefaultHooks(db2.server), + }) deepEqual( - await newDb.query2('user').include('name').sort('name').range(0, 2).get(), + await client2.query2('user').include('name').sort('name').range(0, 2).get(), [ { id: 3, @@ -496,8 +494,6 @@ await test('sort - from start (1M items)', async (t) => { }, ], ) - - // newDb.server.destroySortIndex('user', 'age') }) await test('unset value on create', async (t) => { From 749b33505dd5d917c2a0e16d129851ccab1eeb1a Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:37:03 +0100 Subject: [PATCH 434/449] Use testDb --- test/text/textFilter.ts | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/test/text/textFilter.ts b/test/text/textFilter.ts index 30804ed5b6..9d06474d89 100644 --- a/test/text/textFilter.ts +++ b/test/text/textFilter.ts @@ -1,28 +1,12 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { testDb } from '../shared/index.js' import { join } from 'path' import { deepEqual } from '../shared/assert.js' await test('textFilter', async (t) => { - const db = new BasedDb({ - path: t.tmp, - maxModifySize: 1e3 * 1e3, - }) - await db.start({ clean: true }) - - const dbX = new BasedDb({ - path: join(t.tmp, 'x'), - maxModifySize: 1e3 * 1e3, - }) - await dbX.start({ clean: true }) - - t.after(() => t.backup(db.server)) - t.after(() => dbX.destroy()) - - await db.setSchema({ + const db = await testDb(t, { locales: { - en: { required: true }, + en: { /* required: true */ }, nl: {}, }, types: { @@ -39,10 +23,9 @@ await test('textFilter', async (t) => { }, }, }) - - await dbX.setSchema({ + const dbx = testDb(t, { locales: { - en: { required: true }, + en: { /* required: true */ }, nl: {}, }, types: { @@ -58,7 +41,7 @@ await test('textFilter', async (t) => { }, }, }, - }) + }, { noBackup: true, path: join(t.tmp, 'x') }) await db.create( 'project', From 3472f03f3b156447050109675d7171e2a403bf11 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:41:04 +0100 Subject: [PATCH 435/449] Use the new client --- test/references/references.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/references/references.ts b/test/references/references.ts index bd4ab80ea5..fb7bd06659 100644 --- a/test/references/references.ts +++ b/test/references/references.ts @@ -1,8 +1,9 @@ -import { BasedDb } from '../../src/index.js' +import { BasedDb, getDefaultHooks } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' import { wait } from '../../src/utils/index.js' import {testDb} from '../shared/index.js' +import {DbClientClass} from '../../src/db-client/index.js' await test('references', async (t) => { const db = await testDb(t, { @@ -787,12 +788,12 @@ await test('single ref save and load', async (t) => { }, } as const - await db.setSchema(schema) + let client = await db.setSchema(schema) const users = [{ email: '1@saulx.com' }, { email: '2@saulx.com' }] for (const user of users) { - await db.upsert('user', { email: user.email }, {}) + await client.upsert('user', { email: user.email }, {}) } await db.stop() @@ -800,14 +801,17 @@ await test('single ref save and load', async (t) => { db = new BasedDb({ path: t.tmp, }) - await db.start() - await db.create('user', { + client = new DbClientClass({ + hooks: getDefaultHooks(db.server), + }) + + await client.create('user', { email: '3@saulx.com', invitedBy: 2, }) - deepEqual(await db.query2('user').include('email', 'invitedBy').get(), [ + deepEqual(await client.query2('user').include('email', 'invitedBy').get(), [ { id: 1, email: '1@saulx.com', invitedBy: null }, { id: 2, email: '2@saulx.com', invitedBy: null }, { From d651dc95a9cd43759883fbba34539b886b576b3f Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:42:26 +0100 Subject: [PATCH 436/449] this must be final --- test/references/referencesIndex.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/references/referencesIndex.ts b/test/references/referencesIndex.ts index 155b29d0b2..0a30d87c66 100644 --- a/test/references/referencesIndex.ts +++ b/test/references/referencesIndex.ts @@ -1,4 +1,3 @@ -import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' import test from '../shared/test.js' import { testDb } from '../shared/index.js' @@ -199,7 +198,7 @@ await test('index>len', async (t) => { name: 'marie', }) - const john = db.create('user', { + const john = await db.create('user', { name: 'john', friends: [bob, marie], }) From afbfb039a2b0e1c32ae53684a24ea84d80df2999 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:45:32 +0100 Subject: [PATCH 437/449] Use the new client --- test/scenarios/e-commerce.ts | 39 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/test/scenarios/e-commerce.ts b/test/scenarios/e-commerce.ts index 771d6f1852..425f33876c 100644 --- a/test/scenarios/e-commerce.ts +++ b/test/scenarios/e-commerce.ts @@ -34,9 +34,9 @@ await test('E-commerce Simulation', async (t) => { clearInterval(intervalId) }) - t.after(async () => t.backup(db.server) + t.after(async () => t.backup(db.server)) - await db.setSchema({ + const client = await db.setSchema({ locales: { en: {}, de: {} }, // Add locales for text fields types: { user: { @@ -135,7 +135,7 @@ await test('E-commerce Simulation', async (t) => { const userIdsArr: any[] = [] for (let i = 0; i < initialCategories; i++) { - const catId = db.create('category', { + const catId = client.create('category', { name: `Category ${i}`, description: { en: `Description for category ${i}` }, }) @@ -145,7 +145,7 @@ await test('E-commerce Simulation', async (t) => { } for (let i = 0; i < initialUsers; i++) { - const userId = db.create('user', { + const userId = client.create('user', { name: `User ${i}`, email: `user${i}@example.com`, lastLogin: Math.max( @@ -161,7 +161,7 @@ await test('E-commerce Simulation', async (t) => { for (let i = 0; i < initialProducts; i++) { const category = getRandom(categoryIds) if (category) { - const prodId = db.create('product', { + const prodId = client.create('product', { name: `Product ${i} ${randomString(5)}`, description: { en: `This is product ${i}. ${randomString(50)}`, @@ -177,7 +177,7 @@ await test('E-commerce Simulation', async (t) => { totalItemsCreated++ } } - await db.drain() + await client.drain() // --- Simulation Loop --- let totalAliasUpdate = 0 @@ -192,7 +192,7 @@ await test('E-commerce Simulation', async (t) => { const entityType = Math.random() if (entityType < 0.1 && categoryIds < 500) { // Create Category - const catId = await db.create('category', { + const catId = await client.create('category', { name: `New Category ${totalItemsCreated}`, description: { en: `Dynamic category ${totalItemsCreated}` }, }) @@ -201,7 +201,7 @@ await test('E-commerce Simulation', async (t) => { totalItemsCreated++ } else if (entityType < 0.4 && userIds < 10000) { // Create User - const userId = await db.create('user', { + const userId = await client.create('user', { name: `User ${totalItemsCreated}`, email: `user${totalItemsCreated}@example.com`, }) @@ -212,7 +212,7 @@ await test('E-commerce Simulation', async (t) => { // Create Product const category = getRandom(categoryIds) if (category) { - const prodId = await db.create('product', { + const prodId = await client.create('product', { name: `Product ${totalItemsCreated} ${randomString(5)}`, description: { en: `Desc ${totalItemsCreated}` }, price: randomPrice(), @@ -229,7 +229,7 @@ await test('E-commerce Simulation', async (t) => { const user = getRandom(userIds) const product = getRandom(productIds) if (user && product) { - const reviewId = await db.create('review', { + const reviewId = await client.create('review', { user, // product, rating: (Math.floor(Math.random() * 5) + 1) as 1 | 2 | 3 | 4 | 5, @@ -281,8 +281,7 @@ await test('E-commerce Simulation', async (t) => { // Update User (Name/Email via Upsert) const oldEmail = `user${getRandom(userIds)}@example.com` if (oldEmail) { - await db.upsert('user', { - email: oldEmail, // Find by alias + await client.upsert('user', { email: oldEmail }, { name: `Updated Name ${randomString(4)}`, lastLogin: Date.now(), }) @@ -308,21 +307,21 @@ await test('E-commerce Simulation', async (t) => { const idx = Math.floor(Math.random() * productIdsArr.length) const productId = productIdsArr[idx] if (productId) { - await db.delete('product', productId).catch(catchNotExists) + await client.delete('product', productId).catch(catchNotExists) productIdsArr.splice(idx, 1) } } else if (entityType < 0.6 && userIdsArr.length > 50) { const idx = Math.floor(Math.random() * userIdsArr.length) const userId = userIdsArr[idx] if (userId) { - await db.delete('user', userId).catch(catchNotExists) + await client.delete('user', userId).catch(catchNotExists) userIdsArr.splice(idx, 1) } } else if (reviewIdsArr.length > 10) { const idx = Math.floor(Math.random() * reviewIdsArr.length) const reviewId = reviewIdsArr[idx] if (reviewId) { - await db.delete('review', reviewId).catch(catchNotExists) + await client.delete('review', reviewId).catch(catchNotExists) reviewIdsArr.splice(idx, 1) } } @@ -332,7 +331,7 @@ await test('E-commerce Simulation', async (t) => { const queryType = Math.random() if (queryType < 0.1) { isSorted( - await db.query2('user').sort('lastLogin', 'asc').get(), + await client.query2('user').sort('lastLogin', 'asc').get(), 'lastLogin', 'asc', ) @@ -400,7 +399,7 @@ await test('E-commerce Simulation', async (t) => { // Get user by email (alias) const email = `user${getRandom(userIds)}@example.com` if (email) { - await db.query2('user', { email }).get() + await client.query2('user', { email }).get() } } } @@ -410,13 +409,13 @@ await test('E-commerce Simulation', async (t) => { // Occasionally try invalid operations await throws( async () => - db.create('product', { name: 'Too expensive', price: 20000 }), + client.create('product', { name: 'Too expensive', price: 20000 }), false, 'Validation: Price too high', ) await throws( async () => - db.create('review', { + client.create('review', { rating: 6, user: getRandom(userIds), product: getRandom(productIds), @@ -484,7 +483,7 @@ await test('E-commerce Simulation', async (t) => { await wait(500) const finalProductCount = ( - await db.query2('product').range(0, 10_000_000).get() + await client.query2('product').range(0, 10_000_000).get() ).length equal( From ad0ff5eb3fbb750a47cbd766813b4705c23b1ae4 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:47:32 +0100 Subject: [PATCH 438/449] Fix upserts --- test/shared/northwindDb.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/shared/northwindDb.ts b/test/shared/northwindDb.ts index 9922d6e220..811f8295ba 100644 --- a/test/shared/northwindDb.ts +++ b/test/shared/northwindDb.ts @@ -682,8 +682,7 @@ export default async function createNorthwindDb( [9, '48304'], [9, '55113'], [9, '55439'], - ].forEach((row) => db.upsert('territories', { - territoryId: row[1], + ].forEach((row) => db.upsert('territories', { territoryId: `${row[1]}` }, { employees: { add: [ row[0] ], }, @@ -1540,7 +1539,7 @@ export default async function createNorthwindDb( [11077, 'RATTC', 1, '1998-05-06', '1998-06-03', '', 2, 8.53, 'Rattlesnake Canyon Grocery', '2817 Milton Dr.', 'Albuquerque', 'NM', '87110', 'USA'], ].map(async (row) => db.create('orders', { id: row[0], - customer: await db.upsert('customers', { customerId: row[1] }), + customer: await db.upsert('customers', { customerId: `${row[1]}` }, {}), employee: row[2], orderStatus: (row[5] === '') ? 'created' : 'shipped', orderDate: row[3] && new Date(row[3]), From bc926c59cf922204f58e0fc90e47bf190e7b0824 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:53:27 +0100 Subject: [PATCH 439/449] fix nyctaxi --- test/scenarios/nycTaxi.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/test/scenarios/nycTaxi.ts b/test/scenarios/nycTaxi.ts index 89a3f9dc0c..3233e1a3e2 100644 --- a/test/scenarios/nycTaxi.ts +++ b/test/scenarios/nycTaxi.ts @@ -1,11 +1,10 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { join } from 'path' import { readdir, readFile } from 'node:fs/promises' import { promisify } from 'node:util' import { gunzip as _gunzip } from 'zlib' import { Sema } from 'async-sema' -import { logMemoryUsage } from '../shared/index.js' +import { logMemoryUsage, testDb } from '../shared/index.js' const gunzip = promisify(_gunzip) @@ -342,16 +341,7 @@ class Loading { } await test.skip('taxi', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - // FIXME - //t.after(() => t.backup(db.server)) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { zone: { props: { @@ -431,7 +421,7 @@ await test.skip('taxi', async (t) => { }, }, }, - }) + }, { noBackup: true }) for (let i = 0; i < taxiZoneLookup.length; i += 4) { db.create('zone', { From 9b98c8b5292436c3d21a6edcac2a4ad2a4247d89 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 14:57:37 +0100 Subject: [PATCH 440/449] testdb fixes --- test/scenarios/voteStorage.ts | 37 +++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/test/scenarios/voteStorage.ts b/test/scenarios/voteStorage.ts index 36e698d4d5..cd264d5d37 100644 --- a/test/scenarios/voteStorage.ts +++ b/test/scenarios/voteStorage.ts @@ -3,6 +3,7 @@ import test from '../shared/test.js' import { SchemaProp, SchemaType } from '../../src/schema/index.js' import { deepEqual } from '../shared/assert.js' import { inspect } from 'util' +import {testDb} from '../shared/index.js' const countrySchema: SchemaType = { props: { @@ -55,7 +56,7 @@ await test('vote including round', async (t) => { const voteCountrySchema: any = countrySchema - await db.setSchema({ + const client = await db.setSchema({ types: { payment: { fingerprint: 'alias', @@ -125,32 +126,26 @@ await test('vote including round', async (t) => { }, }, }) - const final = await db.create('round', {}) + const final = await client.create('round', {}) for (let i = 0; i < 3e5; i++) { - const payment = db.create('payment', { + const payment = client.create('payment', { fingerprint: `blablabla-${i}`, status: 'WebhookSuccess', round: final, }) - const vote = db.create('vote', { + const vote = client.create('vote', { fingerprint: `blablabla-vote-${i}`, payment, round: final, }) } await db.save() - console.log('set all items', await db.drain()) + console.log('set all items', await client.drain()) }) const testVotes = (opts: { votes: any; amount: number }) => { return test(`vote single ref test remove ${inspect(opts)}`, async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db.server)) - - await db.setSchema({ + const client = await testDb(t, { types: { round: { votes: { @@ -172,23 +167,23 @@ const testVotes = (opts: { votes: any; amount: number }) => { let amount = opts.amount - const final = await db.create('round') + const final = await client.create('round', {}) for (let i = 0; i < amount; i++) { - db.create('vote', { + client.create('vote', { round: final, }) } - console.log(`Creating votes (${amount})ms`, await db.drain()) + console.log(`Creating votes (${amount})ms`, await client.drain()) console.log('Remove votes from final') - await db.update('round', final, { + await client.update('round', final, { votes: opts.votes, }) deepEqual( - (await db.query2('round', final).include('votes').get()).votes.length, + (await client.query2('round', final).include('votes').get()).votes.length, 0, 'clear refs', ) @@ -197,22 +192,22 @@ const testVotes = (opts: { votes: any; amount: number }) => { for (let i = 0; i < len; i++) { const randomId = amount === 1 ? 1 : Math.ceil(Math.random() * amount) deepEqual( - await db.query2('vote', randomId).include('round').get(), + await client.query2('vote', randomId).include('round').get(), { id: randomId, round: null }, `clears refs on the other side ${randomId}`, ) } - const votes = await db.query2('vote').range(0, 1e6).include('id').get() + const votes = await client.query2('vote').range(0, 1e6).include('id').get() let i = votes.length - 1 for (i = 0; i < votes.length; i++) { - db.delete('vote', votes[i].id) + client.delete('vote', votes[i].id) } console.log( 'Total db time removing all votes (refs in round)', - await db.drain(), + await client.drain(), ) }) } From b61ab56d5ab8154bb9ccf53479cccff97e7fcc10 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 15:06:17 +0100 Subject: [PATCH 441/449] Use testDb --- test/references/create.perf.ts | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/test/references/create.perf.ts b/test/references/create.perf.ts index 2e09445b1e..e20421a905 100644 --- a/test/references/create.perf.ts +++ b/test/references/create.perf.ts @@ -1,15 +1,10 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { equal, perf } from '../shared/assert.js' +import { testDb } from '../shared/index.js' +import { perf } from '../shared/assert.js' await test('create 1m items with 1 reference(s)', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db.server)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { refs: { @@ -37,13 +32,7 @@ await test('create 1m items with 1 reference(s)', async (t) => { }) await test('create 1m items with 100 reference(s)', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => t.backup(db.server)) - - await db.setSchema({ + const db = await testDb(t, { types: { test: { refs: { From 04b92b3d9ec6b932f4a9c5ad6b748d3fe74553e6 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 15:06:28 +0100 Subject: [PATCH 442/449] Remove dead code --- test/save/blockHash.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/save/blockHash.ts b/test/save/blockHash.ts index bc583fa6be..e025b367c5 100644 --- a/test/save/blockHash.ts +++ b/test/save/blockHash.ts @@ -1,4 +1,4 @@ -import assert, { equal, notEqual } from 'node:assert' +import assert, { equal } from 'node:assert' import fs from 'node:fs/promises' import path from 'node:path' import { createHash } from 'node:crypto' @@ -8,7 +8,6 @@ import native from '../../src/native.js' import { deepEqual } from '../shared/assert.js' import { getBlockHash } from '../../src/db-server/blocks.js' -const f = (v) => v.map((r) => r.hash) const sha1 = async (path: string) => createHash('sha1') .update(await fs.readFile(path)) From 267073a13604820468e24b81118b2b1c67a9b8c7 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Wed, 25 Feb 2026 15:07:47 +0100 Subject: [PATCH 443/449] Use testDb --- test/aggregate/groupBY.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/test/aggregate/groupBY.ts b/test/aggregate/groupBY.ts index fe2ae74ff7..e3ac71394e 100644 --- a/test/aggregate/groupBY.ts +++ b/test/aggregate/groupBY.ts @@ -1,6 +1,5 @@ -import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' -import { throws, deepEqual } from '../shared/assert.js' +import { deepEqual } from '../shared/assert.js' import { testDb } from '../shared/index.js' await test('sum group by', async (t) => { @@ -384,13 +383,7 @@ await test('group by unique numbers', async (t) => { }) await test.skip('groupBy ranges in numeric properties', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await db.setSchema({ types: { trip: { tripId: 'number', @@ -399,7 +392,7 @@ await test.skip('groupBy ranges in numeric properties', async (t) => { distance: 'number', }, }, - }) + }, { noBackup: true }) for (let i = 0; i < 10; i++) { db.create('trip', { From 9cbe4ddda7dfb63a9c87be51ae9312a1c65ece35 Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 25 Feb 2026 16:35:02 +0100 Subject: [PATCH 444/449] update types --- package.json | 1 + src/db-client/query2/types.ts | 20 ++++++++++---- src/schema/defs/props/references.ts | 6 +++++ test/edges/edgeType.ts | 1 - test/filter/edges.ts | 17 +++--------- test/filter/references.ts | 4 +-- test/filter/string.ts | 2 +- test/validation/validationReferences.ts | 36 ++++++++++++++++++++++--- tsconfig.test.json | 18 +++++++++++++ 9 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 tsconfig.test.json diff --git a/package.json b/package.json index b247fe3add..9da371fb22 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "watch-zig:linux-arm64": "zig build -Dtarget=aarch64-linux-gnu --watch", "watch-zig:linux-x64": "zig build -Dtarget=x86_64-linux-gnu --watch", "watch-ts": "tsc -p tsconfig.build.json --watch", + "watch-ts-test": "tsc -p tsconfig.test.json --watch", "watch-native": "npm run build-c && npm run watch-zig", "watch-exports": "tsx --watch-path=./native/types.zig scripts/zigTsExports.ts ", "watch": "npm run concurrently npm:watch-native npm:watch-ts npm:watch-exports", diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index c635ab5c51..0d71f0dbc8 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -56,7 +56,7 @@ export type PickOutputFromProps< Props[P], S['types'], S['locales'] extends Record ? S['locales'] : {}, - '*' + '-*' > : InferProp< Props[P], @@ -155,12 +155,22 @@ export type RefKeys = { [K in keyof Props]: IsRefProp extends true ? K : never }[keyof Props] +export type NonRefNonEdgeKeys = { + [K in keyof Props]: IsRefProp extends true + ? never + : K extends `$${string}` + ? never + : K +}[keyof Props] + export type ResolveInclude = K extends any ? K extends '*' ? NonRefKeys - : K extends '**' - ? RefKeys - : K + : K extends '-*' + ? NonRefNonEdgeKeys + : K extends '**' + ? RefKeys + : K : never export type IncludeSelection< @@ -183,7 +193,7 @@ export type PickOutput< ResolvedProps[P], S['types'], S['locales'] extends Record ? S['locales'] : {}, - '*' + '-*' > : InferSchemaOutput[P] : InferSchemaOutput[P] diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 5d45cc22e0..6a16d902ef 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -137,6 +137,9 @@ const setReferences = ( op: ModifyEnum, lang: LangCodeEnum, ) => { + if (!Array.isArray(value)) { + throw new Error('References value must be an array') + } let offset = 0 const len = value.length while (offset < len) { @@ -176,6 +179,9 @@ const setReferences = ( } const deleteReferences = (buf: AutoSizedUint8Array, value: any[]) => { + if (!Array.isArray(value)) { + throw new Error('References value must be an array') + } let offset = 0 while (offset < value.length) { const item = value[offset] diff --git a/test/edges/edgeType.ts b/test/edges/edgeType.ts index cc8d148b8d..d558387f4f 100644 --- a/test/edges/edgeType.ts +++ b/test/edges/edgeType.ts @@ -4,7 +4,6 @@ import { countDirtyBlocks, testDbServer, testDbClient, - testDb, } from '../shared/index.js' await test('single reference', async (t) => { diff --git a/test/filter/edges.ts b/test/filter/edges.ts index 900eacdc9d..42e49fc2e3 100644 --- a/test/filter/edges.ts +++ b/test/filter/edges.ts @@ -1,15 +1,10 @@ import { BasedDb } from '../../src/index.js' import test from '../shared/test.js' import { deepEqual } from '../shared/assert.js' +import { testDb } from '../shared/index.js' await test('filter edges', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { team: { props: { @@ -93,13 +88,7 @@ await test('filter edges', async (t) => { }) await test('filter references', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - await db.start({ clean: true }) - t.after(() => db.stop()) - - await db.setSchema({ + const db = await testDb(t, { types: { team: { props: { diff --git a/test/filter/references.ts b/test/filter/references.ts index 9ebe982b7d..60849a64a0 100644 --- a/test/filter/references.ts +++ b/test/filter/references.ts @@ -1,10 +1,10 @@ import test from '../shared/test.js' import { BasedDb } from '../../src/index.js' import { deepEqual } from '../shared/assert.js' -import {testDb} from '../shared/index.js' +import { testDb } from '../shared/index.js' await test('filter references drones', async (t) => { - const db= await testDb(t, { + const db = await testDb(t, { types: { workspace: { props: { diff --git a/test/filter/string.ts b/test/filter/string.ts index f83f751606..9f354feff3 100644 --- a/test/filter/string.ts +++ b/test/filter/string.ts @@ -565,7 +565,7 @@ await test('OR equal', async (t) => { }) await test('OR equal main', async (t) => { - const db = await testDb(t, { + const db = await testDb(t, { types: { italy: { props: { diff --git a/test/validation/validationReferences.ts b/test/validation/validationReferences.ts index 1953b71b14..4a994cc925 100644 --- a/test/validation/validationReferences.ts +++ b/test/validation/validationReferences.ts @@ -1,9 +1,9 @@ import { deepEqual, throws } from '../shared/assert.js' -import {testDb} from '../shared/index.js' +import { testDb } from '../shared/index.js' import test from '../shared/test.js' await test('update', async (t) => { - const db= await testDb(t, { + const db = await testDb(t, { locales: { en: {}, de: {} }, types: { flap: { @@ -36,6 +36,7 @@ await test('update', async (t) => { }) await throws(async () => { + // @ts-expect-error return db.query2('flap').include('x.$derp').get() }, 'Non existing reference on flap') @@ -75,61 +76,72 @@ await test('update', async (t) => { ) await throws(async () => { + // @ts-expect-error db.create('user', { connections: user1 }) }, 'Expected array for references field connections') await throws(async () => { + // @ts-expect-error db.update('user', userWithConn, { connections: user1 }) }, 'Expected array or object for references field connections') await throws(async () => { + // @ts-expect-error db.create('user', { connections: [user1, 'not an id'] }) }, 'Invalid reference "not an id" for field connections') await throws(async () => { + // @ts-expect-error db.create('user', { connections: [user1, {}] }) }, 'Invalid reference "[object Object]" for field connections') await throws(async () => { + // @ts-expect-error db.create('user', { connections: [user1, invalidId] }) }, 'Invalid reference "usr_invalid" for field connections') await throws(async () => { + // @ts-expect-error db.update('user', userWithConn, { connections: { add: [invalidId] } }) }, 'Invalid reference "usr_invalid" for field add in connections') await throws(async () => { db.update('user', userWithConn, { + // @ts-expect-error connections: { update: [invalidId] }, }) }, 'Invalid reference "usr_invalid" for field update in connections') await throws(async () => { + // @ts-expect-error db.update('user', userWithConn, { connections: { add: [invalidId] } }) }, 'Invalid reference "usr_invalid" for field add in connections') await throws(async () => { db.update('user', userWithConn, { + // @ts-expect-error connections: { delete: [invalidId] }, }) }, 'Invalid reference "usr_invalid" for field delete in connections') await throws(async () => { + // @ts-expect-error db.update('user', userWithConn, { connections: { update: 'bla' } }) }, 'Expected array for field set in connections') await throws(async () => { + // @ts-expect-error db.update('user', userWithConn, { connections: { add: {} } }) }, 'Expected array for field add in connections') await throws(async () => { + // @ts-expect-error db.update('user', userWithConn, { connections: { delete: 123 } }) }, 'Expected array for field delete in connections') // --- Friends (with Edges) Validation --- const now = Date.now() const badge = new Uint8Array([1, 2, 3]) - const badgeString = 'badge-string' // String is also valid for binary const userWithFriends = await db.create('user', { name: 'friendlyUser', @@ -140,7 +152,7 @@ await test('update', async (t) => { $friendsSince: now, $friendShipBadge: badge, }, - { id: user2, $bestFriend: false, $friendShipBadge: badgeString }, // Minimal edge data + string badge + { id: user2, $bestFriend: false, $friendShipBadge: badge }, // Minimal edge data ], }) @@ -206,6 +218,9 @@ await test('update', async (t) => { id: 3, $bestFriend: true, $friendsSince: new Date('09/02/2000').getTime(), + $friendShipBadge: new Uint8Array([ + 98, 97, 100, 103, 101, 45, 115, 116, 114, 105, 110, 103, + ]), }, ], }, @@ -257,27 +272,33 @@ await test('update', async (t) => { ) await throws(async () => { + // @ts-expect-error db.create('user', { friends: { id: user1 } }) }, 'Expected array for references field friends') await throws(async () => { + // @ts-expect-error db.update('user', userWithFriends, { friends: user1 }) }, 'Expected array or object for references field friends') await throws(async () => { + // @ts-expect-error db.create('user', { friends: [user1, 'not an id'] }) }, 'Invalid reference "not an id" for field friends') await throws(async () => { + // @ts-expect-error db.create('user', { friends: [user1, { $bestFriend: true }] }) }, 'Missing id in reference object for field friends') await throws(async () => { + // @ts-expect-error db.create('user', { friends: [user1, { id: invalidId }] }) }, 'Invalid reference "usr_invalid" for field friends') await throws(async () => { db.create('user', { + // @ts-expect-error friends: [{ id: user1, $bestFriend: 'yes' }], }) }, 'Incorrect type for $bestFriend expected boolean got string') @@ -290,39 +311,46 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { + // @ts-expect-error friends: [{ id: user1, $friendShipBadge: [1, 2, 3] }], }) }, 'Incorrect type for $friendShipBadge expected binary got array') await throws(async () => { db.create('user', { + // @ts-expect-error friends: [{ id: user1, $friendLevel: 9000 }], }) }, 'Unknown edge field "$friendLevel" for reference field friends') await throws(async () => { db.update('user', userWithFriends, { + // @ts-expect-error friends: { add: [{ id: user1, $bestFriend: 'yes' }] }, }) }, 'Incorrect type for $bestFriend expected boolean got string') await throws(async () => { db.update('user', userWithFriends, { + // @ts-expect-error friends: { add: [{ $bestFriend: true }] }, }) }, 'Missing id in reference object for field add in friends') await throws(async () => { db.update('user', userWithFriends, { + // @ts-expect-error friends: { delete: [{ id: user1 }] }, }) }, 'Cannot have edge data in delete operation for field friends') await throws(async () => { + // @ts-expect-error db.update('user', userWithFriends, { friends: { add: {} } }) }, 'Expected array for field add in friends') await throws(async () => { + // @ts-expect-error db.update('user', userWithFriends, { friends: { delete: 123 } }) }, 'Expected array for field delete in friends') diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000000..1719acded3 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,18 @@ +{ + "extends": "@saulx/tsconfig/default.json", + "compilerOptions": { + "outDir": "dist", + "esModuleInterop": true, + "allowJs": true, + "noPropertyAccessFromIndexSignature": false, + "rootDir": ".", + "strictNullChecks": true, + "noEmit": true, + "jsx": "react", + "declarationMap": true, + "strictPropertyInitialization": true, + "pretty": true + }, + "include": ["test"], + "exclude": ["dist", "**/_*"] +} From 87ea06679b24aaf22568af777bb5a8ae65a0835a Mon Sep 17 00:00:00 2001 From: youzi Date: Wed, 25 Feb 2026 18:33:15 +0100 Subject: [PATCH 445/449] add .locale --- compile_errors.txt | 7914 ++++++++++++++++++++++++++++ src/db-client/index.ts | 4 +- src/db-client/query2/index.ts | 85 +- src/db-client/query2/types.ts | 116 +- src/db-query/BasedDbQuery.ts | 27 - src/db-query/BasedDbQueryResult.ts | 19 - test/query/db.ts | 9 + test/text/textFilter.ts | 40 +- test/validation/validation.ts | 176 +- testType.ts | 17 + testType2.ts | 24 + testType3.ts | 24 + 12 files changed, 8242 insertions(+), 213 deletions(-) create mode 100644 compile_errors.txt delete mode 100644 src/db-query/BasedDbQuery.ts delete mode 100644 src/db-query/BasedDbQueryResult.ts create mode 100644 testType.ts create mode 100644 testType2.ts create mode 100644 testType3.ts diff --git a/compile_errors.txt b/compile_errors.txt new file mode 100644 index 0000000000..7fee670c10 --- /dev/null +++ b/compile_errors.txt @@ -0,0 +1,7914 @@ +scripts/repl.ts:6:10 - error TS2305: Module '"../src/protocol/index.js"' has no exported member 'AggregateType'. + +6 import { AggregateType } from '../src/protocol/index.js' +   ~~~~~~~~~~~~~ + +scripts/test_push_exports.ts:13:37 - error TS2307: Cannot find module '../src/modify/AutoSizedUint8Array.js' or its corresponding type declarations. + +13 import { AutoSizedUint8Array } from '../src/modify/AutoSizedUint8Array.js' +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +scripts/test_push_exports.ts:17:9 - error TS2739: Type '{ id: number; start: number; size: number; }' is missing the following properties from type 'ModifyMainHeader': type, increment, incrementPositive, expire + +17 const header: ModifyMainHeader = { +   ~~~~~~ + +src/db-client/query2/index.ts:73:5 - error TS2304: Cannot find name 'L'. + +73 L, +   ~ + +src/db-client/query2/index.ts:142:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +142 ): NextBranch< +   ~~~~~~~~~~~ +143 S, +  ~~~~~~ +... +151 GroupedKey +  ~~~~~~~~~~~~~~ +152 > +  ~~~ + +src/db-client/query2/index.ts:155:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +155 ): NextBranch< +   ~~~~~~~~~~~ +156 S, +  ~~~~~~ +... +164 GroupedKey +  ~~~~~~~~~~~~~~ +165 > +  ~~~ + +src/db-client/query2/index.ts:182:12 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +182 count(): NextBranch< +   ~~~~~~~~~~~ +183 S, +  ~~~~~~ +... +191 GroupedKey +  ~~~~~~~~~~~~~~ +192 > { +  ~~~ + +src/db-client/query2/index.ts:199:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +199 ): NextBranch< +   ~~~~~~~~~~~ +200 S, +  ~~~~~~ +... +208 GroupedKey +  ~~~~~~~~~~~~~~ +209 > +  ~~~ + +src/db-client/query2/index.ts:212:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +212 ): NextBranch< +   ~~~~~~~~~~~ +213 S, +  ~~~~~~ +... +221 GroupedKey +  ~~~~~~~~~~~~~~ +222 > +  ~~~ + +src/db-client/query2/index.ts:241:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +241 ): NextBranch< +   ~~~~~~~~~~~ +242 S, +  ~~~~~~ +... +250 GroupedKey +  ~~~~~~~~~~~~~~ +251 > +  ~~~ + +src/db-client/query2/index.ts:254:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +254 ): NextBranch< +   ~~~~~~~~~~~ +255 S, +  ~~~~~~ +... +263 GroupedKey +  ~~~~~~~~~~~~~~ +264 > +  ~~~ + +src/db-client/query2/index.ts:283:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +283 ): NextBranch< +   ~~~~~~~~~~~ +284 S, +  ~~~~~~ +... +292 GroupedKey +  ~~~~~~~~~~~~~~ +293 > +  ~~~ + +src/db-client/query2/index.ts:296:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +296 ): NextBranch< +   ~~~~~~~~~~~ +297 S, +  ~~~~~~ +... +305 GroupedKey +  ~~~~~~~~~~~~~~ +306 > +  ~~~ + +src/db-client/query2/index.ts:325:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +325 ): NextBranch< +   ~~~~~~~~~~~ +326 S, +  ~~~~~~ +... +334 GroupedKey +  ~~~~~~~~~~~~~~ +335 > +  ~~~ + +src/db-client/query2/index.ts:338:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +338 ): NextBranch< +   ~~~~~~~~~~~ +339 S, +  ~~~~~~ +... +348 GroupedKey +  ~~~~~~~~~~~~~~ +349 > +  ~~~ + +src/db-client/query2/index.ts:368:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +368 ): NextBranch< +   ~~~~~~~~~~~ +369 S, +  ~~~~~~ +... +377 GroupedKey +  ~~~~~~~~~~~~~~ +378 > +  ~~~ + +src/db-client/query2/index.ts:381:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +381 ): NextBranch< +   ~~~~~~~~~~~ +382 S, +  ~~~~~~ +... +391 GroupedKey +  ~~~~~~~~~~~~~~ +392 > +  ~~~ + +src/db-client/query2/index.ts:412:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +412 ): NextBranch< +   ~~~~~~~~~~~ +413 S, +  ~~~~~~ +... +421 GroupedKey +  ~~~~~~~~~~~~~~ +422 > +  ~~~ + +src/db-client/query2/index.ts:425:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +425 ): NextBranch< +   ~~~~~~~~~~~ +426 S, +  ~~~~~~ +... +434 GroupedKey +  ~~~~~~~~~~~~~~ +435 > +  ~~~ + +src/db-client/query2/index.ts:438:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +438 ): NextBranch { +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:472:6 - error TS2314: Generic type 'NextBranch' requires 10 type argument(s). + +472 ): NextBranch< +   ~~~~~~~~~~~ +473 S, +  ~~~~~~ +... +481 GroupedKey +  ~~~~~~~~~~~~~~ +482 > +  ~~~ + +src/db-client/query2/index.ts:493:5 - error TS2304: Cannot find name 'L'. + +493 L, +   ~ + +src/db-client/query2/index.ts:541:5 - error TS2304: Cannot find name 'L'. + +541 L, +   ~ + +src/db-client/query2/index.ts:550:36 - error TS2536: Type '"locales"' cannot be used to index type 'S'. + +550 locale>( +   ~~~~~~~~~~~~ + +src/db-client/query2/index.ts:579:5 - error TS2304: Cannot find name 'L'. + +579 L, +   ~ + +src/db-client/query2/index.ts:598:5 - error TS2304: Cannot find name 'L'. + +598 L, +   ~ + +src/db-client/query2/index.ts:617:5 - error TS2304: Cannot find name 'L'. + +617 L, +   ~ + +src/db-client/query2/index.ts:675:13 - error TS2707: Generic type 'Query' requires between 0 and 9 type arguments. + +675 >(type: T): Query +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:684:24 - error TS2314: Generic type 'InferSchemaOutput' requires 2 type argument(s). + +684 id: number | Partial>, +   ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:685:4 - error TS2707: Generic type 'Query' requires between 0 and 9 type arguments. + +685 ): Query +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:693:40 - error TS2314: Generic type 'InferSchemaOutput' requires 2 type argument(s). + +693 target?: number | number[] | Partial>, +   ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:694:4 - error TS2707: Generic type 'Query' requires between 0 and 9 type arguments. + +694 ): Query { +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:697:20 - error TS2558: Expected 0-9 type arguments, but got 10. + +697 return new Query( +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:715:11 - error TS2707: Generic type 'Query' requires between 0 and 9 type arguments. + +715 > extends Query< +   ~~~~~~ +716 S, +  ~~~~ +... +725 GroupedKey +  ~~~~~~~~~~~~ +726 > { +  ~ + +src/db-client/query2/index.ts:730:42 - error TS2314: Generic type 'InferSchemaOutput' requires 2 type argument(s). + +730 target?: number | number[] | Partial>, +   ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/index.ts:733:10 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +733 this.ast.type = type as string +   ~~~ + +src/db-client/query2/index.ts:734:22 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +734 if (target) this.ast.target = target +   ~~~ + +src/db-client/query2/index.ts:758:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +758 !this.ast.props && +   ~~~ + +src/db-client/query2/index.ts:759:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +759 !this.ast.sum && +   ~~~ + +src/db-client/query2/index.ts:760:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +760 !this.ast.count && +   ~~~ + +src/db-client/query2/index.ts:761:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +761 !this.ast.avg && +   ~~~ + +src/db-client/query2/index.ts:762:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +762 !this.ast.hmean && +   ~~~ + +src/db-client/query2/index.ts:763:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +763 !this.ast.max && +   ~~~ + +src/db-client/query2/index.ts:764:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +764 !this.ast.min && +   ~~~ + +src/db-client/query2/index.ts:765:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +765 !this.ast.stddev && +   ~~~ + +src/db-client/query2/index.ts:766:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +766 !this.ast.variance && +   ~~~ + +src/db-client/query2/index.ts:767:13 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +767 !this.ast.cardinality +   ~~~ + +src/db-client/query2/index.ts:769:12 - error TS2339: Property 'include' does not exist on type 'BasedQuery2'. + +769 this.include('*') +   ~~~~~~~ + +src/db-client/query2/index.ts:777:12 - error TS2339: Property 'ast' does not exist on type 'BasedQuery2'. + +777 this.ast, +   ~~~ + +src/db-client/query2/index.ts:875:43 - error TS2707: Generic type 'Query' requires between 0 and 9 type arguments. + +875 type AnyQuery = Query< +   ~~~~~~ +876 S, +  ~~~~ +... +885 any +  ~~~~~ +886 > +  ~ + +src/db-client/query2/index.ts:907:5 - error TS2707: Generic type 'Query' requires between 0 and 9 type arguments. + +907 : Query< +   ~~~~~~ +908 S, +  ~~~~~~~~ +... +917 GroupedKey +  ~~~~~~~~~~~~~~~~ +918 > +  ~~~~~ + +src/db-client/query2/types.ts:347:15 - error TS2314: Generic type 'InferPropsPathType' requires 3 type argument(s). + +347 ? InferPropsPathType +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +src/db-client/query2/types.ts:347:56 - error TS2304: Cannot find name 'L'. + +347 ? InferPropsPathType +   ~ + +src/db-client/query2/types.ts:376:5 - error TS2314: Generic type 'InferPropsPathType' requires 3 type argument(s). + +376 > = InferPropsPathType & EdgeProps, P, L> +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/aggregate/basic.ts:55:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +55 await db.query2('vote').sum('NL').get(), +   ~~~ + +test/aggregate/basic.ts:61:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +61 await db.query2('vote').sum('NL', 'AU').get(), +   ~~~ + +test/aggregate/basic.ts:72:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +72 await db.query2('vote').sum('flap.hello').get(), +   ~~~ + +test/aggregate/basic.ts:126:29 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +126 await db.query2('vote').count().get(), +   ~~~~~ + +test/aggregate/basic.ts:142:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +142 await db.query2('vote').include('IT').count().get(), +   ~~~~~~~ + +test/aggregate/basic.ts:158:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +158 await db.query2('vote').filter('NL', '=', 20).count().get(), +   ~~~~~~ + +test/aggregate/basic.ts:164:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +164 await db.query2('vote').filter('NL', '>', 255).count().get(), +   ~~~~~~ + +test/aggregate/basic.ts:225:29 - error TS2339: Property 'stddev' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +225 await db.query2('vote').stddev('NL', { mode: 'sample' }).get(), +   ~~~~~~ + +test/aggregate/basic.ts:233:29 - error TS2339: Property 'stddev' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +233 await db.query2('vote').stddev('NL', { mode: 'sample' }).get(), +   ~~~~~~ + +test/aggregate/basic.ts:241:29 - error TS2339: Property 'stddev' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +241 await db.query2('vote').stddev('NL', { mode: 'population' }).get(), +   ~~~~~~ + +test/aggregate/basic.ts:249:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +249 await db.query2('vote').sum('NL').get(), +   ~~~ + +test/aggregate/basic.ts:259:8 - error TS2339: Property 'stddev' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +259 .stddev('NL', { mode: 'population' }) +   ~~~~~~ + +test/aggregate/basic.ts:392:29 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +392 await db.query2('vote').groupBy('region').get(), +   ~~~~~~~ + +test/aggregate/basic.ts:402:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +402 await db.query2('vote').sum('NL', 'FI').groupBy('region').get(), +   ~~~ + +test/aggregate/basic.ts:421:29 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +421 await db.query2('vote').count().groupBy('region').get(), +   ~~~~~ + +test/aggregate/basic.ts:437:29 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +437 await db.query2('vote').avg('NL', 'PT', 'FI').groupBy('region').get(), +   ~~~ + +test/aggregate/basic.ts:459:29 - error TS2339: Property 'hmean' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +459 await db.query2('vote').hmean('NL', 'PT', 'FI').groupBy('region').get(), +   ~~~~~ + +test/aggregate/basic.ts:483:8 - error TS2339: Property 'stddev' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +483 .stddev('NL', 'PL', { mode: 'population' }) +   ~~~~~~ + +test/aggregate/basic.ts:504:29 - error TS2339: Property 'stddev' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +504 await db.query2('vote').stddev('NL', 'PL').groupBy('region').get(), +   ~~~~~~ + +test/aggregate/basic.ts:525:8 - error TS2339: Property 'var' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +525 .var('NL', 'PL', { mode: 'population' }) +   ~~~ + +test/aggregate/basic.ts:548:8 - error TS2339: Property 'var' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +548 .var('NL', 'PL', { mode: 'sample' }) +   ~~~ + +test/aggregate/basic.ts:560:29 - error TS2339: Property 'var' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +560 await db.query2('vote').var('NL', 'PL').groupBy('region').get(), +   ~~~ + +test/aggregate/basic.ts:570:29 - error TS2339: Property 'max' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +570 await db.query2('vote').max('NL', 'NO', 'PT', 'FI').groupBy('region').get(), +   ~~~ + +test/aggregate/basic.ts:595:29 - error TS2339: Property 'min' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +595 await db.query2('vote').min('NL', 'NO', 'PT', 'FI').groupBy('region').get(), +   ~~~ + +test/aggregate/basic.ts:915:12 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly product: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 10; }; readonly flap: { ...; }; readonly product: { ...; }; }; }; readonly shel...'. + +915 .include('*') +   ~~~~~~~ + +test/aggregate/basic.ts:1002:10 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly job: { props: { readonly day: { type: "timestamp"; }; readonly tip: { ...; }; readonly employee: { ...; } & { ...; }; }; }; readonly employee: { ...; }; readonl...'. + +1002 .groupBy('day', { step: 'hour', timeZone: 'America/Sao_Paulo' }) +   ~~~~~~~ + +test/aggregate/deep.ts:57:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +57 .include((select) => select('votes').sum('NL', 'AU')) +   ~~~~~~~ + +test/aggregate/deep.ts:66:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +66 .include((select) => select('votes').groupBy('country').sum('NL', 'AU')) +   ~~~~~~~ + +test/aggregate/deep.ts:142:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +142 .include((select) => select('votes').count()) +   ~~~~~~~ + +test/aggregate/deep.ts:151:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +151 .include((select) => select('votes').groupBy('country').sum('NL', 'AU')) +   ~~~~~~~ + +test/aggregate/deep.ts:271:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly team: { props: { readonly teamName: { readonly type: "string"; }; readonly city: { ...; }; readonly players: Omit<...> & { ...; }; }; }; readonly player: { ...;...'. + +271 .include('teamName', 'city', (select) => +   ~~~~~~~ + +test/aggregate/deep.ts:357:29 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly beer: { props: { readonly name: { type: "string"; }; ... 4 more ...; readonly year: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +357 await db.query2('beer').avg('price').groupBy('type').get(), +   ~~~ + +test/aggregate/deep.ts:370:29 - error TS2339: Property 'hmean' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly beer: { props: { readonly name: { type: "string"; }; ... 4 more ...; readonly year: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +370 await db.query2('beer').hmean('price').groupBy('type').get(), +   ~~~~~ + +test/aggregate/deep.ts:422:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly movie: { props: { readonly name: { type: "string"; }; readonly genre: { ...; }; readonly actors: Omit<...> & { ...; }; readonly movie: { ...; }; }; }; readonly ...'. + +422 .include((q) => q('movies').groupBy('genre').count()) +   ~~~~~~~ + +test/aggregate/deep.ts:508:30 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly week: { type: "string"; }; ... 6 more ...; readonly Fri: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +508 await db.query2('lunch').cardinality('Mon').get(), +   ~~~~~~~~~~~ + +test/aggregate/deep.ts:514:30 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly week: { type: "string"; }; ... 6 more ...; readonly Fri: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +514 await db.query2('lunch').cardinality('Mon').groupBy('week').get(), +   ~~~~~~~~~~~ + +test/aggregate/deep.ts:571:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly booth: { props: { readonly company: { type: "string"; }; readonly badgesScanned: { ...; }; readonly booth: { ...; }; }; }; readonly fair: { ...; }; }; locales: ...'. + +571 .include((s) => s('booths').cardinality('badgesScanned')) +   ~~~~~~~ + +test/aggregate/deep.ts:655:31 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vehicle: { ...; } & { ...; }; readon...'. + +655 await db.query2('driver').sum('rank').groupBy('vehicle').get(), +   ~~~ + +test/aggregate/deep.ts:667:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vehicle: { ...; } & { ...; }; readon...'. + +667 .include((q) => q('trips').groupBy('vehicle').max('distance')) +   ~~~~~~~ + +test/aggregate/deep.ts:723:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly strong: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5...'. + +723 await db.query2('user').sum('friends.strong').get(), +   ~~~ + +test/aggregate/experimental.ts:90:30 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly week: { type: "string"; }; ... 6 more ...; readonly Fri: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +90 await db.query2('lunch').cardinality('Mon').get(), +   ~~~~~~~~~~~ + +test/aggregate/experimental.ts:98:30 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly week: { type: "string"; }; ... 6 more ...; readonly Fri: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +98 await db.query2('lunch').cardinality('Mon').groupBy('week').get(), +   ~~~~~~~~~~~ + +test/aggregate/groupBY.ts:52:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +52 await db.query2('vote').sum('NL', 'AU').groupBy('country').get(), +   ~~~ + +test/aggregate/groupBY.ts:61:29 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +61 await db.query2('vote').groupBy('country').get(), +   ~~~~~~~ + +test/aggregate/groupBY.ts:126:29 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +126 await db.query2('vote').count().groupBy('country').get(), +   ~~~~~ + +test/aggregate/groupBY.ts:241:29 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly country: { ...; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }...'. + +241 await db.query2('user').groupBy('name').sum('flap').get(), +   ~~~~~~~ + +test/aggregate/groupBY.ts:253:29 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly country: { ...; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }...'. + +253 await db.query2('user').groupBy('country').sum('flap').get(), +   ~~~~~~~ + +test/aggregate/groupBY.ts:321:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +321 await db.query2('trip').sum('distance').groupBy('vendorIduint8').get(), +   ~~~ + +test/aggregate/groupBY.ts:330:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +330 await db.query2('trip').sum('distance').groupBy('vendorIdint8').get(), +   ~~~ + +test/aggregate/groupBY.ts:339:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +339 await db.query2('trip').sum('distance').groupBy('vendorIduint16').get(), +   ~~~ + +test/aggregate/groupBY.ts:348:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +348 await db.query2('trip').sum('distance').groupBy('vendorIdint16').get(), +   ~~~ + +test/aggregate/groupBY.ts:357:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +357 await db.query2('trip').sum('distance').groupBy('vendorIduint32').get(), +   ~~~ + +test/aggregate/groupBY.ts:366:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +366 await db.query2('trip').sum('distance').groupBy('vendorIdint32').get(), +   ~~~ + +test/aggregate/groupBY.ts:375:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; ... 8 more ...; readonly vendorIdnumber: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +375 await db.query2('trip').sum('distance').groupBy('vendorIdnumber').get(), +   ~~~ + +test/aggregate/groupBY.ts:386:20 - error TS2448: Block-scoped variable 'db' used before its declaration. + +386 const db = await db.setSchema({ +   ~~ + + test/aggregate/groupBY.ts:386:9 + 386 const db = await db.setSchema({ +    ~~ + 'db' is declared here. + +test/aggregate/multiple.ts:77:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +77 await db.query2('vote').sum('NL').sum('NO').max('NL').min('NL').get(), +   ~~~ + +test/aggregate/multiple.ts:85:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +85 .sum('NL') +   ~~~ + +test/aggregate/multiple.ts:106:6 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +106 .sum('NL') +   ~~~ + +test/aggregate/multiple.ts:139:6 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +139 .sum('NL') +   ~~~ + +test/aggregate/multiple.ts:206:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +206 await db.query2('vote').sum('NL').count().sum('PT').stddev('NO').get(), +   ~~~ + +test/aggregate/multiple.ts:224:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly votes: Omit<...> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +224 .sum('NL') +   ~~~ + +test/aggregate/overall.perf.ts:41:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly beer: { props: { readonly name: { type: "string"; }; ... 4 more ...; readonly year: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +41 await db.query2('beer').sum('price').get() +   ~~~ + +test/aggregate/overall.perf.ts:45:29 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly beer: { props: { readonly name: { type: "string"; }; ... 4 more ...; readonly year: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +45 await db.query2('beer').groupBy('year').get() +   ~~~~~~~ + +test/aggregate/overall.perf.ts:49:29 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly beer: { props: { readonly name: { type: "string"; }; ... 4 more ...; readonly year: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +49 await db.query2('beer').groupBy('type').get() +   ~~~~~~~ + +test/aggregate/overall.perf.ts:53:29 - error TS2339: Property 'max' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly beer: { props: { readonly name: { type: "string"; }; ... 4 more ...; readonly year: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +53 await db.query2('beer').max('price').groupBy('type').get() +   ~~~ + +test/aggregate/overall.perf.ts:72:41 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly sequence: { props: { readonly bla: { type: "uint32"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +72 const q = await db.query2('sequence').count().get() +   ~~~~~ + +test/aggregate/overall.perf.ts:316:8 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly audience: { partial?: boolean | undefined; ... 4 more ...; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +316 .avg(...countries) +   ~~~ + +test/aggregate/overall.perf.ts:337:8 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly audience: { partial?: boolean | undefined; ... 4 more ...; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +337 .avg(...countries) +   ~~~ + +test/aggregate/overall.perf.ts:349:8 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly audience: { partial?: boolean | undefined; ... 4 more ...; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +349 .avg(...countries) +   ~~~ + +test/aggregate/overall.perf.ts:361:8 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly audience: { partial?: boolean | undefined; ... 4 more ...; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +361 .avg(...countries) +   ~~~ + +test/aggregate/overall.perf.ts:373:8 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly audience: { partial?: boolean | undefined; ... 4 more ...; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +373 .avg(...countries) +   ~~~ + +test/aggregate/temporal.ts:32:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +32 await db.query2('trip').sum('distance').groupBy('pickup', 'day').get(), +   ~~~ + +test/aggregate/temporal.ts:43:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +43 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:54:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +54 await db.query2('trip').sum('distance').groupBy('pickup', 'hour').get(), +   ~~~ + +test/aggregate/temporal.ts:63:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +63 await db.query2('trip').sum('distance').groupBy('pickup', 'dow').get(), +   ~~~ + +test/aggregate/temporal.ts:72:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +72 await db.query2('trip').sum('distance').groupBy('pickup', 'isoDOW').get(), +   ~~~ + +test/aggregate/temporal.ts:81:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +81 await db.query2('trip').sum('distance').groupBy('pickup', 'doy').get(), +   ~~~ + +test/aggregate/temporal.ts:90:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +90 await db.query2('trip').sum('distance').groupBy('pickup', 'month').get(), +   ~~~ + +test/aggregate/temporal.ts:99:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +99 await db.query2('trip').sum('distance').groupBy('pickup', 'year').get(), +   ~~~ + +test/aggregate/temporal.ts:144:6 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +144 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:179:6 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +179 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:260:42 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly day: { type: "timestamp"; }; readonly eaters: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +260 const total = await db.query2('lunch').cardinality('eaters').get() +   ~~~~~~~~~~~ + +test/aggregate/temporal.ts:267:6 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly day: { type: "timestamp"; }; readonly eaters: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +267 .cardinality('eaters') +   ~~~~~~~~~~~ + +test/aggregate/temporal.ts:295:6 - error TS2339: Property 'cardinality' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly lunch: { props: { readonly day: { type: "timestamp"; }; readonly eaters: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +295 .cardinality('eaters') +   ~~~~~~~~~~~ + +test/aggregate/temporal.ts:345:29 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +345 await db.query2('trip').sum('distance').groupBy('pickup').get(), +   ~~~ + +test/aggregate/temporal.ts:360:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +360 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:374:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +374 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:420:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +420 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:437:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +437 .sum('distance') +   ~~~ + +test/aggregate/temporal.ts:453:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly trip: { props: { readonly pickup: { type: "timestamp"; }; readonly dropoff: { ...; }; readonly distance: { ...; }; readonly vendorId: { ...; }; }; }; }; locales...'. + +453 .sum('distance') +   ~~~ + +test/aggregate/validation.ts:29:29 - error TS2339: Property 'max' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly vote: { props: { readonly region: { readonly type: "string"; }; readonly AU: { ...; }; readonly FI: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +29 await db.query2('vote').max('AU', 'FI').groupBy('region').get(), +   ~~~ + +test/aggregate/validation.ts:39:29 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly vote: { props: { readonly region: { readonly type: "string"; }; readonly AU: { ...; }; readonly FI: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +39 await db.query2('vote').avg('AU', 'FI').groupBy('region').get(), +   ~~~ + +test/aggregate/validation.ts:50:29 - error TS2339: Property 'hmean' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly vote: { props: { readonly region: { readonly type: "string"; }; readonly AU: { ...; }; readonly FI: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +50 await db.query2('vote').hmean('AU', 'FI').groupBy('region').get(), +   ~~~~~ + +test/aggregate/validation.ts:121:30 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly movie: { props: { readonly name: { type: "string"; }; readonly year: { ...; }; readonly genre: { ...; }; readonly actors: Omit<...> & { ...; }; readonly movies:...'. + +121 await db.query2('movie').groupBy('year').count().get(), +   ~~~~~~~ + +test/aggregate/validation.ts:134:30 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly movie: { props: { readonly name: { type: "string"; }; readonly year: { ...; }; readonly genre: { ...; }; readonly actors: Omit<...> & { ...; }; readonly movies:...'. + +134 await db.query2('movie').groupBy('genre').min('year').get(), +   ~~~~~~~ + +test/alias/alias.ts:43:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly externalId: { ...; }; readonly potato: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +43 deepEqual(await db.query2('user').filter('externalId', '=', 'cool').get(), [ +   ~~~~~~ + +test/alias/alias.ts:52:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly externalId: { ...; }; readonly potato: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +52 await db.query2('user').filter('externalId', 'includes', 'cool').get(), +   ~~~~~~ + +test/alias/alias.ts:156:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly email: { ...; }; readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; };...'. + +156 await db.query2('user').include('email', 'friends').get(), +   ~~~~~~~ + +test/alias/alias.ts:191:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly email: { ...; }; readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; };...'. + +191 await db.query2('user').include('friends', 'email').get(), +   ~~~~~~~ + +test/alias/alias.ts:210:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly email: { ...; }; readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; };...'. + +210 .filter('email', 'includes', '2', { lowerCase: true }) +   ~~~~~~ + +test/alias/alias.ts:545:48 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "alias"; }; readonly role: { ...; } & { ...; }; }; }; readonly role: { ...; }; }; locales: Partial<...>; }, ... 5 more ....'. + +545 deepEqual(await db.query2('role', adminRole).include('name', 'users').get(), { +   ~~~~~~~ + +test/alias/alias.ts:605:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "alias"; }; readonly role: { ...; } & { ...; }; readonly projects: { ...; }; }; }; readonly project: { ...; }; readonly ...'. + +605 .include('id') +   ~~~~~~~ + +test/alias/alias.ts:615:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "alias"; }; readonly role: { ...; } & { ...; }; readonly projects: { ...; }; }; }; readonly project: { ...; }; readonly ...'. + +615 .include('name', 'users', 'users.$role') +   ~~~~~~~ + +test/alias/filter.ts:33:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly plot: { props: { readonly uuid: { type: "alias"; }; readonly slug: { ...; }; readonly name: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; },...'. + +33 .filter('age', '>', 10) +   ~~~~~~ + +test/alignModify.ts:45:24 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +45 const res = await db.query2('user').include('friends', 'str').get() +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/based-client/addSpecs.ts:57:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +57 client.query2('cookie').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/authorize.ts:139:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +139 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/authorize.ts:156:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +156 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/authorize.ts:197:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +197 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/authorize.ts:242:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +242 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/authorizeOnSpec.ts:94:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +94 await client.query2('slax').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/browser/index.ts:78:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +78 client.query2('counter').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/browser/index.ts:89:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +89 client.query2('text').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/dbQuery.ts:32:23 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +32 return db.query2('user').subscribe(update) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/based-client/dbQuery.ts:38:23 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +38 return db.query2('user').get() +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/based-client/dbQuery.ts:73:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +73 client.query2('users').subscribe((res) => nextResolve?.(res)) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/dbQuery.ts:74:13 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +74 clientOld.query2('users').subscribe((res) => nextResolveOld?.(res)) +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/error.ts:140:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +140 coreClient.query2('counter', {}).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/error.ts:177:18 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +177 coreClient.query2('errorTimer', {}).subscribe(() => {}, reject), +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/functions.ts:3:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +3 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/functionsPerf.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/get.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/get.ts:3:44 - error TS2307: Cannot find module '@based/errors' or its corresponding type declarations. + +3 import { BasedError, BasedErrorCode } from '@based/errors' +   ~~~~~~~~~~~~~~~ + +test/based-client/get.ts:36:37 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +36 const bla = await based.query2('any', payload).get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:83:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +83 return based.query2('checkPayload', payload).get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:108:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +108 coreClient.query2('any', 'xxx').subscribe((res) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:113:33 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +113 const res1 = await coreClient.query2('any', 'xxx').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:115:33 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +115 const res2 = await coreClient.query2('any', 'xxx').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:133:32 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +133 const str = await coreClient.query2('any', 'xxx').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:137:32 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +137 const num = await coreClient.query2('any', 19).get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:141:37 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +141 const boolTrue = await coreClient.query2('any', true).get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:145:38 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +145 const boolFalse = await coreClient.query2('any', false).get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:151:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +151 .query2('checkPayload', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:172:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +172 t.is(await coreClient.query2('counter').get(), 0) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:176:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +176 t.is(await coreClient.query2('counter').get(), 0) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:181:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +181 t.is(await coreClient.query2('counter').get(), 0) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:188:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +188 t.is(await coreClient.query2('counter-cached').get(), 0) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:189:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +189 t.is(await coreClient.query2('counter-cached').get(), 0) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:222:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +222 coreClient.query2('counter').get(), +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:229:37 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +229 await t.notThrowsAsync(coreClient.query2('counter').get()) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/get.ts:270:26 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +270 const g = await client.query2('flap').getWhen((d) => d.status) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/hooks.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/hooks.ts:112:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +112 return based.query2('myobs').get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/hooks.ts:119:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +119 return based.query2('myobs').subscribe(update) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/hooks.ts:138:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +138 const close = client.query2('myobs', { bla: true }).subscribe(() => {}) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/hooks.ts:149:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +149 await client.query2('myobs').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/hooks.ts:157:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +157 const close2 = client.query2('myobs2', { bla: true }).subscribe(() => {}) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/http.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/httpGet.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/installFunctions.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/lazyConnect.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/lazyConnect.ts:73:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +73 const close = client.query2('cookie').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/memLeaks.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/messages.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/messages.ts:62:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +62 const close = client.query2('counter').subscribe(() => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedChannelSimple.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/nestedFunctions.ts:3:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +3 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/nestedFunctions.ts:33:29 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +33 const closeX = coreClient.query2('counter').subscribe(() => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:45:28 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +45 const close = coreClient.query2('obsWithNested').subscribe(() => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:50:29 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +50 const close2 = coreClient.query2('obsWithNested', 'json').subscribe(() => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:56:32 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +56 const bla = await coreClient.query2('obsWithNested', 'json').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:67:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +67 .query2('obsWithNestedLvl2', 'glurk') +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:72:33 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +72 const bla2 = await coreClient.query2('obsWithNestedLvl2', 'glakkel').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:109:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +109 return based.query2('obsWithNested', 'json').subscribe(update) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:118:16 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +118 .query2(payload === 'json' ? 'objectCounter' : 'counter', payload) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:167:25 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +167 await based.query2('obsWithNested', 'json', context).get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:202:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +202 return based.query2('obsWithNested', 'json').subscribe(update) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:211:16 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +211 .query2(payload === 'json' ? 'objectCounter' : 'counter', payload) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctions.ts:260:25 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +260 await based.query2('obsWithNested', 'json').get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctionsError.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/nestedFunctionsError.ts:93:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +93 return based.query2('blabla').subscribe(update) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedFunctionsError.ts:108:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +108 client.query2('hello').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedQuerySimple.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/nestedQuerySimple.ts:47:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +47 return based.query2('nested').subscribe((r) => { +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedQuerySimple.ts:58:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +58 client.query2('bla').subscribe(() => {}) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/nestedQuerySimple.ts:60:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +60 client.query2('bla', { x: 1 }).subscribe(() => {}) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/null.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/null.ts:46:22 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +46 return b.query2('null').subscribe(update) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/null.ts:60:28 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +60 const val = await client.query2('null').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/null.ts:66:29 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +66 const val2 = await client.query2('nestedNull').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/null.ts:71:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +71 const close = client.query2('null').subscribe((v) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/payloadPerf.ts:85:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +85 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/payloadPerf.ts:110:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +110 .query2('counterUint8', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/persist.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/persist.ts:73:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +73 .query2( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/persist.ts:85:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +85 .query2( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/persist.ts:111:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +111 .query2( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/persist.ts:128:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +128 .query2( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:3:34 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +3 import { wait, readStream } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/protocolContentType.ts:245:12 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +245 client.query2('errorQuery').subscribe((d, err) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:248:15 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +248 clientOld.query2('errorQuery').subscribe((d, err) => { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:251:12 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +251 client.query2('nullQuery').subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:254:15 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +254 clientOld.query2('nullQuery').subscribe((d) => { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:257:12 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +257 client.query2('undefinedQuery').subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:260:15 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +260 clientOld.query2('undefinedQuery').subscribe((d) => { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:263:12 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +263 client.query2('numberQuery').subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:266:15 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +266 clientOld.query2('numberQuery').subscribe((d) => { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:270:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +270 .query2('stringQuery', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:277:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +277 .query2('stringQuery', { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:284:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +284 .query2('bigStringQuery', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:291:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +291 .query2('bigStringQuery', { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:298:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +298 .query2('flap', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:305:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +305 .query2('flap', { +   ~~~~~~ + + node_modules/@based/client-old/dist/src/index.d.ts:141:5 + 141 query(name: string, payload?: any, opts?: QueryOptions): BasedQuery; +    ~~~~~ + 'query' is declared here. + +test/based-client/protocolContentType.ts:312:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +312 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/query.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/query.ts:51:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +51 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/query.ts:59:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +59 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCache.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryCache.ts:54:8 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +54 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCache.ts:68:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +68 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryCtxBound.ts:70:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +70 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:108:18 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +108 close = client.query2('counter', 'error').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:121:28 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +121 t.deepEqual(await client.query2('counter').get(), { userId: 1, cnt: 0 }) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:122:28 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +122 t.deepEqual(await client.query2('counter', 'bla').get(), { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:127:30 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +127 t.throwsAsync(() => client.query2('counter', 'error').get()) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:187:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +187 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:280:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +280 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:318:18 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +318 close = client.query2('counter', 'error').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:331:28 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +331 t.deepEqual(await client.query2('counter').get(), { userId: 1, cnt: 0 }) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:332:28 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +332 t.deepEqual(await client.query2('counter', 'bla').get(), { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:337:30 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +337 t.throwsAsync(() => client.query2('counter', 'error').get()) +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:417:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +417 const close = client.query2('counter').subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:421:26 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +421 const close2 = client2.query2('counter').subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:477:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +477 return based.query2('nest', payload, ctx).subscribe(update) +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:492:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +492 const close = client.query2('counter').subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:545:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +545 return based.query2('nest', payload, ctx).get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/queryCtxBound.ts:613:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +613 return based.query2('nest', payload, ctx).get() +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/queryDiff.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryDiff.ts:62:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +62 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryErrorHandling.ts:46:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +46 return based.query2('nested').subscribe( +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:58:26 - error TS2551: Property 'query2' does not exist on type 'BasedFunctionClient'. Did you mean 'query'? + +58 return based.query2('nested').subscribe(async (r) => { +   ~~~~~~ + + src/functions/client.ts:17:12 + 17 abstract query( +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:72:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +72 client.query2('bla').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:78:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +78 client.query2('bla', { x: 1 }).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:84:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +84 client.query2('bla', new Uint8Array(1000)).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:89:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +89 client.query2('asyncBla', new Uint8Array(1000)).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:94:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +94 client.query2('asyncBla', new Uint8Array(1000)).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:145:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +145 const close = client.query2('bla', { x: 1 }).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryErrorHandling.ts:221:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +221 const close = client.query2('bla', { x: 1 }).subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryInstancePerf.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryInstancePerf.ts:76:10 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +76 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryReusedDiff.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryReusedDiff.ts:62:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +62 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryReusedDiff.ts:73:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +73 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryUint8Payload.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/queryUint8Payload.ts:66:24 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +66 const close = client.query2('counter', flap).subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryUint8Payload.ts:70:25 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +70 const close2 = client.query2('counter', flap2).subscribe((d) => { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/queryUint8Payload.ts:84:26 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +84 const x = await client.query2('bla', flap2).get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/rateLimit.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/reEvaluateAuthState.ts.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/reEvaluateAuthState.ts.ts:83:14 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +83 client.query2('counter').subscribe( +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/relay.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/relay.ts:125:26 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +125 const x = await client.query2('counter').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/relay.ts:142:30 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +142 const count = await client.query2('flap').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/reload.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/ssr.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/ssr.ts:51:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +51 await client.query2('counter').get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/ssr.ts:52:16 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +52 await client.query2('counter', { bla: true }).get() +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/ssr.ts:71:12 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +71 client.query2('counter'), +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/stream.ts:2:34 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait, readStream } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/streamChunks.ts:4:28 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +4 import { readStream } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/streamHttp.ts:2:34 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait, readStream } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/streamNested.ts:2:34 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { readStream, wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/throttle.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/throttle.ts:54:6 - error TS2551: Property 'query2' does not exist on type 'BasedClient'. Did you mean 'query'? + +54 .query2('counter', { +   ~~~~~~ + + src/client/index.ts:389:3 + 389 query(name: string, payload?: any, opts?: QueryOptions): BasedClientQuery { +    ~~~~~ + 'query' is declared here. + +test/based-client/verifyAuthState.ts:2:22 - error TS2307: Cannot find module '@based/utils' or its corresponding type declarations. + +2 import { wait } from '@based/utils' +   ~~~~~~~~~~~~~~ + +test/based-client/verifyAuthState.ts:80:8 - error TS18048: 'err' is possibly 'undefined'. + +80 t.is(err.message, 'Token is too small') +   ~~~ + +test/bench.perf.ts:54:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +54 db.query2('test').filter('x', '=', 0).range(1, 10_001).get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/bench.perf.ts:67:12 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +67 db.query2('test', i + 1) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/bench.perf.ts:113:34 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +113 client1.query2('test').filter('x', '=', 0).range(1, 10_001).get(), +   ~~~~~~ + +test/bench.perf.ts:128:36 - error TS18047: 'cur' is possibly 'null'. + +128 ).reduce((prev, cur) => prev + cur.length, 0) +   ~~~ + +test/bench.perf.ts:128:40 - error TS2339: Property 'length' does not exist on type '{ id: any; }'. + +128 ).reduce((prev, cur) => prev + cur.length, 0) +   ~~~~~~ + +test/benchmarks/utils.ts:1:25 - error TS2307: Cannot find module '@based/db' or its corresponding type declarations. + +1 import { BasedDb } from '@based/db' +   ~~~~~~~~~~~ + +test/bigNode.perf.ts:37:5 - error TS2353: Object literal may only specify known properties, and 'f0' does not exist in type '{ ref?: number | BasedModify | { id: number | BasedModify; } | null | undefined; }'. + +37 f0: 10, +   ~~ + +test/bigNode.perf.ts:46:5 - error TS2353: Object literal may only specify known properties, and 'f0' does not exist in type '{ ref?: number | BasedModify | { id: number | BasedModify; } | null | undefined; }'. + +46 f0: 10, +   ~~ + +test/bigNode.perf.ts:53:21 - error TS2339: Property 'f4092' does not exist on type '{ id: number; }'. + +53 deepEqual(mega[1].f4092, 1337) +   ~~~~~ + +test/bigNode.perf.ts:54:21 - error TS2339: Property 'f100' does not exist on type '{ id: number; }'. + +54 deepEqual(giga[1].f100, 1337) +   ~~~~ + +test/bigNode.perf.ts:60:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mega: { props: { readonly ref: { readonly type: "reference"; readonly ref: "giga"; readonly prop: "ref"; } & { ...; }; }; }; readonly giga: { ...; }; }; locales...'. + +60 const megaRefQ = await db.query2('mega').include('ref').get() +   ~~~~~~~ + +test/bigNode.perf.ts:63:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mega: { props: { readonly ref: { readonly type: "reference"; readonly ref: "giga"; readonly prop: "ref"; } & { ...; }; }; }; readonly giga: { ...; }; }; locales...'. + +63 const gigaRef = await db.query2('giga').include('ref').get() +   ~~~~~~~ + +test/bigNode.perf.ts:70:50 - error TS2339: Property 'def' does not exist on type '{ id: number; }[]'. + +70 const serializedSchema = serialize(megaInclude.def.readSchema!) +   ~~~ + +test/bigNode.perf.ts:75:17 - error TS2339: Property 'result' does not exist on type '{ id: number; }[]'. + +75 megaInclude.result, +   ~~~~~~ + +test/bigNode.perf.ts:76:17 - error TS2339: Property 'result' does not exist on type '{ id: number; }[]'. + +76 megaInclude.result.byteLength - 4, +   ~~~~~~ + +test/bigNode.perf.ts:82:56 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mega: { props: { readonly ref: { readonly type: "reference"; readonly ref: "giga"; readonly prop: "ref"; } & { ...; }; }; }; readonly giga: { ...; }; }; locales...'. + +82 const megaIncludeSelective = await db.query2('mega').include('f4092').get() +   ~~~~~~~ + +test/binary.ts:21:5 - error TS2322: Type 'Uint32Array' is not assignable to type 'Uint8Array'. + The types of 'filter(...).findLast' are incompatible between these types. + Type '{ (predicate: (value: number, index: number, array: Uint32Array) => value is S, thisArg?: any): S | undefined; (predicate: (value: number, index: number, array: Uint32Array<...>) => unknown, thisArg?: any): number | undefined; }' is not assignable to type '{ (predicate: (value: number, index: number, array: Uint8Array) => value is S, thisArg?: any): S | undefined; (predicate: (value: number, index: number, array: Uint8Array<...>) => unknown, thisArg?: any): number | undefined; }'. + Types of parameters 'predicate' and 'predicate' are incompatible. + Type '(value: number, index: number, array: Uint8Array) => unknown' is not assignable to type '(value: number, index: number, array: Uint32Array) => value is any'. + Signature '(value: number, index: number, array: Uint8Array): unknown' must be a type predicate. + +21 file: new Uint32Array([1, 2, 3, 4]), +   ~~~~ + +test/binary.ts:51:9 - error TS2531: Object is possibly 'null'. + +51 equal((await db.query2('user', id2).get()).file.length, italyBytes.byteLength) +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/binary.ts:69:20 - error TS2531: Object is possibly 'null'. + +69 const checksum = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/binary.ts:69:59 - error TS2339: Property 'checksum' does not exist on type '{ id: number; article: Uint8Array; }'. + +69 const checksum = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~ + +test/binary.ts:75:21 - error TS2531: Object is possibly 'null'. + +75 const checksum2 = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/binary.ts:75:60 - error TS2339: Property 'checksum' does not exist on type '{ id: number; article: Uint8Array; }'. + +75 const checksum2 = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~ + +test/boolean.ts:34:41 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +34 deepEqual(await client.query2('user').filter('isNice', '=', true).get(), [ +   ~~~~~~ + +test/boolean.ts:38:41 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +38 deepEqual(await client.query2('user').filter('isNice').get(), [ +   ~~~~~~ + +test/boolean.ts:42:41 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +42 deepEqual(await client.query2('user').filter('isNice', '=', false).get(), [ +   ~~~~~~ + +test/capped.ts:131:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly latestArticles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +131 deepEqual(await client.query2('user', user).include('**').get(), { +   ~~~~~~~ + +test/cardinality.ts:45:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +45 .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') +   ~~~~~~~ + +test/cardinality.ts:60:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +60 .include('myUniqueValuesCount') +   ~~~~~~~ + +test/cardinality.ts:96:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +96 .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') +   ~~~~~~~ + +test/cardinality.ts:107:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +107 .include('myUniqueValuesCountFromArray') +   ~~~~~~~ + +test/cardinality.ts:121:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +121 .include('myUniqueValuesCount') +   ~~~~~~~ + +test/cardinality.ts:153:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +153 .include('myUniqueValuesCount', 'myUniqueValuesCountFromArray') +   ~~~~~~~ + +test/cardinality.ts:183:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +183 .filter('myUniqueValuesCount', '=', 11) +   ~~~~~~ + +test/cardinality.ts:214:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +214 .filter('id', '>=', 3) +   ~~~~~~ + +test/cardinality.ts:259:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +259 .filter('id', '>=', 3) +   ~~~~~~ + +test/cardinality.ts:279:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +279 .filter('id', '>=', 3) +   ~~~~~~ + +test/cardinality.ts:299:9 - error TS2322: Type 'Uint8Array' is not assignable to type 'string | string[] | undefined'. + Type 'Uint8Array' is missing the following properties from type 'string[]': pop, push, concat, shift, and 6 more. + +299 $undeftokens: xxHash64(ENCODER.encode('lala')), +   ~~~~~~~~~~~~ + +test/cardinality.ts:307:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly myUniqueValuesCount: { ...; }; readonly myUniqueValuesCountFromArray: { ...; }; readonly contri...'. + +307 .filter('id', '>=', 3) +   ~~~~~~ + +test/cardinality.ts:348:30 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly store: { props: { readonly name: { type: "string"; }; readonly visitors: { ...; }; readonly visits: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +348 await db.query2('store').include('visitors').get(), +   ~~~~~~~ + +test/cardinality.ts:365:30 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly store: { props: { readonly name: { type: "string"; }; readonly visitors: { ...; }; readonly visits: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +365 await db.query2('store').include('visitors').get(), +   ~~~~~~~ + +test/cardinality.ts:404:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly store: { props: { readonly name: { type: "string"; }; readonly customers: Omit<...> & { ...; }; }; }; readonly customer: { ...; }; }; locales: Partial<...>; }, ...'. + +404 const pb = await db.query2('customer').include('productsBought').get() +   ~~~~~~~ + +test/cardinality.ts:417:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly store: { props: { readonly name: { type: "string"; }; readonly customers: Omit<...> & { ...; }; }; }; readonly customer: { ...; }; }; locales: Partial<...>; }, ...'. + +417 const pbr = await db.query2('store').include('*', '**').get() +   ~~~~~~~ + +test/clientServer.perf.ts:43:35 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +43 client.query2('user').sort('name').include('name', 'users').get(), +   ~~~~ + +test/clientServer.perf.ts:54:53 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +54 const allUsers1 = await clients[0].query2('user').range(0, 100_000).get() +   ~~~~~ + +test/clientServer.ts:27:14 - error TS2353: Object literal may only specify known properties, and 'name' does not exist in type '{ id: any; }'. + +27 { id: 1, name: 'youzi' }, +   ~~~~ + +test/clientServer.ts:28:14 - error TS2353: Object literal may only specify known properties, and 'name' does not exist in type '{ id: any; }'. + +28 { id: 2, name: 'jamez' }, +   ~~~~ + +test/clientServer.ts:40:14 - error TS2353: Object literal may only specify known properties, and 'age' does not exist in type '{ id: any; }'. + +40 { id: 1, age: 0 }, +   ~~~ + +test/clientServer.ts:41:14 - error TS2353: Object literal may only specify known properties, and 'age' does not exist in type '{ id: any; }'. + +41 { id: 2, age: 0 }, +   ~~~ + +test/clientServer.ts:80:39 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +80 await client1.query2('user', res).include('*', '**').get(), +   ~~~~~~~ + +test/colvec.ts:63:11 - error TS2304: Cannot find name 'db'. + +63 await db +   ~~ + +test/colvec.ts:91:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly col: { readonly blockCapacity: 10000; readonly insertOnly: true; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +91 deepEqual(await client.query2('col').include('str').get(), [ +   ~~~~~~~ + +test/colvec.ts:118:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly col: { readonly blockCapacity: 10000; readonly insertOnly: true; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +118 deepEqual(await client.query2('col').include('str').get(), [ +   ~~~~~~~ + +test/concurrency.perf.ts:46:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly friends: Omit<{ ...; }, "items"> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +46 .include((s) => s('friends').range(0, 10)) +   ~~~~~~~ + +test/concurrency.perf.ts:112:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +112 db.query2('t').search('contribution', 's').include('i').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/copy.ts:63:12 - error TS2339: Property 'copy' does not exist on type 'BasedDb'. + +63 await db.copy('edition', edition1, { +   ~~~~ + +test/copy.ts:73:19 - error TS2339: Property 'copy' does not exist on type 'BasedDb'. + +73 return db.copy('sequence', id, { +   ~~~~ + +test/copy.ts:80:18 - error TS2339: Property 'copy' does not exist on type 'BasedDb'. + +80 db.copy('page', id, { +   ~~~~ + +test/copy.ts:94:6 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +94 .query2('edition') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/crc32c.ts:115:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly transaction: { props: { readonly myHash: { ...; }; }; }; readonly transactionN: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +115 equal(await db.query2('transaction').include('id', 'myHash').get(), [ +   ~~~~~~~ + +test/crc32c.ts:123:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly transaction: { props: { readonly myHash: { ...; }; }; }; readonly transactionN: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +123 await db.query2('transactionN').include('id', 'myNativeMadeHash').get(), +   ~~~~~~~ + +test/db-schema/schemaUpdates.ts:45:50 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +45 const ageSorted = await client2.query2('user').sort('age', 'asc').get() +   ~~~~ + +test/db-schema/schemaUpdates.ts:55:51 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +55 const ageSorted2 = await client1.query2('user').sort('age', 'asc').get() +   ~~~~ + +test/db-schema/schemaUpdates.ts:70:51 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +70 const ageSorted3 = await client1.query2('user').sort('age', 'asc').get() +   ~~~~ + +test/db-schema/schemaUpdates.ts:180:45 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +180 const all = (await client2.query2('user').range(0, 1000_000).get())! +   ~~~~~ + +test/default.ts:46:49 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly friends: Omit<{ ...; }, "items"> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +46 deepEqual(await client.query2('user', userId).include('friends.**').get(), { +   ~~~~~~~ + +test/default.ts:79:49 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly friends: Omit<{ ...; }, "items"> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +79 deepEqual(await client.query2('user', userId).include('friends.**').get(), { +   ~~~~~~~ + +test/default.ts:106:9 - error TS2552: Cannot find name 'clint'. Did you mean 'client'? + +106 await clint.update('user', userId, { +   ~~~~~ + + test/default.ts:13:9 + 13 const client = await testDb(t, { +    ~~~~~~ + 'client' is declared here. + +test/default.ts:112:49 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly friends: Omit<{ ...; }, "items"> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +112 deepEqual(await client.query2('user', userId).include('friends.**').get(), { +   ~~~~~~~ + +test/default.ts:174:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly avatar: { readonly type: "binary"; readonly default: Uint8Array<...>; }; readonly name: { ...; }; readonly flap: { ...; }; }; }; }; lo...'. + +174 await db.query2('user', userId).include('*', '**').get(), +   ~~~~~~~ + +test/default.ts:195:38 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly avatar: { readonly type: "binary"; readonly default: Uint8Array<...>; }; readonly name: { ...; }; readonly flap: { ...; }; }; }; }; lo...'. + +195 await db.query2('user', userId2).include('*', '**').get(), +   ~~~~~~~ + +test/default.ts:249:13 - error TS2322: Type 'string' is not assignable to type 'undefined'. + +249 default: 'default-slug', +   ~~~~~~~ + +test/default.ts:257:13 - error TS2322: Type '{ ref: string; prop: string; }' is not assignable to type 'Omit, keyof Base> | (Omit, keyof Base> & Omit, keyof Base>)'. + Type '{ ref: string; prop: string; }' is not assignable to type 'Omit, keyof Base> & Omit, keyof Base>'. + Property 'type' is missing in type '{ ref: string; prop: string; }' but required in type 'Omit, keyof Base>'. + +257 items: { +   ~~~~~ + + src/schema/schema/reference.ts:34:22 + 34 RequiredIfStrict<{ type: 'reference' }, strict> & { +    ~~~~ + 'type' is declared here. + +test/default.ts:284:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +284 await db.query2('user', userId).include('*', '**').get(), +   ~~~~~~~ + +test/default.ts:317:7 - error TS2353: Object literal may only specify known properties, and 'label' does not exist in type '{ id: any; }'. + +317 label: { en: 'Default Label' }, +   ~~~~~ + +test/delete.perf.ts:58:22 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +58 deepEqual(await db.query2('user').get(), []) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/delete.perf.ts:79:22 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +79 deepEqual(await db.query2('article').get(), []) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/delete.perf.ts:114:22 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +114 deepEqual(await db.query2('article').get(), []) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/delete.ts:39:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly nurp: { props: { readonly email: { readonly type: "string"; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +39 deepEqual(await db.query2('nurp').include('email').get(), [ +   ~~~~~~~ + +test/delete.ts:49:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly nurp: { props: { readonly email: { readonly type: "string"; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +49 deepEqual(await db.query2('user').include('email').get(), []) +   ~~~~~~~ + +test/delete.ts:59:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly nurp: { props: { readonly email: { readonly type: "string"; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +59 deepEqual(await db.query2('nurp').include('email').get(), [ +   ~~~~~~~ + +test/delete.ts:100:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly nurp: { props: { readonly email: { readonly type: "string"; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +100 deepEqual(await db.query2('nurp').include('email').get(), [ +   ~~~~~~~ + +test/delete.ts:150:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly nurp: { props: { readonly email: { readonly type: "string"; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +150 deepEqual(await db.query2('nurp').include('email').get(), [ +   ~~~~~~~ + +test/delete.ts:213:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly age: { ...; }; readonly email: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +213 deepEqual(await client2.query2('user').include('id').get(), [{ id: 2 }]) +   ~~~~~~~ + +test/delete.ts:214:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly age: { ...; }; readonly email: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +214 deepEqual(await client.query2('user').include('id').get(), [{ id: 2 }]) +   ~~~~~~~ + +test/dependent.ts:66:40 - error TS2345: Argument of type 'string' is not assignable to parameter of type '"page" | "sequence" | "edition" | "show" | "item"'. + +66 const len = (await client.query2(type).get()).length +   ~~~~ + + src/db-client/index.ts:112:3 + 112 query2( +    ~~~~~~ + The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible. + +test/dependent.ts:76:32 - error TS2345: Argument of type 'string' is not assignable to parameter of type '"page" | "sequence" | "edition" | "show" | "item"'. + +76 equal((await client.query2(type).get()).length, 0) +   ~~~~ + + src/db-client/index.ts:112:3 + 112 query2( +    ~~~~~~ + The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible. + +test/dependent.ts:108:45 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "parent", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "parent", id: number | (Partial; }, "parent">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "parent">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly child: { ...; }; }; locales: Partial<...>; }, "parent">> & { ...; }'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +108 deepEqual(await client.query2('parent', head).include('**').get(), { +   ~~~~ + + test/dependent.ts:108:45 + 108 deepEqual(await client.query2('parent', head).include('**').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/dependent.ts:108:51 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly parent: { props: { readonly children: Omit<...> & { ...; }; }; }; readonly child: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +108 deepEqual(await client.query2('parent', head).include('**').get(), { +   ~~~~~~~ + +test/dependent.ts:117:45 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "parent", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "parent", id: number | (Partial; }, "parent">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "parent">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly child: { ...; }; }; locales: Partial<...>; }, "parent">> & { ...; }'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +117 deepEqual(await client.query2('parent', head).include('**').get(), { +   ~~~~ + + test/dependent.ts:117:45 + 117 deepEqual(await client.query2('parent', head).include('**').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/dependent.ts:117:51 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly parent: { props: { readonly children: Omit<...> & { ...; }; }; }; readonly child: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +117 deepEqual(await client.query2('parent', head).include('**').get(), { +   ~~~~~~~ + +test/dependent.ts:158:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly human: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 8; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ...'. + +158 deepEqual(await client.query2('human').include('**').get(), [ +   ~~~~~~~ + +test/dependent.ts:201:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly human: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 8; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ...'. + +201 deepEqual(await client.query2('human').include('**').get(), [ +   ~~~~~~~ + +test/dependent.ts:223:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly human: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 8; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ...'. + +223 deepEqual(await client.query2('human').include('**').get(), []) +   ~~~~~~~ + +test/edges/edgeFilterNested.ts:45:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly initiative: { props: { readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +45 .include('name', (q) => +   ~~~~~~~ + +test/edges/edgeFilterNested.ts:62:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly initiative: { props: { readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +62 .include('name', (q) => +   ~~~~~~~ + +test/edges/edgeNumbers.ts:41:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +41 deepEqual(await db.query2('user', user2).include('**').get(), { +   ~~~~~~~ + +test/edges/edges.ts:113:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +113 .include('contributors.$role', 'contributors.$bigString') +   ~~~~~~~ + +test/edges/edges.ts:127:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +127 deepEqual(await db.query2('article').include('contributors.$rating').get(), [ +   ~~~~~~~ + +test/edges/edges.ts:138:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +138 deepEqual(await db.query2('article').include('contributors.$lang').get(), [ +   ~~~~~~~ + +test/edges/edges.ts:149:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +149 deepEqual(await db.query2('article').include('contributors.$on').get(), [ +   ~~~~~~~ + +test/edges/edges.ts:161:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +161 await db.query2('article').include('contributors.$file').get(), +   ~~~~~~~ + +test/edges/edges.ts:190:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +190 .include((s) => +   ~~~~~~~ + +test/edges/edges.ts:214:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +214 .include((s) => +   ~~~~~~~ + +test/edges/edges.ts:246:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +246 .include('contributors.$rating') +   ~~~~~~~ + +test/edges/edges.ts:259:35 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +259 await db.query2('article', 3).include('contributors.$countries.id').get(), +   ~~~~~~~ + +test/edges/edges.ts:279:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +279 .include('contributors') +   ~~~~~~~ + +test/edges/edges.ts:330:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; loca...'. + +330 deepEqual(await db.query2('article').include('author.$role', '*').get(), [ +   ~~~~~~~ + +test/edges/edges.ts:349:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; loca...'. + +349 .include('author.$role', '*') +   ~~~~~~~ + +test/edges/edges.ts:369:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; loca...'. + +369 deepEqual(await db.query2('article').include('author.$msg', '*').get(), [ +   ~~~~~~~ + +test/edges/edges.ts:426:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +426 deepEqual(await db.query2('user', user2).include('**').get(), { +   ~~~~~~~ + +test/edges/edges.ts:442:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +442 deepEqual(await db.query2('user', user1).include('**').get(), { +   ~~~~~~~ + +test/edges/edges.ts:447:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +447 deepEqual(await db.query2('user', user3).include('**').get(), { +   ~~~~~~~ + +test/edges/edges.ts:460:38 - error TS2322: Type '{ update: { id: number; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; $x?: NumInc | undefined; })[] | undefined; update?: (number | BasedModify | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: number; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; $x?: NumInc | undefined; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; $x?: NumInc | undefined; }'. + +460 friends: { update: [{ id: user2, $index: 0 }] }, +   ~~~~~~ + + test/edges/edges.ts:407:11 + 407 friends: { +    ~~~~~~~~~~ + 408 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... + 412 }, +   ~~~~~~~~~~~~~~ + 413 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ bestFriend?: number | BasedModify | { id: number | BasedModify; $x?: NumInc | undefined; } | null | undefined; friends?: { add?: (number | ... 1 more ... | { ...; })[] | undefined; update?: (number | ... 1 more ... | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (nu...' + +test/edges/edges.ts:463:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +463 deepEqual(await db.query2('user', user3).include('**').get(), { +   ~~~~~~~ + +test/edges/edgesMain.ts:60:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +60 .include('contributors.$rdy') +   ~~~~~~~ + +test/edges/edgesMain.ts:100:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +100 .include('name') +   ~~~~~~~ + +test/edges/edgesMain.ts:140:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +140 .include('name') +   ~~~~~~~ + +test/edges/edgesMain.ts:183:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +183 .include('name') +   ~~~~~~~ + +test/edges/edgesMain.ts:258:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +258 .include('contributor.$rdy') +   ~~~~~~~ + +test/edges/edgesMain.ts:275:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +275 deepEqual(await db.query2('article').include('contributor.$rdy').get(), [ +   ~~~~~~~ + +test/edges/edgesMain.ts:339:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +339 .include('contributors.$age') +   ~~~~~~~ + +test/edges/edgesMain.ts:361:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +361 .include('contributors.$age') +   ~~~~~~~ + +test/edges/edgesMain.ts:371:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ......'. + +371 .include('contributors.$plonki') +   ~~~~~~~ + +test/edges/edgesMain.ts:409:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly article: { type: "references"; items: { ...; } & NormalizeEdges<...>; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }...'. + +409 await db.query2('article').include('writer.$age').get(), +   ~~~~~~~ + +test/edges/edgesMain.ts:423:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly article: { type: "references"; items: { ...; } & NormalizeEdges<...>; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }...'. + +423 await db.query2('article').include('writer.$age').get(), +   ~~~~~~~ + +test/edges/edgesMain.ts:429:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly article: { type: "references"; items: { ...; } & NormalizeEdges<...>; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }...'. + +429 await db.query2('article').include('writer.$plonki').get(), +   ~~~~~~~ + +test/edges/edgesReference.ts:58:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly articles: Omit<...> & { ...; }; }; }; readonly country: { ...; }; readonly article: { ...; }; }; l...'. + +58 deepEqual(await db.query2('article').include('contributor.$friend').get(), [ +   ~~~~~~~ + +test/edges/edgesReference.ts:150:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +150 .include('name', 'contributor.$countries') +   ~~~~~~~ + +test/edges/edgesReference.ts:192:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +192 await db.query2('article').include('name', 'contributor.$countries').get() +   ~~~~~~~ + +test/edges/edgesReferences.ts:100:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +100 .include('contributors.$age') +   ~~~~~~~ + +test/edges/edgesReferences.ts:110:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +110 .include('contributors.$friend.name', 'contributors.$friend.location') +   ~~~~~~~ + +test/edges/edgesReferences.ts:131:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly email: { type: "string"; }; readonly name: { ...; }; readonly smurp: { ...; }; readonly articles: Omit<...> & { ...; }; readonly locat...'. + +131 await db.query2('article').include('contributors.$friend').get(), +   ~~~~~~~ + +test/edges/edgesReferences.ts:243:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +243 .include('contributors.id') +   ~~~~~~~ + +test/edges/edgesReferences.ts:255:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +255 .include('contributors.id', 'contributors.$countries.id') +   ~~~~~~~ + +test/edges/edgesReferences.ts:273:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +273 .include('contributors.id', 'contributors.$countries.code') +   ~~~~~~~ + +test/edges/edgesReferences.ts:307:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +307 .include('contributors.id', 'contributors.$countries') +   ~~~~~~~ + +test/edges/edgesReferences.ts:341:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +341 .include((t) => { +   ~~~~~~~ + +test/edges/edgesReferences.ts:379:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +379 .include((t) => { +   ~~~~~~~ + +test/edges/edgesReferences.ts:404:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +404 .include((t) => { +   ~~~~~~~ + +test/edges/edgesReferences.ts:437:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +437 .include((s) => { +   ~~~~~~~ + +test/edges/edgesReferences.ts:469:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +469 .include((s) => { +   ~~~~~~~ + +test/edges/edgesReferences.ts:498:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly country: { props: { readonly code: { readonly type: "string"; readonly maxBytes: 2; }; readonly name: { ...; }; readonly users: Omit<...> & { ...; }; }; }; read...'. + +498 .include((s) => { +   ~~~~~~~ + +test/edges/edgesReferences.ts:565:12 - error TS2339: Property 'save' does not exist on type 'DbClient<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly round: { props: { readonly name: { type: "alias"; }; readonly phases: { ...; }; }; }; readonly sequence: { ...; }; readonly scenario: { ...; }; readonly phase: { ....'. + +565 await db.save() +   ~~~~ + +test/edges/edgesReferences.ts:578:38 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly round: { props: { readonly name: { type: "alias"; }; readonly phases: { ...; }; }; }; readonly sequence: { ...; }; readonly scenario: { ...; }; readonly phase: ...'. + +578 deepEqual(await db.query2('phase').include('scenarios').get(), [ +   ~~~~~~~ + +test/edges/edgesReferences.ts:584:30 - error TS2345: Argument of type '{}' is not assignable to parameter of type 'StrictSchema'. + +584 const db = await testDb(t, {}) +   ~~ + +test/edges/edgesReferences.ts:613:38 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +613 deepEqual(await db.query2('phase').include('scenarios').get(), [ +   ~~~~~~~ + +test/edges/edgesReferences.ts:625:38 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +625 deepEqual(await db.query2('phase').include('scenarios').get(), [ +   ~~~~~~~ + +test/edges/edgeText.ts:39:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +39 deepEqual(await db.query2('user', user2).include('**').get(), { +   ~~~~~~~ + +test/edges/edgeText.ts:55:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +55 deepEqual(await db.query2('user', user1).include('**').get(), { +   ~~~~~~~ + +test/edges/edgeText.ts:60:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +60 deepEqual(await db.query2('user', user3).include('**').get(), { +   ~~~~~~~ + +test/edges/edgeText.ts:73:38 - error TS2322: Type '{ update: { id: number; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; $x?: string | Partial> | undefined; })[] | undefined; update?: (number | ... 1 more ... | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | nul...'. + Types of property 'update' are incompatible. + Type '{ id: number; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; $x?: string | Partial> | undefined; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; $x?: string | Partial> | undefined; }'. + +73 friends: { update: [{ id: user2, $index: 0 }] }, +   ~~~~~~ + + test/edges/edgeText.ts:20:11 +  20 friends: { +    ~~~~~~~~~~ +  21 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... +  25 }, +   ~~~~~~~~~~~~~~ +  26 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ bestFriend?: number | BasedModify | { id: number | BasedModify; $x?: string | Partial> | undefined; } | null | undefined; friends?: { ...; } | ... 2 more ... | undefined; }' + +test/edges/edgeText.ts:75:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +75 deepEqual(await db.query2('user', user3).include('**').get(), { +   ~~~~~~~ + +test/edges/edgeType.ts:92:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly workspace: { props: { readonly name: { type: "string"; }; readonly serviceAccounts: { ...; }; }; }; readonly serviceAccount: { ...; }; }; locales: Partial<...>;...'. + +92 .include( +   ~~~~~~~ + +test/enum.ts:34:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly fancyness: { ...; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +34 deepEqual(await client.query2('user').include('fancyness').get(), [ +   ~~~~~~~ + +test/enum.ts:44:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly fancyness: { ...; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +44 .include('fancyness') +   ~~~~~~~ + +test/enum.ts:57:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly fancyness: { ...; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +57 deepEqual(await client.query2('user').include('fancyness').get(), [ +   ~~~~~~~ + +test/enum.ts:70:7 - error TS2322: Type 'number' is not assignable to type '"fire" | "mid" | "beta" | null | undefined'. + +70 fancyness: 3, +   ~~~~~~~~~ + + test/enum.ts:10:11 +  10 fancyness: { +    ~~~~~~~~~~~~ +  11 type: 'enum', +   ~~~~~~~~~~~~~~~~~~~~~~~~~ + ... +  13 default: 'fire', +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  14 }, +   ~~~~~~~~~~~ + The expected type comes from property 'fancyness' which is declared here on type '{ fancyness?: "fire" | "mid" | "beta" | null | undefined; }' + +test/enum.ts:75:7 - error TS2322: Type '"fond"' is not assignable to type '"fire" | "mid" | "beta" | null | undefined'. + +75 fancyness: 'fond', +   ~~~~~~~~~ + + test/enum.ts:10:11 +  10 fancyness: { +    ~~~~~~~~~~~~ +  11 type: 'enum', +   ~~~~~~~~~~~~~~~~~~~~~~~~~ + ... +  13 default: 'fire', +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  14 }, +   ~~~~~~~~~~~ + The expected type comes from property 'fancyness' which is declared here on type '{ fancyness?: "fire" | "mid" | "beta" | null | undefined; }' + +test/enum.ts:79:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly fancyness: { ...; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +79 deepEqual(await client.query2('user').include('fancyness').get(), [ +   ~~~~~~~ + +test/enum.ts:105:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly review: { props: { readonly score: { readonly type: "enum"; readonly enum: [...]; readonly default: "😐"; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5...'. + +105 deepEqual(await client.query2('review').include('score').get(), [ +   ~~~~~~~ + +test/enum.ts:119:35 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly review: { props: { readonly score: { readonly type: "enum"; readonly enum: [...]; readonly default: "😐"; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5...'. + +119 await client.query2('review').include('score').sort('score', 'desc').get(), +   ~~~~~~~ + +test/errors.ts:23:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly friends: Omit<{ ...; }, "items"> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +23 equal(await db.query2('user').include('friends').get(), [ +   ~~~~~~~ + +test/errors.ts:47:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly friend: { readonly ref: "user"; readonly prop: "friend"; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +47 equal(await db.query2('user').include('friend').get(), [ +   ~~~~~~~ + +test/exists.ts:32:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +32 deepEqual(await db.query2('user').filter('name', 'exists').get(), [ +   ~~~~~~ + +test/exists.ts:39:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +39 deepEqual(await db.query2('user').filter('name', '!exists').get(), [ +   ~~~~~~ + +test/exists.ts:46:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +46 deepEqual(await db.query2('user').filter('friend', '!exists').get(), [ +   ~~~~~~ + +test/exists.ts:57:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +57 deepEqual(await db.query2('user').filter('friends', '!exists').get(), [ +   ~~~~~~ + +test/exists.ts:70:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +70 deepEqual(await db.query2('user').filter('friends', 'exists').get(), [ +   ~~~~~~ + +test/exists.ts:83:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +83 deepEqual(await db.query2('user').filter('friends', 'exists').get(), []) +   ~~~~~~ + +test/exists.ts:94:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<......'. + +94 deepEqual(await db.query2('user').filter('friends', '!exists').get(), [ +   ~~~~~~ + +test/exists.ts:134:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly start: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales...'. + +134 .include('name') +   ~~~~~~~ + +test/exists.ts:145:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly start: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales...'. + +145 .include('name') +   ~~~~~~~ + +test/exists.ts:154:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly start: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales...'. + +154 await db.query2('user').include('name').filter('friends', '!exists').get(), +   ~~~~~~~ + +test/expire.ts:40:10 - error TS2339: Property 'expire' does not exist on type 'DbClientClass<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly token: { props: { readonly name: { type: "string"; }; readonly user: { ...; } & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }>'. + +40 client.expire('token', token1, 1) +   ~~~~~~ + +test/expire.ts:50:16 - error TS2339: Property 'expire' does not exist on type 'DbClientClass<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly token: { props: { readonly name: { type: "string"; }; readonly user: { ...; } & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }>'. + +50 await client.expire('token', token2, 1) +   ~~~~~~ + +test/expire.ts:92:16 - error TS2339: Property 'expire' does not exist on type 'DbClientClass<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; }; }; }; locales: Partial<...>; }>'. + +92 await client.expire('user', id1, 1) +   ~~~~~~ + +test/expire.ts:94:16 - error TS2339: Property 'expire' does not exist on type 'DbClientClass<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; }; }; }; locales: Partial<...>; }>'. + +94 await client.expire('user', id1, 3) +   ~~~~~~ + +test/filter/api.ts:33:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +33 .include('friends') +   ~~~~~~~ + +test/filter/api.ts:46:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +46 .include('friends') +   ~~~~~~~ + +test/filter/api.ts:58:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +58 await db.query2('user').filter('bestFriend', '=', { id: 9 }).get(), +   ~~~~~~ + +test/filter/api.ts:70:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +70 .filter('bestFriend', '=', [{ id: 9 }, { id: 10 }]) +   ~~~~~~ + +test/filter/edges.ts:55:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly team: { props: { readonly name: { type: "string"; }; readonly files: { ...; }; }; }; readonly libraryFile: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +55 .include((q) => +   ~~~~~~~ + +test/filter/edges.ts:73:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly team: { props: { readonly name: { type: "string"; }; readonly files: { ...; }; }; }; readonly libraryFile: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +73 .filter('files', 'exists') +   ~~~~~~ + +test/filter/filter.ts:44:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly status: { type: "enum"; enum: string[]; }; readonly name: { ...; }; readonly x: { ...; }; readonly orgs: Omit<...> & { ...; }; readonly...'. + +44 deepEqual(await db.query2('org').filter('x', '=', x).get(), [ +   ~~~~~~ + +test/filter/filter.ts:52:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly status: { type: "enum"; enum: string[]; }; readonly name: { ...; }; readonly x: { ...; }; readonly orgs: Omit<...> & { ...; }; readonly...'. + +52 deepEqual(await db.query2('org').filter('orgs', '=', [org, org2]).get(), [ +   ~~~~~~ + +test/filter/filter.ts:60:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly status: { type: "enum"; enum: string[]; }; readonly name: { ...; }; readonly x: { ...; }; readonly orgs: Omit<...> & { ...; }; readonly...'. + +60 deepEqual(await db.query2('org').filter('status', '=', 'error').get(), []) +   ~~~~~~ + +test/filter/filter.ts:61:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly status: { type: "enum"; enum: string[]; }; readonly name: { ...; }; readonly x: { ...; }; readonly orgs: Omit<...> & { ...; }; readonly...'. + +61 deepEqual(await db.query2('org').filter('status', '=', 'ok').get(), [ +   ~~~~~~ + +test/filter/filter.ts:75:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly status: { type: "enum"; enum: string[]; }; readonly name: { ...; }; readonly x: { ...; }; readonly orgs: Omit<...> & { ...; }; readonly...'. + +75 deepEqual(await db.query2('org').filter('name', 'includes', '0').get(), []) +   ~~~~~~ + +test/filter/filter.ts:76:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly status: { type: "enum"; enum: string[]; }; readonly name: { ...; }; readonly x: { ...; }; readonly orgs: Omit<...> & { ...; }; readonly...'. + +76 deepEqual(await db.query2('org').filter('name', 'includes', 'hello').get(), [ +   ~~~~~~ + +test/filter/filter.ts:187:33 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +187 (await db.query2('machine').filter('lastPing', '=', x).get()).length, +   ~~~~~~ + +test/filter/filter.ts:211:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +211 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:238:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +238 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:257:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +257 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:268:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +268 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:279:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +279 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:291:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +291 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:302:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +302 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:313:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +313 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:326:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +326 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:339:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +339 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:351:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +351 .include('id') +   ~~~~~~~ + +test/filter/filter.ts:373:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +373 .include('id') +   ~~~~~~~ + +test/filter/filter.ts:409:28 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +409 await db.query2('env').filter('machines', '<', 10).get(), +   ~~~~~~ + +test/filter/filter.ts:428:28 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +428 await db.query2('env').filter('machines', '=', ids).get(), +   ~~~~~~ + +test/filter/filter.ts:443:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +443 .include('env', '*') +   ~~~~~~~ + +test/filter/filter.ts:490:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +490 .filter('status', '=', '🦄') +   ~~~~~~ + +test/filter/filter.ts:501:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +501 deepEqual(await db.query2('env').filter('standby').get(), []) +   ~~~~~~ + +test/filter/filter.ts:507:36 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +507 deepEqual(await db.query2('env').filter('standby').get(), [ +   ~~~~~~ + +test/filter/filter.ts:513:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +513 .include('temperature') +   ~~~~~~~ + +test/filter/filter.ts:526:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly org: { props: { readonly name: { type: "string"; }; readonly type: { ...; }; readonly envs: Omit<...> & { ...; }; }; }; readonly env: { ...; }; readonly machine...'. + +526 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:589:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +589 .include('id', 'lastPing') +   ~~~~~~~ + +test/filter/filter.ts:608:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +608 .include('id', 'lastPing') +   ~~~~~~~ + +test/filter/filter.ts:616:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +616 .include('id', 'lastPing') +   ~~~~~~~ + +test/filter/filter.ts:626:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +626 .include('id', 'lastPing') +   ~~~~~~~ + +test/filter/filter.ts:641:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +641 .include('id', 'lastPing') +   ~~~~~~~ + +test/filter/filter.ts:652:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +652 .include('id', 'lastPing') +   ~~~~~~~ + +test/filter/filter.ts:663:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly derp: { type: "int32"; }; ... 5 more ...; readonly scheduled: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +663 .include('temperature') +   ~~~~~~~ + +test/filter/filter.ts:702:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly temperature: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +702 .include('temperature') +   ~~~~~~~ + +test/filter/filter.ts:734:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly temperature: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +734 .include('id', 'temperature') +   ~~~~~~~ + +test/filter/filter.ts:754:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly temperature: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +754 (await db.query2('machine').include('id').range(0, 3).get()).node(-1), +   ~~~~~~~ + +test/filter/filter.ts:764:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly machine: { props: { readonly temperature: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +764 .include('temperature') +   ~~~~~~~ + +test/filter/filter.ts:815:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly age: { ...; }; readonly bestBud: { ...; } & { ...; }; readonly buddies: Omit<...> & { ......'. + +815 .include('*') +   ~~~~~~~ + +test/filter/filter.ts:823:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly age: { ...; }; readonly bestBud: { ...; } & { ...; }; readonly buddies: Omit<...> & { ......'. + +823 .include( +   ~~~~~~~ + +test/filter/filter.ts:835:12 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly age: { ...; }; readonly bestBud: { ...; } & { ...; }; readonly buddies: Omit<...> & { ......'. + +835 .include( +   ~~~~~~~ + +test/filter/filter.ts:863:39 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly bucket: { props: { readonly red: { type: "uint8"; }; readonly blue: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +863 const b = await db.query2('bucket').filter('red', '<', 4).get() +   ~~~~~~ + +test/filter/or.ts:21:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +21 await db.query2('user').filter('nr', '>', 8).or('nr', '<', 1).get(), +   ~~~~~~ + +test/filter/or.ts:38:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +38 .filter('nr', '>', 8) +   ~~~~~~ + +test/filter/or.ts:63:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +63 .filter('nr', '>', 8) +   ~~~~~~ + +test/filter/or.ts:87:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +87 .filter('nr', '>', 8) +   ~~~~~~ + +test/filter/references.ts:51:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly workspace: { props: { readonly name: { type: "string"; }; readonly users: { ...; }; readonly drones: { ...; }; }; }; readonly user: { ...; }; readonly drone: { ...'. + +51 .include((s) => +   ~~~~~~~ + +test/filter/references.ts:61:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly workspace: { props: { readonly name: { type: "string"; }; readonly users: { ...; }; readonly drones: { ...; }; }; }; readonly user: { ...; }; readonly drone: { ...'. + +61 .filter('workspace.users', 'includes', user) +   ~~~~~~ + +test/filter/referencesField.ts:32:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +32 .include('name', 'age', 'friends') +   ~~~~~~~ + +test/filter/referencesField.ts:49:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +49 .include('name', 'age', 'friends') +   ~~~~~~~ + +test/filter/referencesField.ts:66:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +66 .include('name', 'age', 'friends') +   ~~~~~~~ + +test/filter/referencesField.ts:83:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +83 .include('name', 'age', 'friends') +   ~~~~~~~ + +test/filter/referencesField.ts:104:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +104 .include('name', 'age', 'friends') +   ~~~~~~~ + +test/filter/referencesField.ts:131:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +131 .include('name', 'age', 'friends') +   ~~~~~~~ + +test/filter/string.ts:18:3 - error TS2739: Type 'Map' is missing the following properties from type 'PropTree': props, required, path, schema + +18 tree: new Map(), +   ~~~~ + + src/schema/defs/index.ts:35:3 + 35 tree: PropTree +    ~~~~ + The expected type comes from property 'tree' which is declared here on type 'TypeDef' + +test/filter/string.ts:63:7 - error TS2322: Type 'Uint8Array' is not assignable to type 'string'. + +63 body: compressedItaly, +   ~~~~ + +test/filter/string.ts:74:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +74 .filter('stuff', '=', ENCODER.encode('#' + 2)) +   ~~~~~~ + +test/filter/string.ts:93:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +93 .filter('stuff', 'includes', new Uint8Array([55, 57])) +   ~~~~~~ + +test/filter/string.ts:124:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +124 .filter('derp', 'includes', ENCODER.encode('vitorio')) +   ~~~~~~ + +test/filter/string.ts:136:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +136 .filter('derp', 'includes', ENCODER.encode('xx')) +   ~~~~~~ + +test/filter/string.ts:148:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +148 .filter('derp', 'includes', q) +   ~~~~~~ + +test/filter/string.ts:160:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +160 .filter('derp', '=', largeDerp) +   ~~~~~~ + +test/filter/string.ts:173:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly type: { type: "enum"; enum: ("opinion" | ... 1 more ... | "gossip")[]; }; ... 5 more ...; readonly derp: { ...; }; }; }; readonly i...'. + +173 .filter('body', '=', italy) +   ~~~~~~ + +test/filter/string.ts:198:7 - error TS2322: Type 'Uint8Array' is not assignable to type 'string'. + +198 body: compressedItaly, +   ~~~~ + +test/filter/string.ts:208:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly body: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +208 .filter('body', 'includes', n) +   ~~~~~~ + +test/filter/string.ts:251:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly headline: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +251 .filter('body', 'includes', 'derp derp derp', { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:263:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly headline: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +263 .filter('body', 'includes', 'derp derp derp') +   ~~~~~~ + +test/filter/string.ts:274:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly headline: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +274 .filter('headline', 'includes', 'pager') +   ~~~~~~ + +test/filter/string.ts:293:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly headline: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +293 .filter('headline', 'includes', 'Pager', { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:312:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly headline: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +312 .filter('headline', 'includes', 'refugee', { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:322:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly headline: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +322 .filter('headline', 'includes', 'gaza', { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:358:40 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { readonly type: "binary"; readonly maxBytes: 30; }; readonly stuff: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...'. + +358 deepEqual(await db.query2('article').filter('stuff', '=', stuff).get(), [ +   ~~~~~~ + +test/filter/string.ts:364:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { readonly type: "binary"; readonly maxBytes: 30; }; readonly stuff: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...'. + +364 .filter('derp', 'includes', new Uint8Array([4])) +   ~~~~~~ + +test/filter/string.ts:393:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +393 .filter('body', 'includes', 'aaaaaa', { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:423:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly body: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +423 .filter('body', 'includes', 'aaaaa', { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:460:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefi...'. + +460 .filter('body', 'includes', ['aaaaaaaaaaa', 'bbbbbb'], { +   ~~~~~~ + +test/filter/string.ts:473:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefi...'. + +473 .filter('title', 'includes', ['gaza', 'tubbies'], { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:489:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefi...'. + +489 .filter('title', 'includes', ['crisis', 'refugee'], { lowerCase: true }) +   ~~~~~~ + +test/filter/string.ts:515:7 - error TS2322: Type 'string | Uint8Array' is not assignable to type 'string | null | undefined'. + Type 'Uint8Array' is not assignable to type 'string'. + +515 body: i === amount - 1 ? italy + ' aaabbbbbbbbbaaa' : compressedItaly, +   ~~~~ + +test/filter/string.ts:522:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +522 .filter('body', 'includes', ['aaaaaaaaaaa', 'bbbbbbbb'], { +   ~~~~~~ + +test/filter/string.ts:551:7 - error TS2322: Type 'string | Uint8Array' is not assignable to type 'string | null | undefined'. + Type 'Uint8Array' is not assignable to type 'string'. + +551 body: i === 999 ? derpItaly : compressedItaly, +   ~~~~ + +test/filter/string.ts:559:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly f: { type: "boolean"; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +559 .filter('body', '=', [derpItaly, 'derp', italy]) +   ~~~~~~ + +test/filter/string.ts:588:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly italy: { props: { readonly body: { readonly type: "string"; readonly maxBytes: 5; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +588 .filter('body', '=', ['xx', 'bb']) +   ~~~~~~ + +test/filter/string.ts:640:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly ent: { props: { readonly city: { readonly type: "string"; readonly maxBytes: 15; }; readonly country: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +640 .filter('country', 'includes', ['Italy', 'Germany']) +   ~~~~~~ + +test/filter/string.ts:666:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly potato: { type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +666 await db.query2('user').filter('potato', '=', '').get(), +   ~~~~~~ + +test/filter/string.ts:700:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly region: { readonly type: "string"; readonly maxBytes: 15; }; readonly potato: { ...; }; readonly city: { ...; }; }; }; }; locales: Par...'. + +700 await db.query2('user').filter('potato', '!=', '').get(), +   ~~~~~~ + +test/filter/string.ts:711:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly region: { readonly type: "string"; readonly maxBytes: 15; }; readonly potato: { ...; }; readonly city: { ...; }; }; }; }; locales: Par...'. + +711 .filter('potato', '!=', '') +   ~~~~~~ + +test/filter/string.ts:722:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly region: { readonly type: "string"; readonly maxBytes: 15; }; readonly potato: { ...; }; readonly city: { ...; }; }; }; }; locales: Par...'. + +722 .filter('potato', '=', '') +   ~~~~~~ + +test/HLLunion.ts:86:6 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +86 .query2('user') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/hooks.ts:67:7 - error TS2322: Type 'undefined' is not assignable to type 'number'. + +67 age: undefined, +   ~~~ + + src/db-client/query2/types.ts:42:20 + 42 type Prettify = { +    ~ + 43 [K in keyof T]: T[K] +   ~~~~~~~~~~~~~~~~~~~~~~ + 44 } & {} +   ~ + The expected type comes from property 'age' which is declared here on type '{ id: number; name: string; age: number; defined: InferType<{ readonly age: { type: "boolean"; }; }, { readonly user: { readonly hooks: { readonly create: (payload: Record) => void; readonly update: (payload: Record) => void; readonly read: (result: Record<...>) => void; readonly include: (...' + +test/hooks.ts:69:5 - error TS2741: Property 'defined' is missing in type '{ id: number; name: string; age: number; }' but required in type '{ id: number; name: string; age: number; defined: InferType<{ readonly age: { type: "boolean"; }; }, { readonly user: { readonly hooks: { readonly create: (payload: Record) => void; readonly update: (payload: Record) => void; readonly read: (result: Record<...>) => void; readonly include: (...'. + + 69 { +   ~ + 70 id: 2, +  ~~~~~~~~~~~~ +... + 72 age: 25, +  ~~~~~~~~~~~~~~ + 73 }, +  ~~~~~ + +test/hooks.ts:76:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { readonly hooks: { readonly create: (payload: Record) => void; readonly update: (payload: Record<...>) => void; readonly read: (result: Reco...'. + +76 deepEqual(await db.query2('user').filter('age', '<', 50).get(), [ +   ~~~~~~ + +test/hooks.ts:89:5 - error TS2741: Property 'defined' is missing in type '{ id: number; name: string; age: number; }' but required in type '{ id: number; name: string; age: number; defined: InferType<{ readonly age: { type: "boolean"; }; }, { readonly user: { readonly hooks: { readonly create: (payload: Record) => void; readonly update: (payload: Record) => void; readonly read: (result: Record<...>) => void; readonly include: (...'. + + 89 { +   ~ + 90 id: 1, +  ~~~~~~~~~~~~ +... + 92 age: 31, +  ~~~~~~~~~~~~~~ + 93 }, +  ~~~~~ + +test/hooks.ts:94:5 - error TS2741: Property 'defined' is missing in type '{ id: number; name: string; age: number; }' but required in type '{ id: number; name: string; age: number; defined: InferType<{ readonly age: { type: "boolean"; }; }, { readonly user: { readonly hooks: { readonly create: (payload: Record) => void; readonly update: (payload: Record) => void; readonly read: (result: Record<...>) => void; readonly include: (...'. + + 94 { +   ~ + 95 id: 2, +  ~~~~~~~~~~~~ +... + 97 age: 25, +  ~~~~~~~~~~~~~~ + 98 }, +  ~~~~~ + +test/hooks.ts:101:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { readonly hooks: { readonly create: (payload: Record) => void; readonly update: (payload: Record<...>) => void; readonly read: (result: Reco...'. + +101 deepEqual(await db.query2('user').filter('age', '<', 50).get(), [ +   ~~~~~~ + +test/hooks.ts:152:5 - error TS2739: Type '{ id: number; private: true; }' is missing the following properties from type '{ id: number; name: string; age: number; private: boolean; }': name, age + +152 { +   ~ +153 id: 1, +  ~~~~~~~~~~~~ +154 private: true, +  ~~~~~~~~~~~~~~~~~~~~ +155 }, +  ~~~~~ + +test/hooks.ts:325:7 - error TS2353: Object literal may only specify known properties, and 'powerful' does not exist in type '{ id: number; name: string; age: number; city: string; }'. + +325 powerful: true, +   ~~~~~~~~ + +test/hooks.ts:370:34 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { readonly hooks: { readonly aggregate: (query: any) => void; }; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +370 equal((await db.query2('user').sum('age').get()).age.sum, 21) +   ~~~ + +test/hooks.ts:412:34 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { readonly hooks: { readonly search: (query: any) => void; }; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +412 equal((await db.query2('user').search('youzi').get()).length, 1) +   ~~~~~~ + +test/hooks.ts:454:33 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { readonly hooks: { readonly groupBy: (query: any) => void; }; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +454 equal(await db.query2('user').groupBy('name').sum('age').get(), { +   ~~~~~~~ + +test/hooks.ts:460:20 - error TS2554: Expected 2-3 arguments, but got 1. + +460 const db = await testDb({ +   ~~~~~~ + + test/shared/index.ts:40:3 + 40 schema: StrictSchema, +    ~~~~~~~~~~~~~~~~~~~~~~~ + An argument for 'schema' was not provided. + +test/hooks.ts:502:33 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +502 equal(await db.query2('user').filter('name', '=', 'youzi').get(), [ +   ~~~~~~ + +test/hooks.ts:546:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { readonly hooks: { readonly include: (query: any) => void; }; props: { ...; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +546 equal(await db.query2('user').include('name', 'age').get(), [ +   ~~~~~~~ + +test/include/include.ts:26:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +26 await db.query2('user').include([]).range(0, 5).get(), +   ~~~~~~~ + +test/include/include.ts:41:9 - error TS2531: Object is possibly 'null'. + +41 equal((await db.query2('user', 1).get()).id, 1) +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/include/include.ts:43:41 - error TS2339: Property 'checksum' does not exist on type '{ id: number; nr: number; }[]'. + +43 equal((await db.query2('user').get()).checksum, 2149520223) +   ~~~~~~~~ + +test/include/include.ts:44:41 - error TS2339: Property 'version' does not exist on type '{ id: number; nr: number; }[]'. + +44 equal((await db.query2('user').get()).version, 4507870634704934) +   ~~~~~~~ + +test/include/include.ts:69:29 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly a: { readonly type: "string"; readonly maxBytes: 10; }; readonly b: { ...; }; readonly c: { ...; }; readonly d: { ...; }; }; }; }; loc...'. + +69 await db.query2('user').range(0, 5).get(), +   ~~~~~ + +test/include/includeMeta.ts:39:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +39 deepEqual(await db.query2('item').include('name', { meta: true }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:54:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +54 deepEqual(await db.query2('item').include('name', { meta: true }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:71:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +71 deepEqual(await db.query2('item').include('name', { meta: 'only' }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:97:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +97 await db.query2('item').include('items.$edgeName', { meta: 'only' }).get(), +   ~~~~~~~ + +test/include/includeMeta.ts:131:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +131 deepEqual(await db.query2('item').include('email', { meta: 'only' }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:147:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +147 deepEqual(await db.query2('item').include('email', { meta: true }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:166:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +166 deepEqual(await db.query2('item').include('name', { meta: true }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:183:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +183 deepEqual(await db.query2('item').include('body', { meta: true }).get(), [ +   ~~~~~~~ + +test/include/includeMeta.ts:219:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +219 await db.query2('item').include('body', { meta: true }).get(), +   ~~~~~~~ + +test/include/includeMeta.ts:258:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +258 await db.query2('item').include('body', { meta: 'only' }).get(), +   ~~~~~~~ + +test/include/includeMeta.ts:296:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +296 .include('body', { meta: 'only' }) +   ~~~~~~~ + +test/include/includeMeta.ts:319:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly name: { type: "string"; }; readonly body: { ...; }; readonly email: { ...; }; readonly items: Omit<...> & { ...; }; }; }; }; locales: ...'. + +319 .include('body', { meta: 'only' }) +   ~~~~~~~ + +test/include/includeNested.ts:26:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +26 await db.query2('user').include('*', '**').range(0, 5).get(), +   ~~~~~~~ + +test/include/includeNested.ts:60:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +60 await db.query2('user').include('friends.*').range(0, 5).get(), +   ~~~~~~~ + +test/include/includeSlice.ts:58:9 - error TS18047: 'q' is possibly 'null'. + +58 equal(q.id, 1) +   ~ + +test/include/includeSlice.ts:63:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly x: { type: "uint32"; }; ... 11 more ...; readonly bigBoyString: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +63 .include('name', { +   ~~~~~~~ + +test/include/includeSlice.ts:121:34 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly x: { type: "uint32"; }; ... 11 more ...; readonly bigBoyString: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +121 await db.query2('item', id1).include('body', { end: 3 }).get(), +   ~~~~~~~ + +test/include/includeSlice.ts:129:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly x: { type: "uint32"; }; ... 11 more ...; readonly bigBoyString: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +129 .include('body.fi', { end: 3 }, 'body.en', { end: 3 }) +   ~~~~~~~ + +test/include/includeSlice.ts:136:34 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly x: { type: "uint32"; }; ... 11 more ...; readonly bigBoyString: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +136 await db.query2('item', id1).include('body', { end: 3 }).locale('en').get(), +   ~~~~~~~ + +test/include/includeSlice.ts:144:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly x: { type: "uint32"; }; ... 11 more ...; readonly bigBoyString: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +144 .include('body', { end: 4, bytes: true }) +   ~~~~~~~ + +test/include/includeSlice.ts:154:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly item: { props: { readonly x: { type: "uint32"; }; ... 11 more ...; readonly bigBoyString: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +154 .include('body.en', { end: 3 }, 'body.fi') +   ~~~~~~~ + +test/include/referencesField.ts:34:34 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +34 await db.query2('user', mrA).include('name', 'age', 'friends[0].age').get(), +   ~~~~~~~ + +test/include/referencesField.ts:40:29 - error TS2339: Property 'at' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +40 await db.query2('user').at(0).get(), +   ~~ + +test/include/referencesField.ts:46:29 - error TS2339: Property 'at' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly age: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mo...'. + +46 await db.query2('user').at(3).get(), +   ~~ + +test/include/thread.perf.ts:79:17 - error TS2345: Argument of type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly simple: { props: { readonly nr: { type: "uint32"; }; readonly start: { ...; }; readonly end: { ...; }; readonly target: { ...; } & { ...; }; }; }; }; locales: P...' is not assignable to parameter of type 'BasedDbQuery'. + Type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly simple: { props: { readonly nr: { type: "uint32"; }; readonly start: { ...; }; readonly end: { ...; }; readonly target: { ...; } & { ...; }; }; }; }; locales: P...' is missing the following properties from type 'BasedDbQuery': target, readSchema, at, reset, and 21 more. + +79 registerQuery(x) +   ~ + +test/include/thread.perf.ts:99:10 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly simple: { props: { readonly nr: { type: "uint32"; }; readonly start: { ...; }; readonly end: { ...; }; readonly target: { ...; } & { ...; }; }; }; }; locales: P...'. + +99 .range(0, 5e6 + i) +   ~~~~~ + +test/include/thread.perf.ts:167:14 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly simple: { props: { readonly nr: { type: "uint32"; }; readonly start: { ...; }; readonly end: { ...; }; readonly target: { ...; } & { ...; }; }; }; }; locales: P...'. + +167 .include('id', 'nr') +   ~~~~~~~ + +test/include/thread.perf.ts:186:14 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly simple: { props: { readonly nr: { type: "uint32"; }; readonly start: { ...; }; readonly end: { ...; }; readonly target: { ...; } & { ...; }; }; }; }; locales: P...'. + +186 .include('id', 'nr') +   ~~~~~~~ + +test/include/thread.perf.ts:206:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly simple: { props: { readonly nr: { type: "uint32"; }; readonly start: { ...; }; readonly end: { ...; }; readonly target: { ...; } & { ...; }; }; }; }; locales: P...'. + +206 .include('nr') // 'start', 'end', 'target' +   ~~~~~~~ + +test/include/thread.perf.ts:324:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +324 .query2('user') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/isModified.perf.ts:23:30 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly nr: { type: "uint32"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +23 q.push(db.query2('user').range(0, 5).get()) +   ~~~~~ + +test/json.ts:82:20 - error TS2531: Object is possibly 'null'. + +82 const checksum = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/json.ts:82:59 - error TS2339: Property 'checksum' does not exist on type '{ id: number; article: any; }'. + +82 const checksum = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~ + +test/json.ts:88:21 - error TS2531: Object is possibly 'null'. + +88 const checksum2 = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/json.ts:88:60 - error TS2339: Property 'checksum' does not exist on type '{ id: number; article: any; }'. + +88 const checksum2 = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~ + +test/locales.ts:39:7 - error TS2322: Type 'null' is not assignable to type 'string'. + +39 string: null, +   ~~~~~~ + + src/db-client/query2/types.ts:42:20 + 42 type Prettify = { +    ~ + 43 [K in keyof T]: T[K] +   ~~~~~~~~~~~~~~~~~~~~~~ + 44 } & {} +   ~ + The expected type comes from property 'string' which is declared here on type '{ string: string; id: number; text: { id: string; none: string; aa: string; ab: string; af: string; ak: string; sq: string; am: string; ar: string; an: string; hy: string; as: string; av: string; ae: string; ay: string; ... 131 more ...; cnr: string; }; }' + +test/locales.ts:40:7 - error TS2740: Type '{ [k: string]: null; }' is missing the following properties from type '{ id: string; none: string; aa: string; ab: string; af: string; ak: string; sq: string; am: string; ar: string; an: string; hy: string; as: string; av: string; ae: string; ay: string; az: string; eu: string; be: string; ... 128 more ...; cnr: string; }': id, none, aa, ab, and 143 more. + +40 text: Object.fromEntries(Object.keys(thing.text).map((l) => [l, null])), +   ~~~~ + + src/db-client/query2/types.ts:42:20 + 42 type Prettify = { +    ~ + 43 [K in keyof T]: T[K] +   ~~~~~~~~~~~~~~~~~~~~~~ + 44 } & {} +   ~ + The expected type comes from property 'text' which is declared here on type '{ string: string; id: number; text: { id: string; none: string; aa: string; ab: string; af: string; ak: string; sq: string; am: string; ar: string; an: string; hy: string; as: string; av: string; ae: string; ay: string; ... 131 more ...; cnr: string; }; }' + +test/mem.ts:57:12 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly data: { props: { readonly a: { readonly ref: "data"; readonly prop: "b"; readonly $derp: { ...; }; } & { ...; }; readonly b: Omit<...> & { ...; }; readonly age:...'. + +57 .include('b') +   ~~~~~~~ + +test/mem.ts:72:36 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly data: { props: { readonly a: { readonly ref: "data"; readonly prop: "b"; readonly $derp: { ...; }; } & { ...; }; readonly b: Omit<...> & { ...; }; readonly age:...'. + +72 (await client.query2('data').range(0, 10e6).get()).length, +   ~~~~~ + +test/migration.ts:227:26 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +227 const users = await db.query2('user').get() +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/migration.ts:228:27 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +228 const people = await db.query2('person').include('*', '**').get() +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/modify/props/binary.ts:63:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly blob: { type: "binary"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +63 const res1 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() +   ~~~~~~~ + +test/modify/props/binary.ts:75:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly blob: { type: "binary"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +75 const res2 = await db.query2('holder', id1).include('toThing.$edgeBlob').get() +   ~~~~~~~ + +test/modify/props/boolean.ts:94:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +94 .include('toUser.$edgeBool') +   ~~~~~~~ + +test/modify/props/boolean.ts:99:45 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +99 const resB = await db.query2('holder', b).include('toUser.$edgeBool').get() +   ~~~~~~~ + +test/modify/props/boolean.ts:106:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +106 .include('toUser.$edgeBool') +   ~~~~~~~ + +test/modify/props/boolean.ts:118:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +118 .include('toUser.$edgeBool') +   ~~~~~~~ + +test/modify/props/boolean.ts:122:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +122 const resB2 = await db.query2('holder', b).include('toUser.$edgeBool').get() +   ~~~~~~~ + +test/modify/props/boolean.ts:127:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +127 .include('toUser.$edgeBool') +   ~~~~~~~ + +test/modify/props/boolean.ts:139:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +139 .include('toUser.$edgeBool') +   ~~~~~~~ + +test/modify/props/boolean.ts:143:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +143 const resB3 = await db.query2('holder', b).include('toUser.$edgeBool').get() +   ~~~~~~~ + +test/modify/props/boolean.ts:148:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ......'. + +148 .include('toUser.$edgeBool') +   ~~~~~~~ + +test/modify/props/cardinality.ts:74:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly counter: { type: "cardinality"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 mo...'. + +74 .include('toThing.$edgeCounter') +   ~~~~~~~ + +test/modify/props/cardinality.ts:88:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly counter: { type: "cardinality"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 mo...'. + +88 .include('toThing.$edgeCounter') +   ~~~~~~~ + +test/modify/props/default.ts:102:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly groups: { ...; }; }; }; readonly group: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +102 .include('member.$role') +   ~~~~~~~ + +test/modify/props/default.ts:124:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly groups: { ...; }; }; }; readonly group: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +124 .include('member.$role') +   ~~~~~~~ + +test/modify/props/default.ts:145:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly groups: { ...; }; }; }; readonly group: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +145 .include('member.$role') +   ~~~~~~~ + +test/modify/props/enum.ts:65:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly option: { readonly enum: [...]; } & { ...; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; ...'. + +65 .include('toThing.$edgeOption') +   ~~~~~~~ + +test/modify/props/enum.ts:79:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly option: { readonly enum: [...]; } & { ...; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; ...'. + +79 .include('toThing.$edgeOption') +   ~~~~~~~ + +test/modify/props/json.ts:66:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly data: { type: "json"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., un...'. + +66 const res1 = await db.query2('holder', id1).include('toThing.$edgeData').get() +   ~~~~~~~ + +test/modify/props/json.ts:78:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly data: { type: "json"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., un...'. + +78 const res2 = await db.query2('holder', id1).include('toThing.$edgeData').get() +   ~~~~~~~ + +test/modify/props/mixed.ts:41:48 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 4 more ...; readonly test: { ...; }; }; }; readonly typeTest: { ...; }; }; locales: Partial<.....'. + +41 const typeTest = await db.query2('typeTest').include('*', '**').get() +   ~~~~~~~ + +test/modify/props/mixed.ts:57:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 4 more ...; readonly test: { ...; }; }; }; readonly typeTest: { ...; }; }; locales: Partial<.....'. + +57 const user = await db.query2('user').include('*', '**').get() +   ~~~~~~~ + +test/modify/props/numbers.ts:241:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly n: { type: "number"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., und...'. + +241 .include( +   ~~~~~~~ + +test/modify/props/object.ts:56:13 - error TS2531: Object is possibly 'null'. + +56 deepEqual((await db.query2('thing', id1).get()).info, { +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/modify/props/object.ts:65:13 - error TS2531: Object is possibly 'null'. + +65 deepEqual((await db.query2('thing', id1).get()).info, { +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/modify/props/object.ts:104:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly info: Omit<{ readonly type: "object"; readonly props: { ...; }; }, "props"> & { ...; }; readonly holders: { ...; }; }; }; readonly ho...'. + +104 const res1 = await db.query2('holder', id1).include('toThing.$edgeInfo').get() +   ~~~~~~~ + +test/modify/props/object.ts:121:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly info: Omit<{ readonly type: "object"; readonly props: { ...; }; }, "props"> & { ...; }; readonly holders: { ...; }; }; }; readonly ho...'. + +121 const res2 = await db.query2('holder', id1).include('toThing.$edgeInfo').get() +   ~~~~~~~ + +test/modify/props/references.ts:26:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly refHolders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +26 const res = await db.query2('holder', h1).include('dest.id').get() +   ~~~~~~~ + +test/modify/props/references.ts:38:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly refHolders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +38 const res = await db.query2('holder', h1).include('dest.id').get() +   ~~~~~~~ + +test/modify/props/references.ts:50:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly refHolders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +50 const res = await db.query2('holder', h1).include('dest.id').get() +   ~~~~~~~ + +test/modify/props/references.ts:60:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly refHolders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +60 deepEqual(await db.query2('holder', h1).include('dest').get(), { +   ~~~~~~~ + +test/modify/props/references.ts:95:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly refsHolders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ....'. + +95 const res = await db.query2('holder', h1).include('dests').get() +   ~~~~~~~ + +test/modify/props/references.ts:166:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly refsHolders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ....'. + +166 .include('dests.id') +   ~~~~~~~ + +test/modify/props/references.ts:211:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +211 .include('toThing.$edgeRef.id') +   ~~~~~~~ + +test/modify/props/references.ts:279:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +279 .include('toThing.$edgeRefs.id') +   ~~~~~~~ + +test/modify/props/string.ts:111:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +111 const res1 = await db.query2('holder', id1).include('toThing.$edgeName').get() +   ~~~~~~~ + +test/modify/props/string.ts:123:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +123 const res2 = await db.query2('holder', id1).include('toThing.$edgeName').get() +   ~~~~~~~ + +test/modify/props/string.ts:134:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +134 const res3 = await db.query2('holder', id1).include('toThing.$edgeName').get() +   ~~~~~~~ + +test/modify/props/string.ts:145:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +145 const res4 = await db.query2('holder', id1).include('toThing.$edgeName').get() +   ~~~~~~~ + +test/modify/props/string.ts:156:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +156 const res5 = await db.query2('holder', id1).include('toThing.$edgeName').get() +   ~~~~~~~ + +test/modify/props/string.ts:167:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly name: { type: "string"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +167 const res6 = await db.query2('holder', id1).include('toThing.$edgeName').get() +   ~~~~~~~ + +test/modify/props/text.ts:71:13 - error TS2531: Object is possibly 'null'. + +71 deepEqual((await db.query2('thing', id1).get()).content, { +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/modify/props/text.ts:105:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly content: { type: "text"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +105 const res1 = await db.query2('holder', id1).include('toThing.$edgeText').get() +   ~~~~~~~ + +test/modify/props/text.ts:116:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly content: { type: "text"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +116 const res2 = await db.query2('holder', id1).include('toThing.$edgeText').get() +   ~~~~~~~ + +test/modify/props/timestamp.ts:100:13 - error TS2531: Object is possibly 'null'. + +100 deepEqual((await db.query2('event', id1).get()).ts, 0) +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/modify/props/timestamp.ts:134:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly ts: { type: "timestamp"; }; readonly holders: { ...; }; }; }; readonly holder: { ...; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +134 const res = await db.query2('holder', id).include('toEvent.$edgeTs').get() +   ~~~~~~~ + +test/modify/props/vector.ts:108:29 - error TS18047: 'res' is possibly 'null'. + +108 const vecArr = Array.from(res.vec) as number[] +   ~~~ + +test/modify/props/vector.ts:121:30 - error TS18047: 'res2' is possibly 'null'. + +121 const vecArr2 = Array.from(res2.vec) as number[] +   ~~~~ + +test/modify/props/vector.ts:164:29 - error TS18047: 'res' is possibly 'null'. + +164 const vecArr = Array.from(res.vec) as number[] +   ~~~ + +test/modify/props/vector.ts:176:30 - error TS18047: 'res2' is possibly 'null'. + +176 const vecArr2 = Array.from(res2.vec) as number[] +   ~~~~ + +test/modify/props/vector.ts:217:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly vec: { readonly type: "vector"; readonly size: 3; readonly baseType: "float32"; }; readonly holders: { ...; }; }; }; readonly holder:...'. + +217 const res = await db.query2('holder', id1).include('toThing.$edgeVec').get() +   ~~~~~~~ + +test/modify/props/vector.ts:236:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly thing: { props: { readonly vec: { readonly type: "vector"; readonly size: 3; readonly baseType: "float32"; }; readonly holders: { ...; }; }; }; readonly holder:...'. + +236 const res2 = await db.query2('holder', id1).include('toThing.$edgeVec').get() +   ~~~~~~~ + +test/protocol/schema.ts:59:40 - error TS2322: Type '14' is not assignable to type 'PropTypeEnum'. + +59 prop: { path: ['scenarios'], typeIndex: 14, readBy: 0 }, +   ~~~~~~~~~ + + src/protocol/db-read/types.ts:45:3 + 45 typeIndex: PropTypeEnum +    ~~~~~~~~~ + The expected type comes from property 'typeIndex' which is declared here on type 'ReaderPropDef' + +test/protocol/schema.ts:124:36 - error TS2322: Type '14' is not assignable to type 'PropTypeEnum'. + +124 prop: { path: ['items'], typeIndex: 14, readBy: 0 }, +   ~~~~~~~~~ + + src/protocol/db-read/types.ts:45:3 + 45 typeIndex: PropTypeEnum +    ~~~~~~~~~ + The expected type comes from property 'typeIndex' which is declared here on type 'ReaderPropDef' + +test/query-ast/validate.perf.ts:1:20 - error TS2307: Cannot find module 'valibot' or its corresponding type declarations. + +1 import * as v from 'valibot' +   ~~~~~~~~~ + +test/query/db.ts:66:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +66 const res = await db.query2('user').include('address.street').get() +   ~~~~~~~ + +test/query/db.ts:77:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +77 .include('name') +   ~~~~~~~ + +test/query/db.ts:87:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +87 .include('name') +   ~~~~~~~ + +test/query/db.ts:114:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +114 .include('name') +   ~~~~~~~ + +test/query/db.ts:124:41 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +124 const res = await db.query2('user').sum('age').get() +   ~~~ + +test/query/db.ts:172:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +172 const q = db.query2('user').include('friends.$rank', 'friends.$rating') +   ~~~~~~~ + +test/query/db.ts:181:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +181 const q = db.query2('user').include('friends.$friendRef') +   ~~~~~~~ + +test/query/types.ts:110:11 - error TS2322: Type '"a" | "b" | undefined' is not assignable to type '"a" | "b"'. + Type 'undefined' is not assignable to type '"a" | "b"'. + +110 const myEnum: 'a' | 'b' = everything.myEnum +   ~~~~~~ + +test/query/types.ts:123:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +123 const query = db.query2('everything').include('myEnum') +   ~~~~~~~ + +test/query/types.ts:129:7 - error TS2578: Unused '@ts-expect-error' directive. + +129 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:135:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +135 const query = db.query2('everything').include('*') +   ~~~~~~~ + +test/query/types.ts:142:7 - error TS2578: Unused '@ts-expect-error' directive. + +142 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:147:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +147 const query = db.query2('everything').include('**') +   ~~~~~~~ + +test/query/types.ts:158:7 - error TS2578: Unused '@ts-expect-error' directive. + +158 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:160:7 - error TS2578: Unused '@ts-expect-error' directive. + +160 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:162:7 - error TS2578: Unused '@ts-expect-error' directive. + +162 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:169:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +169 const query = db.query2('everything').include('myEnum', '**') +   ~~~~~~~ + +test/query/types.ts:179:7 - error TS2578: Unused '@ts-expect-error' directive. + +179 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:186:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +186 const query = db.query2('everything').include('n', 's', 'nested') +   ~~~~~~~ + +test/query/types.ts:196:7 - error TS2578: Unused '@ts-expect-error' directive. + +196 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:203:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +203 const query = db.query2('everything').include('*', 'myRefs') +   ~~~~~~~ + +test/query/types.ts:212:7 - error TS2578: Unused '@ts-expect-error' directive. + +212 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:219:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +219 const query = db.query2('everything', 1).include('*', 'myRefs') +   ~~~~~~~ + +test/query/types.ts:238:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +238 .include((select) => select('myRefs').include('isNice')) +   ~~~~~~~ + +test/query/types.ts:250:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +250 .include((select) => select('backRefs').include('myEnum')) +   ~~~~~~~ + +test/query/types.ts:260:43 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +260 const query = db.query2('everything').sum('n', 'i8').sum('card') +   ~~~ + +test/query/types.ts:288:43 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +288 const query = db.query2('everything').sum('n') +   ~~~ + +test/query/types.ts:293:7 - error TS2578: Unused '@ts-expect-error' directive. + +293 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/query/types.ts:297:48 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; readonly backRef: { ...; }; readonly backRefs: { ...; }; }; }; readonly everything: { ...; }; }; locales...'. + +297 const queryGroup = db.query2('everything').groupBy('s').sum('n') +   ~~~~~~~ + +test/query/types.ts:305:9 - error TS2578: Unused '@ts-expect-error' directive. + +305 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/queryResponse.ts:25:19 - error TS18047: 'response' is possibly 'null'. + +25 extractNumber(response.version), +   ~~~~~~~~ + +test/queryResponse.ts:25:28 - error TS2339: Property 'version' does not exist on type '{ id: number; status: "a" | "b" | undefined; }'. + +25 extractNumber(response.version), +   ~~~~~~~ + +test/queryResponse.ts:26:5 - error TS18047: 'response' is possibly 'null'. + +26 response.checksum, +   ~~~~~~~~ + +test/queryResponse.ts:26:14 - error TS2339: Property 'checksum' does not exist on type '{ id: number; status: "a" | "b" | undefined; }'. + +26 response.checksum, +   ~~~~~~~~ + +test/queryResponse.ts:36:12 - error TS18047: 'response' is possibly 'null'. + +36 notEqual(response.version, response2.version) +   ~~~~~~~~ + +test/queryResponse.ts:36:21 - error TS2339: Property 'version' does not exist on type '{ id: number; status: "a" | "b" | undefined; }'. + +36 notEqual(response.version, response2.version) +   ~~~~~~~ + +test/queryResponse.ts:36:30 - error TS18047: 'response2' is possibly 'null'. + +36 notEqual(response.version, response2.version) +   ~~~~~~~~~ + +test/queryResponse.ts:36:40 - error TS2339: Property 'version' does not exist on type '{ id: number; status: "a" | "b" | undefined; }'. + +36 notEqual(response.version, response2.version) +   ~~~~~~~ + +test/range.ts:59:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly nr: { ...; }; readonly location: Omit<....'. + +59 deepEqual(await db.query2('user').include('nr').range(1, 2).get(), [ +   ~~~~~~~ + +test/range.ts:63:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly nr: { ...; }; readonly location: Omit<....'. + +63 await db.query2('user').include('nr').sort('email').range(1, 2).get(), +   ~~~~~~~ + +test/raw.ts:21:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly uniqueSkills: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +21 .include('uniqueSkills', { raw: true }) +   ~~~~~~~ + +test/raw.ts:50:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly role: { ...; }; readonly resume: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., unde...'. + +50 .include(['name', 'role', 'resume'], { raw: true }) +   ~~~~~~~ + +test/references/references.ts:61:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; loca...'. + +61 deepEqual(await db.query2('article').include('contributors.name').get(), [ +   ~~~~~~~ + +test/references/references.ts:75:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; loca...'. + +75 deepEqual(await db.query2('user').include('articles.name').get(), [ +   ~~~~~~~ + +test/references/references.ts:130:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly uid: { readonly type: "uint32"; }; readonly name: { ...; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; ...'. + +130 deepEqual(await db.query2('user').include('resources').get(), [ +   ~~~~~~~ + +test/references/references.ts:158:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly uid: { readonly type: "uint32"; }; readonly name: { ...; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; ...'. + +158 deepEqual(await db.query2('user').include('resources.name').get(), [ +   ~~~~~~~ + +test/references/references.ts:218:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +218 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:218:37 + 218 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:218:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +218 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:242:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +242 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:242:37 + 242 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:242:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +242 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:263:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +263 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:263:37 + 263 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:263:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +263 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:284:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +284 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:284:37 + 284 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:284:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +284 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:309:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +309 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:309:37 + 309 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:309:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +309 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:335:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +335 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:335:37 + 335 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:335:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +335 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:369:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +369 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:369:37 + 369 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:369:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +369 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:400:37 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "user", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "user", id: number | (Partial; }, "user">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "user">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly resource: ...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +400 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~ + + test/references/references.ts:400:37 + 400 deepEqual(await db.query2('user', user).include('resources').get(), { +    ~~~~ + Did you forget to use 'await'? + +test/references/references.ts:400:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly max: 8; }; readonly resources: Omit<...> & { ...; }; }; }; readonly resource: { ...; }; }; l...'. + +400 deepEqual(await db.query2('user', user).include('resources').get(), { +   ~~~~~~~ + +test/references/references.ts:477:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }...'. + +477 deepEqual(await db.query2('article').include('contributors.name').get(), [ +   ~~~~~~~ + +test/references/references.ts:550:32 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "article", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "article", id: number | (Partial; }, "article">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "article">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly art...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +550 await db.query2('article', strudelArticle).include('contributors').get(), +   ~~~~~~~~~~~~~~ + + test/references/references.ts:550:32 + 550 await db.query2('article', strudelArticle).include('contributors').get(), +    ~~~~~~~~~~~~~~ + Did you forget to use 'await'? + +test/references/references.ts:550:48 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }...'. + +550 await db.query2('article', strudelArticle).include('contributors').get(), +   ~~~~~~~ + +test/references/references.ts:565:26 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "article", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "article", id: number | (Partial; }, "article">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "article">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly art...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +565 .query2('article', strudelArticle) +   ~~~~~~~~~~~~~~ + + test/references/references.ts:565:26 + 565 .query2('article', strudelArticle) +    ~~~~~~~~~~~~~~ + Did you forget to use 'await'? + +test/references/references.ts:566:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }...'. + +566 .include((q) => q('contributors').include('name').filter('flap', '>', 25)) +   ~~~~~~~ + +test/references/references.ts:580:26 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "article", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "article", id: number | (Partial; }, "article">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "article">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly art...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +580 .query2('article', strudelArticle) +   ~~~~~~~~~~~~~~ + + test/references/references.ts:580:26 + 580 .query2('article', strudelArticle) +    ~~~~~~~~~~~~~~ + Did you forget to use 'await'? + +test/references/references.ts:581:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }...'. + +581 .include((q) => { +   ~~~~~~~ + +test/references/references.ts:597:26 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "article", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "article", id: number | (Partial; }, "article">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "article">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial & { ...; }; }; }; readonly art...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +597 .query2('article', strudelArticle) +   ~~~~~~~~~~~~~~ + + test/references/references.ts:597:26 + 597 .query2('article', strudelArticle) +    ~~~~~~~~~~~~~~ + Did you forget to use 'await'? + +test/references/references.ts:598:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }...'. + +598 .include((select) => { +   ~~~~~~~ + +test/references/references.ts:814:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly email: { ...; }; readonly invitedBy: { ...; } & { ...; }; readonly invited: { ...; }; }; }; }; loc...'. + +814 deepEqual(await client.query2('user').include('email', 'invitedBy').get(), [ +   ~~~~~~~ + +test/references/references.ts:877:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly product: { props: { readonly reviews: Omit<...> & { ...; }; }; }; readonly review: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +877 const products = await db.query2('product').include('*', '**').get() +   ~~~~~~~ + +test/references/references.ts:878:45 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly product: { props: { readonly reviews: Omit<...> & { ...; }; }; }; readonly review: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +878 const reviews = await db.query2('review').include('*', '**').get() +   ~~~~~~~ + +test/references/references.ts:928:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +928 deepEqual(await db.query2('user', mrSnurp).include('**').get(), { +   ~~~~~~~ + +test/references/references.ts:936:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +936 deepEqual(await db.query2('user', mrSnurp).include('**').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:37:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +37 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:51:11 - error TS2322: Type '{ update: { id: BasedCreatePromise; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: BasedCreatePromise; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; }'. + +51 $index: -1, +   ~~~~~~ + + test/references/referencesIndex.ts:11:11 +  11 friends: { +    ~~~~~~~~~~ +  12 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... +  15 }, +   ~~~~~~~~~~~~~~ +  16 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ name?: string | null | undefined; friends?: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify<...> | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; }' + +test/references/referencesIndex.ts:62:11 - error TS2322: Type '{ update: { id: BasedCreatePromise; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: BasedCreatePromise; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; }'. + +62 $index: -1, +   ~~~~~~ + + test/references/referencesIndex.ts:11:11 +  11 friends: { +    ~~~~~~~~~~ +  12 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... +  15 }, +   ~~~~~~~~~~~~~~ +  16 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ name?: string | null | undefined; friends?: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify<...> | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; }' + +test/references/referencesIndex.ts:68:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +68 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:86:11 - error TS2322: Type '{ update: { id: BasedCreatePromise; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: BasedCreatePromise; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; }'. + +86 $index: 0, +   ~~~~~~ + + test/references/referencesIndex.ts:11:11 +  11 friends: { +    ~~~~~~~~~~ +  12 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... +  15 }, +   ~~~~~~~~~~~~~~ +  16 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ name?: string | null | undefined; friends?: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify<...> | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; }' + +test/references/referencesIndex.ts:92:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +92 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:111:11 - error TS2322: Type '{ update: { id: BasedCreatePromise; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: BasedCreatePromise; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; }'. + +111 $index: 2, +   ~~~~~~ + + test/references/referencesIndex.ts:11:11 +  11 friends: { +    ~~~~~~~~~~ +  12 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... +  15 }, +   ~~~~~~~~~~~~~~ +  16 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ name?: string | null | undefined; friends?: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify<...> | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; }' + +test/references/referencesIndex.ts:117:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +117 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:133:11 - error TS2322: Type '{ update: { id: BasedCreatePromise; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: BasedCreatePromise; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; }'. + +133 $index: -1, +   ~~~~~~ + + test/references/referencesIndex.ts:11:11 +  11 friends: { +    ~~~~~~~~~~ +  12 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... +  15 }, +   ~~~~~~~~~~~~~~ +  16 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ name?: string | null | undefined; friends?: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify<...> | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; }' + +test/references/referencesIndex.ts:139:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +139 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:156:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +156 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:169:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +169 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesIndex.ts:213:11 - error TS2322: Type '{ update: { id: BasedCreatePromise; $index: number; }[]; }' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + Types of property 'update' are incompatible. + Type '{ id: BasedCreatePromise; $index: number; }[]' is not assignable to type '(number | BasedModify | { id: number | BasedModify; })[]'. + Object literal may only specify known properties, and '$index' does not exist in type 'BasedModify | { id: number | BasedModify; }'. + +213 $index: 2, +   ~~~~~~ + + test/references/referencesIndex.ts:182:11 + 182 friends: { +    ~~~~~~~~~~ + 183 items: { +   ~~~~~~~~~~~~~~~~~~~~ + ... + 186 }, +   ~~~~~~~~~~~~~~ + 187 }, +   ~~~~~~~~~~~ + The expected type comes from property 'friends' which is declared here on type '{ name?: string | null | undefined; friends?: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify<...> | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; }' + +test/references/referencesIndex.ts:219:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +219 deepEqual(await db.query2('user', john).include('*', 'friends').get(), { +   ~~~~~~~ + +test/references/referencesModify.ts:49:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +49 await db.query2('user').include('*', 'friends').get(), +   ~~~~~~~ + +test/references/referencesModify.ts:67:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +67 await db.query2('user').include('*', 'friends').get(), +   ~~~~~~~ + +test/references/referencesModify.ts:88:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +88 await db.query2('user').include('*', 'friends').get(), +   ~~~~~~~ + +test/references/referencesModify.ts:186:35 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly a: { props: { readonly name: { type: "string"; }; readonly bees: Omit<...> & { ...; }; }; }; readonly b: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +186 deepEqual((await db.query2('a').include('bees').get())[0].bees[0].id, 2) +   ~~~~~~~ + +test/references/referencesModify.ts:191:35 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly a: { props: { readonly name: { type: "string"; }; readonly bees: Omit<...> & { ...; }; }; }; readonly b: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +191 deepEqual((await db.query2('a').include('bees').get())[0].bees.length, 1) +   ~~~~~~~ + +test/references/referencesModify.ts:192:35 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly a: { props: { readonly name: { type: "string"; }; readonly bees: Omit<...> & { ...; }; }; }; readonly b: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +192 deepEqual((await db.query2('a').include('bees').get())[0].bees[0].id, 2) +   ~~~~~~~ + +test/save/blockHash.ts:50:44 - error TS2339: Property 'checksum' does not exist on type '{ id: number; title: string; body: string; }[]'. + +50 (await client.query2('article').get()).checksum, +   ~~~~~~~~ + +test/save/blockHash.ts:51:42 - error TS2339: Property 'checksum' does not exist on type '{ id: number; title: string; body: string; }[]'. + +51 (await client.query2('story').get()).checksum, +   ~~~~~~~~ + +test/save/save.ts:197:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly group: { props: { readonly name: { readonly type: "string"; }; readonly users: Omit<...> & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; },...'. + +197 const users1 = await client.query2('user').include('group').get() +   ~~~~~~~ + +test/save/save.ts:198:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly group: { props: { readonly name: { readonly type: "string"; }; readonly users: Omit<...> & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; },...'. + +198 const users2 = await client2.query2('user').include('group').get() +   ~~~~~~~ + +test/save/save.ts:258:37 - error TS2345: Argument of type '{ readonly locales: { readonly en: {}; readonly fi: { readonly fallback: readonly ["en"]; }; }; readonly types: { readonly article: { readonly props: { readonly title: { readonly type: "text"; }; readonly body: { readonly type: "text"; }; }; }; }; }' is not assignable to parameter of type 'StrictSchema'. + Type '{ readonly locales: { readonly en: {}; readonly fi: { readonly fallback: readonly ["en"]; }; }; readonly types: { readonly article: { readonly props: { readonly title: { readonly type: "text"; }; readonly body: { readonly type: "text"; }; }; }; }; }' is not assignable to type '{ version?: string | undefined; types: SchemaTypes; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; locales?: Partial<...> | undefined; } & { ...; } & Omit<...> & { ...; }'. + Type '{ readonly locales: { readonly en: {}; readonly fi: { readonly fallback: readonly ["en"]; }; }; readonly types: { readonly article: { readonly props: { readonly title: { readonly type: "text"; }; readonly body: { readonly type: "text"; }; }; }; }; }' is not assignable to type '{ version?: string | undefined; types: SchemaTypes; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; locales?: Partial<...> | undefined; }'. + The types of 'locales.en' are incompatible between these types. + Property 'fallback' is missing in type '{}' but required in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; }'. + +258 const client = await db.setSchema(schema) +   ~~~~~~ + + src/schema/schema/locales.ts:7:5 + 7 fallback: LangName[] +    ~~~~~~~~ + 'fallback' is declared here. + +test/save/save.ts:290:32 - error TS2344: Type '{ readonly locales: { readonly en: {}; readonly fi: { readonly fallback: readonly ["en"]; }; }; readonly types: { readonly article: { readonly props: { readonly title: { readonly type: "text"; }; readonly body: { readonly type: "text"; }; }; }; }; }' does not satisfy the constraint 'SchemaIn'. + Type '{ readonly locales: { readonly en: {}; readonly fi: { readonly fallback: readonly ["en"]; }; }; readonly types: { readonly article: { readonly props: { readonly title: { readonly type: "text"; }; readonly body: { readonly type: "text"; }; }; }; }; }' is not assignable to type 'Schema'. + Type '{ readonly locales: { readonly en: {}; readonly fi: { readonly fallback: readonly ["en"]; }; }; readonly types: { readonly article: { readonly props: { readonly title: { readonly type: "text"; }; readonly body: { readonly type: "text"; }; }; }; }; }' is not assignable to type '{ version?: string | undefined; types: SchemaTypes; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; locales?: Partial<...> | undefined; }'. + The types of 'locales.en' are incompatible between these types. + Property 'fallback' is missing in type '{}' but required in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; }'. + +290 const client2 = new DbClient({ +   ~~~~~~~~~~~~~ + + src/schema/schema/locales.ts:7:5 + 7 fallback: LangName[] +    ~~~~~~~~ + 'fallback' is declared here. + +test/save/save.ts:366:15 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +366 await db2.query2('person').include('name', 'books').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/save/save.ts:367:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +367 await db.query2('person').include('name', 'books').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/save/save.ts:644:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +644 .filter('alias', 'includes', 'slim') +   ~~~~~~ + +test/save/save.ts:652:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +652 .filter('alias', 'includes', 'slim') +   ~~~~~~ + +test/save/save.ts:662:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +662 .filter('alias', 'includes', 'slick') +   ~~~~~~ + +test/save/save.ts:670:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +670 .filter('alias', 'includes', 'slick') +   ~~~~~~ + +test/save/save.ts:680:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +680 .filter('alias', 'includes', 'boss') +   ~~~~~~ + +test/save/save.ts:688:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +688 .filter('alias', 'includes', 'boss') +   ~~~~~~ + +test/save/save.ts:696:36 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +696 await client2.query2('person').include('name', 'alias', 'books').get(), +   ~~~~~~~ + +test/save/save.ts:697:35 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly book: { props: { readonly name: { readonly type: "string"; readonly max: 16; }; readonly isbn: { ...; }; readonly owner: { ...; } & { ...; }; }; }; readonly per...'. + +697 await client.query2('person').include('name', 'alias', 'books').get(), +   ~~~~~~~ + +test/save/save.ts:799:26 - error TS2345: Argument of type 'BasedDb' is not assignable to parameter of type 'DbServer'. + Type 'BasedDb' is missing the following properties from type 'DbServer': dbCtxExternal, migrating, saveInProgress, activeReaders, and 17 more. + +799 t.after(() => t.backup(db2)) +   ~~~ + +test/save/saveEdge.ts:43:48 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bestFriend: { ...; } & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +43 deepEqual(await client.query2('user', user2).include('**').get(), { +   ~~~~~~~ + +test/save/saveRange.ts:63:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly story: { ...; }; }; }; }; locales: Part...'. + +63 deepEqual(await client.query2('user').include('age').range(0, 1).get(), [ +   ~~~~~~~ + +test/save/saveRange.ts:112:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly story: { ...; }; }; }; }; locales: Part...'. + +112 deepEqual(await client2.query2('user').include('age').range(0, 1).get(), [ +   ~~~~~~~ + +test/save/saveRange.ts:121:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly story: { ...; }; }; }; }; locales: Part...'. + +121 .include('age') +   ~~~~~~~ + +test/save/saveRange.ts:132:42 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly story: { ...; }; }; }; }; locales: Part...'. + +132 deepEqual(await client2.query2('user').include('name').range(0, 2).get(), [ +   ~~~~~~~ + +test/save/saveRange.ts:146:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; readonly story: { ...; }; }; }; }; locales: Part...'. + +146 .include('name') +   ~~~~~~~ + +test/save/saveRange.ts:387:9 - error TS2353: Object literal may only specify known properties, and 'id' does not exist in type '{ y?: NumInc | null | undefined; }'. + +387 id: i, +   ~~ + +test/scenarios/e-commerce.ts:1:24 - error TS2307: Cannot find module '../../src/db-client/_modify/error.js' or its corresponding type declarations. + +1 import { errors } from '../../src/db-client/_modify/error.js' +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/scenarios/e-commerce.ts:334:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 50; }; ... 4 more ...; readonly reviews: Omit<...> & { ...; }; }; }; readonly cate...'. + +334 await client.query2('user').sort('lastLogin', 'asc').get(), +   ~~~~ + +test/scenarios/e-commerce.ts:343:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +343 .query2('product') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/e-commerce.ts:356:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +356 .query2('user', userId) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/e-commerce.ts:370:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +370 .query2('review') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/e-commerce.ts:392:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +392 .query2('product') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/e-commerce.ts:486:36 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 50; }; ... 4 more ...; readonly reviews: Omit<...> & { ...; }; }; }; readonly cate...'. + +486 await client.query2('product').range(0, 10_000_000).get() +   ~~~~~ + +test/scenarios/northwind.ts:12:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +12 const r1 = await db.query2('region').include('*').get() +   ~~~~~~~ + +test/scenarios/northwind.ts:33:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +33 const r2 = await db.query2('employees').include('firstName', 'lastName').get() +   ~~~~~~~ + +test/scenarios/northwind.ts:50:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +50 .include('firstName', 'lastName') +   ~~~~~~~ + +test/scenarios/northwind.ts:70:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +70 .include('orderDate', 'shippedDate', 'customer.id', 'freight') +   ~~~~~~~ + +test/scenarios/northwind.ts:101:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +101 .include('title', 'firstName', 'lastName') +   ~~~~~~~ + +test/scenarios/northwind.ts:150:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +150 .include('firstName', 'lastName', 'region') +   ~~~~~~~ + +test/scenarios/northwind.ts:168:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +168 .include('firstName', 'lastName', 'region') +   ~~~~~~~ + +test/scenarios/northwind.ts:197:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +197 .include('firstName', 'lastName', 'title', 'city', 'region') +   ~~~~~~~ + +test/scenarios/northwind.ts:238:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +238 .include('companyName', 'contactTitle', 'city', 'country') +   ~~~~~~~ + +test/scenarios/northwind.ts:307:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +307 .filter('product', '=', 3) +   ~~~~~~ + +test/scenarios/northwind.ts:317:44 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +317 const r13 = await db.query2('employees').groupBy('city').count().get() +   ~~~~~~~ + +test/scenarios/northwind.ts:337:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +337 .include('orderDate', 'customer.companyName') +   ~~~~~~~ + +test/scenarios/northwind.ts:377:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +377 .include( +   ~~~~~~~ + +test/scenarios/northwind.ts:429:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +429 .filter('country', '=', 'Mexico') +   ~~~~~~ + +test/scenarios/northwind.ts:511:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +511 .sort('unitPrice', 'desc') +   ~~~~ + +test/scenarios/northwind.ts:564:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +564 .sort('unitPrice', 'desc') +   ~~~~ + +test/scenarios/northwind.ts:607:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +607 .filter('country', '=', ['Germany', 'France', 'UK']) +   ~~~~~~ + +test/scenarios/northwind.ts:662:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +662 .filter('unitPrice', '..', [10, 20]) +   ~~~~~~ + +test/scenarios/northwind.ts:923:45 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +923 const r24 = (await db.query2('customers').include('companyName').get()).map( +   ~~~~~~~ + +test/scenarios/northwind.ts:1032:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1032 .include('contactName', 'city', 'country') +   ~~~~~~~ + +test/scenarios/northwind.ts:1038:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1038 .include('contactName', 'city', 'country') +   ~~~~~~~ + +test/scenarios/northwind.ts:1090:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1090 .include('city', 'country') +   ~~~~~~~ + +test/scenarios/northwind.ts:1096:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1096 .include('city', 'country') +   ~~~~~~~ + +test/scenarios/northwind.ts:1135:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1135 .include('*') +   ~~~~~~~ + +test/scenarios/northwind.ts:1166:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1166 .include('*') +   ~~~~~~~ + +test/scenarios/northwind.ts:1193:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1193 .include('id') +   ~~~~~~~ + +test/scenarios/northwind.ts:1202:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1202 .include('*') +   ~~~~~~~ + +test/scenarios/northwind.ts:1218:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1218 .include('customer.companyName', 'orderDate') +   ~~~~~~~ + +test/scenarios/northwind.ts:1288:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1288 .include('companyName', (q) => q('orders').include('id')) +   ~~~~~~~ + +test/scenarios/northwind.ts:1376:5 - error TS2531: Object is possibly 'null'. + +1376 (await db.query2('customers', { customerId: 'WELLI' }).get()).id!, +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/scenarios/northwind.ts:1383:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1383 .filter('customer', '=', wandk) +   ~~~~~~ + +test/scenarios/northwind.ts:1395:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1395 const orders = await db.query2('orders').include('customer.id').get() +   ~~~~~~~ + +test/scenarios/northwind.ts:1407:33 - error TS2339: Property 'companyName' does not exist on type '{ id: any; }'. + +1407 companyName: customer.companyName, +   ~~~~~~~~~~~ + +test/scenarios/northwind.ts:1413:31 - error TS2339: Property 'companyName' does not exist on type '{ id: any; }'. + +1413 companyName: customer.companyName, +   ~~~~~~~~~~~ + +test/scenarios/northwind.ts:1457:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1457 .include('customerId', 'companyName', 'city') +   ~~~~~~~ + +test/scenarios/northwind.ts:1487:41 - error TS2339: Property 'min' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1487 deepEqual(await db.query2('products').min('unitPrice').get(), { +   ~~~ + +test/scenarios/northwind.ts:1496:33 - error TS2339: Property 'min' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1496 await db.query2('products').min('unitPrice').groupBy('category').get(), +   ~~~ + +test/scenarios/northwind.ts:1512:41 - error TS2339: Property 'max' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1512 deepEqual(await db.query2('products').max('unitPrice').get(), { +   ~~~ + +test/scenarios/northwind.ts:1519:41 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1519 deepEqual(await db.query2('products').count().get(), { count: 77 }) +   ~~~~~ + +test/scenarios/northwind.ts:1525:41 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1525 deepEqual(await db.query2('products').count().groupBy('category').get(), { +   ~~~~~ + +test/scenarios/northwind.ts:1539:45 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1539 deepEqual(await db.query2('orderDetails').sum('quantity').get(), { +   ~~~ + +test/scenarios/northwind.ts:1550:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1550 .sum('quantity') +   ~~~ + +test/scenarios/northwind.ts:1563:8 - error TS2339: Property 'sum' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1563 .sum('quantity') +   ~~~ + +test/scenarios/northwind.ts:1584:41 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1584 deepEqual(await db.query2('products').avg('unitPrice').get(), { +   ~~~ + +test/scenarios/northwind.ts:1595:8 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1595 .avg('unitPrice') +   ~~~ + +test/scenarios/northwind.ts:1606:33 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1606 await db.query2('products').avg('unitPrice').groupBy('category').get(), +   ~~~ + +test/scenarios/northwind.ts:1638:45 - error TS2339: Property 'avg' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1638 deepEqual(await db.query2('orderDetails').avg('discountAmount').get(), { +   ~~~ + +test/scenarios/nycTaxi.ts:429:7 - error TS2322: Type 'string | number' is not assignable to type 'string | null | undefined'. + Type 'number' is not assignable to type 'string'. + +429 borough: taxiZoneLookup[i + 1], +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:430:7 - error TS2322: Type 'string | number' is not assignable to type 'string | null | undefined'. + Type 'number' is not assignable to type 'string'. + +430 zone: taxiZoneLookup[i + 2], +   ~~~~ + +test/scenarios/nycTaxi.ts:431:7 - error TS2322: Type 'string | number' is not assignable to type 'string | null | undefined'. + Type 'number' is not assignable to type 'string'. + +431 serviceZone: taxiZoneLookup[i + 3], +   ~~~~~~~~~~~ + +test/scenarios/nycTaxi.ts:438:7 - error TS2322: Type 'string | number' is not assignable to type 'string | null | undefined'. + Type 'number' is not assignable to type 'string'. + +438 name: rates[i + 1], +   ~~~~ + +test/scenarios/nycTaxi.ts:471:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +471 .include('id') +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:476:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +476 .include('id') +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:481:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +481 .include('id') +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:486:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +486 .include('id') +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:541:27 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +541 await db.query2('zone').include('borough').get().inspect() +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:551:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +551 .include( +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:560:27 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +560 await db.query2('trip').count().groupBy('dropoffLoc').get().inspect() +   ~~~~~ + +test/scenarios/nycTaxi.ts:561:27 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +561 await db.query2('trip').count().groupBy('paymentType').get().inspect() +   ~~~~~ + +test/scenarios/nycTaxi.ts:563:27 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +563 await db.query2('trip').count().get().inspect() +   ~~~~~ + +test/scenarios/nycTaxi.ts:596:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +596 .filter('pickupYear', '>=', 2022) +   ~~~~~~ + +test/scenarios/nycTaxi.ts:607:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +607 .include('name', (select) => { +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:621:6 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +621 .groupBy('pickup', { step: 'day', timeZone: 'America/New_York' }) +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:633:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +633 .filter('pickupHour', '>=', 7) +   ~~~~~~ + +test/scenarios/nycTaxi.ts:642:6 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +642 .groupBy('pickup', { step: 'dow', timeZone: 'America/New_York' }) +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:659:6 - error TS2339: Property 'groupBy' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +659 .groupBy('pickupDropoffLocs') +   ~~~~~~~ + +test/scenarios/nycTaxi.ts:670:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly zone: { props: { readonly locationId: { ...; }; ... 4 more ...; readonly dropoffs: { ...; }; }; }; readonly rate: { ...; }; readonly vendor: { ...; }; readonly ...'. + +670 .filter('pickupHour', '>=', 7) +   ~~~~~~ + +test/scenarios/vote.ts:155:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +155 .query2('round', final) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/vote.ts:174:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +174 .query2('round', final) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/vote.ts:303:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +303 .query2('payment', realIds) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/vote.ts:392:15 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +392 (await db.query2('vote').range(0, 1e6).get()).length, +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/voteEdges.ts:132:12 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +132 await db.query2('vote').get().inspect(1) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/voteEdges.ts:133:12 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +133 await db.query2('round', final).include('*', '**').get().inspect(1) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/voteLargeAmounts.perf.ts:138:26 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +138 const cnt = await db.query2('vote').count().get() +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/voteLargeAmounts.perf.ts:163:8 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +163 .query2('vote') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/scenarios/voteLargeAmounts.perf.ts:185:7 - error TS2345: Argument of type 'BasedDb' is not assignable to parameter of type 'DbServer'. + Type 'BasedDb' is missing the following properties from type 'DbServer': dbCtxExternal, migrating, saveInProgress, activeReaders, and 17 more. + +185 db, +   ~~ + +test/scenarios/voteLargeAmounts.perf.ts:199:16 - error TS2339: Property 'flushTime' does not exist on type 'DbClient'. + +199 client.flushTime = 10 +   ~~~~~~~~~ + +test/scenarios/voteStorage.ts:131:46 - error TS2345: Argument of type '{ fingerprint: string; status: "WebhookSuccess"; round: number; }' is not assignable to parameter of type '{ [x: string]: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; ... 11 more ...; createdAt?: s...'. + Property 'fingerprint' is incompatible with index signature. + Type 'string' is not assignable to type '{ add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { id: number | BasedModify<...>; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined'. + +131 const payment = client.create('payment', { +   ~ +132 fingerprint: `blablabla-${i}`, +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... +134 round: final, +  ~~~~~~~~~~~~~~~~~~~ +135 }) +  ~~~~~ + +test/scenarios/voteStorage.ts:136:40 - error TS2345: Argument of type '{ fingerprint: string; payment: BasedCreatePromise; round: number; }' is not assignable to parameter of type '{ [x: string]: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; ... 4 more ...; round?: number...'. + Property 'countries' is missing in type '{ fingerprint: string; payment: BasedCreatePromise; round: number; }' but required in type '{ [x: string]: { add?: (number | BasedModify | { id: number | BasedModify; })[] | undefined; update?: (number | BasedModify | { ...; })[] | undefined; delete?: (number | BasedModify<...>)[] | undefined; } | (number | ... 1 more ... | { ...; })[] | null | undefined; ... 4 more ...; round?: number...'. + +136 const vote = client.create('vote', { +   ~ +137 fingerprint: `blablabla-vote-${i}`, +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... +139 round: final, +  ~~~~~~~~~~~~~~~~~~~ +140 }) +  ~~~~~ + + test/scenarios/voteStorage.ts:125:9 + 125 countries: voteCountrySchema, +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 'countries' is declared here. + +test/scenarios/voteStorage.ts:186:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly round: { props: { readonly votes: Omit<{ ...; }, "items"> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +186 (await client.query2('round', final).include('votes').get()).votes.length, +   ~~~~~~~ + +test/scenarios/voteStorage.ts:195:47 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly round: { props: { readonly votes: Omit<{ ...; }, "items"> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +195 await client.query2('vote', randomId).include('round').get(), +   ~~~~~~~ + +test/scenarios/voteStorage.ts:201:47 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly round: { props: { readonly votes: Omit<{ ...; }, "items"> & { ...; }; }; }; readonly vote: { ...; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +201 const votes = await client.query2('vote').range(0, 1e6).include('id').get() +   ~~~~~ + +test/schema/parse/edges.ts:36:17 - error TS2322: Type '{ enum: string[]; }' is not assignable to type 'SchemaString | SchemaBinary | SchemaBoolean | SchemaCardinality | SchemaJson | SchemaNumber | ... 6 more ... | SchemaReference<...>'. + Type '{ enum: string[]; }' is not assignable to type 'SchemaEnum'. + Property 'type' is missing in type '{ enum: string[]; }' but required in type '{ type: "enum"; }'. + +36 $option: { enum: ['cool', 'dazzling'] }, +   ~~~~~~~ + + dist/schema/schema/enum.d.ts:5:5 + 5 type: 'enum'; +    ~~~~ + 'type' is declared here. + +test/schema/parse/edges.ts:40:15 - error TS2741: Property 'type' is missing in type '{ ref: string; prop: string; $nerds: { type: "boolean"; }; }' but required in type 'Omit, keyof Base>'. + +40 items: { +   ~~~~~ + + dist/schema/schema/reference.d.ts:18:5 + 18 type: 'reference'; +    ~~~~ + 'type' is declared here. + +test/schema/parse/edges.ts:54:15 - error TS2322: Type '{ enum: string[]; }' is not assignable to type 'SchemaString | SchemaBinary | SchemaBoolean | SchemaCardinality | SchemaJson | SchemaNumber | ... 6 more ... | SchemaReference<...>'. + Type '{ enum: string[]; }' is not assignable to type 'SchemaEnum'. + Property 'type' is missing in type '{ enum: string[]; }' but required in type '{ type: "enum"; }'. + +54 $option: { enum: ['cool', 'dazzling'] }, +   ~~~~~~~ + + dist/schema/schema/enum.d.ts:5:5 + 5 type: 'enum'; +    ~~~~ + 'type' is declared here. + +test/schema/parse/edges.ts:57:15 - error TS2741: Property 'type' is missing in type '{ ref: string; prop: string; $nerds: { type: "boolean"; }; }' but required in type 'Omit, keyof Base>'. + +57 items: { +   ~~~~~ + + dist/schema/schema/reference.d.ts:18:5 + 18 type: 'reference'; +    ~~~~ + 'type' is declared here. + +test/schema/parse/infer.ts:1:36 - error TS2307: Cannot find module '../../src/schema.js' or its corresponding type declarations. + +1 import type { Infer, Schema } from '../../src/schema.js' +   ~~~~~~~~~~~~~~~~~~~~~ + +test/schema/parse/references.ts:211:17 - error TS2578: Unused '@ts-expect-error' directive. + +211 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/schema/parse/schema.ts:12:7 - error TS2578: Unused '@ts-expect-error' directive. + +12 // @ts-expect-error +   ~~~~~~~~~~~~~~~~~~~ + +test/schema/props/write.ts:20:8 - error TS2307: Cannot find module '../../../src/schema/defs/props/separate.js' or its corresponding type declarations. + +20 } from '../../../src/schema/defs/props/separate.js' +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/schema/serialize/serialize.ts:46:13 - error TS2353: Object literal may only specify known properties, and 'required' does not exist in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; }'. + +46 en: { required: true }, +   ~~~~~~~~ + +test/schema/serialize/serialize.ts:47:7 - error TS2741: Property 'fallback' is missing in type '{}' but required in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; }'. + +47 nl: {}, +   ~~ + + dist/schema/schema/locales.d.ts:5:5 + 5 fallback: LangName[]; +    ~~~~~~~~ + 'fallback' is declared here. + +test/schema/serialize/serialize.ts:153:13 - error TS2353: Object literal may only specify known properties, and 'required' does not exist in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; }'. + +153 en: { required: true }, +   ~~~~~~~~ + +test/schema/serialize/serialize.ts:154:7 - error TS2741: Property 'fallback' is missing in type '{}' but required in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; }'. + +154 nl: {}, +   ~~ + + dist/schema/schema/locales.d.ts:5:5 + 5 fallback: LangName[]; +    ~~~~~~~~ + 'fallback' is declared here. + +test/search.ts:29:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +29 .filter('body', 'like', 'article') +   ~~~~~~ + +test/search.ts:41:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +41 .filter('body', 'like', 'snurfelpants') +   ~~~~~~ + +test/search.ts:53:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +53 .filter('body', 'like', ['snurfelpants', 'article']) +   ~~~~~~ + +test/search.ts:65:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +65 .filter('body', 'like', 'kxngdom') +   ~~~~~~ + +test/search.ts:78:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +78 .filter('body', 'like', 'derperp') +   ~~~~~~ + +test/search.ts:91:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +91 .filter('body', 'like', 'kxngdom', { score: 0 }) +   ~~~~~~ + +test/search.ts:128:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +128 .search('Netherlands', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:140:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +140 .search('giraffe', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:152:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +152 .search('kingdom', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:165:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +165 .search('Netherlands', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:178:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +178 .search('giraffe', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:191:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +191 .search('derp', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:204:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +204 .search('giraffe first', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:216:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +216 .search('first', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:229:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +229 .search('second', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:242:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +242 .search('giraffe first', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:255:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +255 .search('italy netherlands', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:268:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +268 .search('italy netherlands', 'body', 'title') +   ~~~~~~ + +test/search.ts:281:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +281 .search('italy netherlands', 'body', 'title') +   ~~~~~~ + +test/search.ts:314:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +314 .search('giraffe first', 'body') +   ~~~~~~ + +test/search.ts:327:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +327 .search('derp derp', 'body', 'title') +   ~~~~~~ + +test/search.ts:374:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +374 .search('first', 'body') +   ~~~~~~ + +test/search.ts:386:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +386 .search('first', 'body') +   ~~~~~~ + +test/search.ts:420:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +420 .filter('body', 'like', 'mihailovitsin') +   ~~~~~~ + +test/search.ts:431:10 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly body: { readonly type: "string"; readonly compression: "none"; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +431 .filter('body', 'like', 'mihailovitšin') +   ~~~~~~ + +test/search.ts:461:8 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly date: { readonly type: "uint32"; }; readonly title: { ...; }; readonly body: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 mor...'. + +461 .search('giraffe first', { body: 0, title: 1 }) +   ~~~~~~ + +test/search.ts:486:40 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly name: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +486 deepEqual(await db.query2('article').search('Kavel').get(), [ +   ~~~~~~ + +test/serializeQueryDef.ts:33:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +33 deepEqual(await db.query2('user').filter('isNice', '=', true).get(), [ +   ~~~~~~ + +test/serializeQueryDef.ts:37:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +37 deepEqual(await db.query2('user').filter('isNice').get(), [ +   ~~~~~~ + +test/serializeQueryDef.ts:41:37 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly isNice: { type: "boolean"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +41 deepEqual(await db.query2('user').filter('isNice', false).get(), [ +   ~~~~~~ + +test/shared/test.ts:89:45 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +89 let x = await client.query2(type).include('*', '**').get() +   ~~~~~~~ + +test/shared/test.ts:92:50 - error TS2339: Property 'count' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +92 counts.push((await client.query2(type).count().get()).count) +   ~~~~~ + +test/simpleQuery.ts:36:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly age: { readonly type: "uint32"; }; readonly name: { ...; }; readonly countryCode: { ...; }; readonly location: Omit<...> & { ...; }; }...'. + +36 deepEqual(await db.query2('user').include('id').get(), [{ id: 1 }], 'Id only') +   ~~~~~~~ + +test/simpleQuery.ts:39:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly age: { readonly type: "uint32"; }; readonly name: { ...; }; readonly countryCode: { ...; }; readonly location: Omit<...> & { ...; }; }...'. + +39 await db.query2('user').filter('age', '<', 20).include('id', 'age').get(), +   ~~~~~~ + +test/simpleQuery.ts:44:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly age: { readonly type: "uint32"; }; readonly name: { ...; }; readonly countryCode: { ...; }; readonly location: Omit<...> & { ...; }; }...'. + +44 await db.query2('user').include('*').get(), +   ~~~~~~~ + +test/singleRef.ts:100:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bla: { readonly type: "uint32"; }; readonly simple: { ...; } & { ...; }; readonly name: { ...; }; }; }; readonly simple: { ...; }; }; ...'. + +100 deepEqual(await client.query2('simple').include('user.name').get(), [ +   ~~~~~~~ + +test/singleRef.ts:114:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly bla: { readonly type: "uint32"; }; readonly simple: { ...; } & { ...; }; readonly name: { ...; }; }; }; readonly simple: { ...; }; }; ...'. + +114 deepEqual(await client.query2('simple').include('user.name').get(), [ +   ~~~~~~~ + +test/singleRef.ts:197:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: { ...;...'. + +197 deepEqual(await db.query2('blup').include('flap').get(), [ +   ~~~~~~~ + +test/singleRef.ts:204:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: { ...;...'. + +204 const result1 = await db.query2('user').include('myBlup.flap').get() +   ~~~~~~~ + +test/singleRef.ts:210:44 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: { ...;...'. + +210 const result = await db.query2('simple').include('user.myBlup.flap').get() +   ~~~~~~~ + +test/singleRef.ts:216:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: { ...;...'. + +216 deepEqual(await db.query2('user').include('simple').get(), [ +   ~~~~~~~ + +test/singleRef.ts:229:39 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: { ...;...'. + +229 deepEqual(await db.query2('simple').include('user').get(), [ +   ~~~~~~~ + +test/singleRef.ts:236:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: { ...;...'. + +236 deepEqual(await db.query2('user').include('simple').get(), [ +   ~~~~~~~ + +test/singleRef.ts:304:39 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; readonly admin: { ...; } & ...'. + +304 deepEqual(await db.query2('simple').include('admin.user').get(), [ +   ~~~~~~~ + +test/singleRef.ts:406:39 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +406 deepEqual(await db.query2('simple').include('id').range(0, 1).get(), [ +   ~~~~~~~ + +test/singleRef.ts:411:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +411 await db.query2('simple').include('user').range(0, 1).get(), +   ~~~~~~~ + +test/singleRef.ts:422:31 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +422 await db.query2('simple', lastRes).include('user.location').get(), +   ~~~~~~~ + + +test/singleRef.ts:422:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +422 await db.query2('simple', lastRes).include('user.location').get(), +   ~~~~~~~ + +test/singleRef.ts:434:31 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +434 await db.query2('simple', lastRes).include('user').get(), +   ~~~~~~~ + + +test/singleRef.ts:434:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +434 await db.query2('simple', lastRes).include('user').get(), +   ~~~~~~~ + +test/singleRef.ts:454:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +454 .include('user.myBlup') +   ~~~~~~~ + +test/singleRef.ts:467:31 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +467 await db.query2('simple', lastRes).include('user.myBlup').get(), +   ~~~~~~~ + + +test/singleRef.ts:467:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +467 await db.query2('simple', lastRes).include('user.myBlup').get(), +   ~~~~~~~ + +test/singleRef.ts:476:31 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +476 await db.query2('simple', lastRes).include('user.myBlup', 'lilBlup').get(), +   ~~~~~~~ + + +test/singleRef.ts:476:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +476 await db.query2('simple', lastRes).include('user.myBlup', 'lilBlup').get(), +   ~~~~~~~ + +test/singleRef.ts:486:32 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +486 (await db.query2('simple', lastRes).include('user.myBlup').get()).node() +   ~~~~~~~ + + +test/singleRef.ts:486:41 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +486 (await db.query2('simple', lastRes).include('user.myBlup').get()).node() +   ~~~~~~~ + +test/singleRef.ts:495:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +495 .include('user.myBlup', 'lilBlup', 'user.name') +   ~~~~~~~ + +test/singleRef.ts:513:31 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +513 await db.query2('simple', lastRes).include('user.location.label').get(), +   ~~~~~~~ + + +test/singleRef.ts:513:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +513 await db.query2('simple', lastRes).include('user.location.label').get(), +   ~~~~~~~ + +test/singleRef.ts:517:39 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +517 deepEqual(await db.query2('simple', lastRes).include('user.location').get(), { +   ~~~~~~~ + + +test/singleRef.ts:517:48 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +517 deepEqual(await db.query2('simple', lastRes).include('user.location').get(), { +   ~~~~~~~ + +test/singleRef.ts:525:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +525 .include('user.myBlup', 'lilBlup') +   ~~~~~~~ + +test/singleRef.ts:551:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +551 .include('user', 'user.myBlup') +   ~~~~~~~ + +test/singleRef.ts:575:25 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "simple", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number[] | undefined'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "simple", id: number | (Partial; }, "simple">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise | undefined' is not assignable to parameter of type 'number | (Partial; }, "simple">> & { ...; })'. + Type 'undefined' is not assignable to type 'number | (Partial; }, "simple">> & { ...; })'. + +575 .query2('simple', lastRes) +   ~~~~~~~ + + +test/singleRef.ts:576:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +576 .include('user', 'user.myBlup', 'lilBlup') +   ~~~~~~~ + +test/singleRef.ts:658:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly myBlup: { ...; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: {...'. + +658 .include('user', 'user.myBlup', 'lilBlup') +   ~~~~~~~ + +test/singleRef.ts:673:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly myBlup: { ...; } & { ...; }; readonly simple: { ...; } & { ...; }; }; }; readonly blup: {...'. + +673 .filter('age', '=', 5) +   ~~~~~~ + +test/singleRefQuery.ts:108:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; readonly name: { ...; }; };...'. + +108 .filter('user.myBlup.age', '=', 10) +   ~~~~~~ + +test/singleRefQuery.ts:123:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; readonly simple: { ...; } & { ...; }; readonly name: { ...; }; };...'. + +123 .filter('lilBlup.age', '=', 20) +   ~~~~~~ + +test/sort/sort.perf.ts:27:9 - error TS2365: Operator '<' cannot be applied to types 'void' and 'number'. + +27 equal(dbTime < 1000, true, 'db modify should not take longer then 1s') +   ~~~~~~~~~~~~~ + +test/sort/sort.perf.ts:39:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +39 .include('age', 'name', 'email') +   ~~~~~~~ + +test/sort/sort.ts:56:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +56 await db.query2('user').sort('age', 'desc').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:68:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +68 await db.query2('user').sort('age', 'asc').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:82:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +82 await db.query2('user').sort('email', 'asc').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:94:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +94 await db.query2('user').sort('email', 'desc').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:114:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +114 await db.query2('user').sort('email').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:127:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +127 await db.query2('user').sort('age').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:146:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +146 await db.query2('user').sort('email').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:165:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +165 await db.query2('user').sort('age').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:184:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +184 await db.query2('user').sort('age').include('email', 'age').get(), +   ~~~~ + +test/sort/sort.ts:196:37 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +196 deepEqual(await db.query2('user').sort('age').include('email', 'age').get(), [ +   ~~~~ + +test/sort/sort.ts:218:34 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +218 await db.query2('user', ids).include('name', 'age').sort('age').get(), +   ~~~~~~~ + +test/sort/sort.ts:237:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +237 .include('name', 'age') +   ~~~~~~~ + +test/sort/sort.ts:263:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +263 .include('name', 'age') +   ~~~~~~~ + +test/sort/sort.ts:293:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +293 .include('name', 'age') +   ~~~~~~~ + +test/sort/sort.ts:322:36 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +322 (await db.query2('user', ids2).include('name', 'age', 'email').get()) +   ~~~~~~~ + +test/sort/sort.ts:332:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +332 .include('name', 'age', 'email') +   ~~~~~~~ + +test/sort/sort.ts:344:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +344 .include('name', 'age', 'email') +   ~~~~~~~ + +test/sort/sort.ts:360:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +360 .include('name', 'age', 'email') +   ~~~~~~~ + +test/sort/sort.ts:378:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +378 .include('name', 'age', 'email') +   ~~~~~~~ + +test/sort/sort.ts:394:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; readonly email: { ...; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +394 .include('name', 'age', 'email') +   ~~~~~~~ + +test/sort/sort.ts:444:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly gender: { readonly type: "uint32"; }; readonly age: { ...; }; readonly name: { ...; }; readonly email: { ...; }; }; }; }; locales: Par...'. + +444 await client.query2('user').include('name').sort('age').range(0, 2).get(), +   ~~~~~~~ + +test/sort/sort.ts:452:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly gender: { readonly type: "uint32"; }; readonly age: { ...; }; readonly name: { ...; }; readonly email: { ...; }; }; }; }; locales: Par...'. + +452 await client.query2('user').include('name').sort('age').range(0, 2).get(), +   ~~~~~~~ + +test/sort/sort.ts:460:33 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly gender: { readonly type: "uint32"; }; readonly age: { ...; }; readonly name: { ...; }; readonly email: { ...; }; }; }; }; locales: Par...'. + +460 await client.query2('user').include('name').sort('name').range(0, 2).get(), +   ~~~~~~~ + +test/sort/sort.ts:485:34 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly gender: { readonly type: "uint32"; }; readonly age: { ...; }; readonly name: { ...; }; readonly email: { ...; }; }; }; }; locales: Par...'. + +485 await client2.query2('user').include('name').sort('name').range(0, 2).get(), +   ~~~~~~~ + +test/sort/sort.ts:512:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +512 await db.query2('dialog').sort('fun', 'desc').get() +   ~~~~ + +test/sort/sort.ts:531:31 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +531 await db.query2('dialog').sort('fun', 'desc').get(), +   ~~~~ + +test/sort/sort.ts:557:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +557 deepEqual(await db.query2('dialog').sort('fun', 'desc').get(), [ +   ~~~~ + +test/sort/sort.ts:569:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +569 deepEqual(await db.query2('dialog').sort('fun', 'desc').get(), [ +   ~~~~ + +test/sort/sort.ts:595:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "string"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +595 deepEqual(await db.query2('dialog').sort('fun', 'desc').get(), [ +   ~~~~ + +test/sort/sortAlias.ts:28:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +28 await db.query2('article').sort('email', 'desc').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/sort/sortAlias.ts:44:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +44 await db.query2('article').sort('email', 'desc').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/sort/sortAlias.ts:57:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +57 await db.query2('article').sort('email', 'desc').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/sort/sortAlias.ts:73:14 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +73 await db.query2('article').sort('email', 'desc').get(), +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/sort/sortBinary.ts:38:39 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly binary: { props: { readonly data: { readonly type: "binary"; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +38 deepEqual(await db.query2('binary').include('name', 'data').get(), [ +   ~~~~~~~ + +test/sort/sortBinary.ts:45:31 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly binary: { props: { readonly data: { readonly type: "binary"; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +45 await db.query2('binary').sort('data').include('name', 'data').get(), +   ~~~~ + +test/sort/sortBinary.ts:57:8 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly binary: { props: { readonly data: { readonly type: "binary"; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +57 .sort('data', 'desc') +   ~~~~ + +test/sort/sortById.ts:33:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +33 await db.query2('user').include('name').sort('id', 'asc').get(), +   ~~~~~~~ + +test/sort/sortById.ts:39:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +39 await db.query2('user').include('name').sort('id', 'desc').get(), +   ~~~~~~~ + +test/sort/sortById.ts:51:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly derp: { ...; }; readonly friends: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 m...'. + +51 await db.query2('user').include('name', 'friends.name').range(0, 1).get(), +   ~~~~~~~ + +test/sort/sortEnum.ts:37:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly status: { type: "enum"; enum: string[]; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +37 .filter('status', '=', ['a', 'b', 'c']) +   ~~~~~~ + +test/sort/sortEnum.ts:43:23 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly status: { type: "enum"; enum: string[]; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +43 db.query2('user').filter('status', '=', ['d']).range(0, 600).get(), +   ~~~~~~ + +test/sort/sortHll.ts:55:8 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +55 .sort('brazilians', 'desc') +   ~~~~ + +test/sort/sortHll.ts:74:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +74 await db.query2('article').sort('count', 'asc').include('derp').get(), +   ~~~~ + +test/sort/sortHll.ts:97:8 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +97 .sort('count', 'asc') +   ~~~~ + +test/sort/sortHll.ts:144:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +144 .filter('id', '=', testRecordId) +   ~~~~~~ + +test/sort/sortHll.ts:170:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +170 await db.query2('article').sort('count', 'desc').include('count').get(), +   ~~~~ + +test/sort/sortHll.ts:191:8 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +191 .sort('brazilians', 'desc') +   ~~~~ + +test/sort/sortHll.ts:205:5 - error TS2322: Type 'Uint8Array' is not assignable to type 'string | string[] | null | undefined'. + Type 'Uint8Array' is missing the following properties from type 'string[]': pop, push, concat, shift, and 6 more. + +205 count: xxHash64(ENCODER.encode('name1')), +   ~~~~~ + +test/sort/sortHll.ts:227:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly derp: { type: "number"; }; readonly count: { ...; }; readonly brazilians: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ....'. + +227 await db.query2('article').sort('count', 'desc').include('count').get(), +   ~~~~ + +test/sort/sortIds.ts:31:9 - error TS2322: Type 'number' is not assignable to type '0 | 1 | 2 | 3 | 4 | 5 | null | undefined'. + +31 bla: ~~(Math.random() * 5), +   ~~~ + +test/sort/sortIds.ts:39:41 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly mep: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +39 isSorted(await db.query2('user', ids).sort('age').get(), 'age') +   ~~~~ + +test/sort/sortIds.ts:40:41 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly mep: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +40 isSorted(await db.query2('user', ids).sort('name').get(), 'name') +   ~~~~ + +test/sort/sortIds.ts:41:41 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly mep: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +41 isSorted(await db.query2('user', ids).sort('flap').get(), 'flap') +   ~~~~ + +test/sort/sortIds.ts:42:41 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly mep: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +42 isSorted(await db.query2('user', ids).sort('blurf').get(), 'blurf') +   ~~~~ + +test/sort/sortIds.ts:43:41 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly mep: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +43 isSorted(await db.query2('user', ids).sort('bla').get(), 'bla') +   ~~~~ + +test/sort/sortIds.ts:44:41 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; ... 5 more ...; readonly mep: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +44 isSorted(await db.query2('user', ids).sort('mep').get(), 'mep') +   ~~~~ + +test/sort/sortIds.ts:96:10 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly flap: { readonly type: "uint32"; }; readonly name: { ...; }; readonly articles: Omit<...> & { ...; }; }; }; readonly article: { ...; }...'. + +96 .include((s) => s('contributors').sort('flap')) +   ~~~~~~~ + +test/sort/sortNodeId.ts:27:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly person: { props: { readonly name: { readonly type: "string"; }; readonly age: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +27 .include('name') +   ~~~~~~~ + +test/sort/sortNumber.ts:52:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +52 isSorted(await db.query2('example').sort('u32').include('u32').get(), 'u32') +   ~~~~ + +test/sort/sortNumber.ts:54:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +54 await db.query2('example').sort('boolean').include('boolean').get(), +   ~~~~ + +test/sort/sortNumber.ts:57:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +57 isSorted(await db.query2('example').sort('u8').include('u8').get(), 'u8') +   ~~~~ + +test/sort/sortNumber.ts:58:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +58 isSorted(await db.query2('example').sort('i8').include('i8').get(), 'i8') +   ~~~~ + +test/sort/sortNumber.ts:59:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +59 isSorted(await db.query2('example').sort('i16').include('i16').get(), 'i16') +   ~~~~ + +test/sort/sortNumber.ts:60:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +60 isSorted(await db.query2('example').sort('i32').include('i32').get(), 'i32') +   ~~~~ + +test/sort/sortNumber.ts:62:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +62 await db.query2('example').sort('number').include('number').get(), +   ~~~~ + +test/sort/sortNumber.ts:66:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +66 await db.query2('example').sort('timestamp').include('timestamp').get(), +   ~~~~ + +test/sort/sortNumber.ts:73:8 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +73 .sort('enum') +   ~~~~ + +test/sort/sortNumber.ts:80:39 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +80 isSorted(await db.query2('example').sort('u32').include('u32').get(), 'u32') +   ~~~~ + +test/sort/sortNumber.ts:84:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly example: { props: { readonly enum: { type: "enum"; enum: string[]; }; ... 8 more ...; readonly i32: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ...,...'. + +84 .include('enum') +   ~~~~~~~ + +test/sort/sortString.ts:70:11 - error TS2365: Operator '<' cannot be applied to types 'void' and 'number'. + +70 equal(dbTime < 1000, true, 'db modify should not take longer then 1s') +   ~~~~~~~~~~~~~ + +test/sort/sortString.ts:80:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +80 .query2('article') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/sort/sortString.ts:91:10 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +91 .query2('article') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/sort/sortString.ts:149:32 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly article: { props: { readonly name: { readonly type: "string"; readonly maxBytes: 20; }; readonly nr: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ......'. + +149 await db.query2('article').include('name', 'nr').sort('name', 'desc').get(), +   ~~~~~~~ + +test/sort/sortTimestamp.ts:43:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly flap: { type: "number"; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +43 .sort('startTime', 'asc') +   ~~~~ + +test/sort/sortTimestamp.ts:56:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly flap: { type: "number"; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +56 .sort('startTime', 'asc') +   ~~~~ + +test/sort/sortTimestamp.ts:72:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly flap: { type: "number"; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +72 .sort('startTime', 'asc') +   ~~~~ + +test/sort/sortTimestamp.ts:85:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly flap: { type: "number"; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +85 .sort('startTime', 'desc') +   ~~~~ + +test/sort/sortTimestamp.ts:101:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly flap: { type: "number"; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +101 .sort('startTime', 'asc') +   ~~~~ + +test/sort/sortTimestamp.ts:114:6 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly flap: { type: "number"; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., ...'. + +114 .sort('startTime', 'desc') +   ~~~~ + +test/sort/sortTimestamp.ts:152:30 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly derp: { type: "boolean"; }; readonly flap: { ...; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial...'. + +152 await db.query2('event').sort('startTime', 'asc').get(), +   ~~~~ + +test/sort/sortTimestamp.ts:162:36 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly event: { props: { readonly derp: { type: "boolean"; }; readonly flap: { ...; }; readonly startTime: { ...; }; readonly name: { ...; }; }; }; }; locales: Partial...'. + +162 await db.query2('event').sort('startTime', 'asc').get(), +   ~~~~ + +test/string.ts:42:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +42 deepEqual(await db.query2('user').include('name', 'snurp').get(), [ +   ~~~~~~~ + +test/string.ts:50:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +50 deepEqual(await db.query2('user').include('name', 'snurp', 'age').get(), [ +   ~~~~~~~ + +test/string.ts:62:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +62 .include( +   ~~~~~~~ + +test/string.ts:87:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +87 deepEqual(await db.query2('user').include('location.label').get(), [ +   ~~~~~~~ + +test/string.ts:96:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +96 deepEqual(await db.query2('user').include('location').get(), [ +   ~~~~~~~ + +test/string.ts:107:37 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +107 deepEqual(await db.query2('user').include('location', 'burp').get(), [ +   ~~~~~~~ + +test/string.ts:122:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { readonly type: "string"; }; ... 5 more ...; readonly location: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 ...'. + +122 .include( +   ~~~~~~~ + +test/string.ts:258:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +258 .include('user.name', 'user.myBlup.name') +   ~~~~~~~ + +test/string.ts:283:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly myBlup: { readonly ref: "blup"; readonly prop: "user"; } & { ...; }; ... 7 more ...; readonly location: Omit<...> & { ...; }; }; }; re...'. + +283 await db.query2('simple').include('user.name', 'user.myBlup.name').get(), +   ~~~~~~~ + +test/string.ts:322:30 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "file", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "file", id: number | (Partial; }, "file">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "file">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial; }, "file">> & { ...; }'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +322 (await db.query2('file', file).get()).node().contents, +   ~~~~ + + test/string.ts:322:30 + 322 (await db.query2('file', file).get()).node().contents, +    ~~~~ + Did you forget to use 'await'? + +test/string.ts:322:43 - error TS2339: Property 'node' does not exist on type '{ id: number; contents: string; }[]'. + +322 (await db.query2('file', file).get()).node().contents, +   ~~~~ + +test/string.ts:333:41 - error TS2339: Property 'size' does not exist on type '{ id: number; contents: string; }[]'. + +333 equal((await db.query2('file').get()).size > 1000 * 1e3, true) +   ~~~~ + +test/string.ts:357:7 - error TS2322: Type 'Buffer' is not assignable to type 'string'. + +357 contents: x, +   ~~~~~~~~ + +test/string.ts:398:30 - error TS2769: No overload matches this call. + Overload 1 of 2, '(type: "file", id?: number[] | undefined): BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { ...; }; locales: Partial<...>; }, ... 5 more ..., undefined>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number[]'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is missing the following properties from type 'number[]': length, pop, push, concat, and 35 more. + Overload 2 of 2, '(type: "file", id: number | (Partial; }, "file">> & { ...; })): BasedQuery2<...>', gave the following error. + Argument of type 'BasedCreatePromise' is not assignable to parameter of type 'number | (Partial; }, "file">> & { ...; })'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type 'Partial; }, "f...'. + Type 'BasedModify<(schema: SchemaOut, type: string, payload: Record, buf: AutoSizedUint8Array, lang: LangCodeEnum) => void>' is not assignable to type '{ [Symbol.toStringTag]?: undefined; }'. + Types of property '[Symbol.toStringTag]' are incompatible. + Type '"BasedModify"' is not assignable to type 'undefined'. + +398 (await db.query2('file', file).get()).node().contents, +   ~~~~ + + test/string.ts:398:30 + 398 (await db.query2('file', file).get()).node().contents, +    ~~~~ + Did you forget to use 'await'? + +test/string.ts:398:43 - error TS2339: Property 'node' does not exist on type '{ id: number; name: string; contents: string; }[]'. + +398 (await db.query2('file', file).get()).node().contents, +   ~~~~ + +test/string.ts:451:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly file: { props: { readonly contentsUncompressed: { ...; }; readonly contentsCompressed: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +451 .include('contentsUncompressed') +   ~~~~~~~ + +test/string.ts:457:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly file: { props: { readonly contentsUncompressed: { ...; }; readonly contentsCompressed: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +457 .include('contentsCompressed') +   ~~~~~~~ + +test/subscription/subscription.perf.ts:36:16 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +36 const q = db.query2('user', 1) +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/subscription/subscription.perf.ts:49:9 - error TS2345: Argument of type 'BasedDb' is not assignable to parameter of type 'DbServer'. + Type 'BasedDb' is missing the following properties from type 'DbServer': dbCtxExternal, migrating, saveInProgress, activeReaders, and 17 more. + +49 db, +   ~~ + +test/subscription/subscription.perf.ts:90:16 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +90 const q = db.query2('user', 1).include('flap') +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/subscription/subscription.perf.ts:104:12 - error TS2339: Property 'addIdSubscription' does not exist on type '{ externalFromInt(address: BigInt): any; intFromExternal(external: any): BigInt; query: (q: Uint8Array, dbCtx: any) => ArrayBuffer | null; ... 14 more ...; selvaLangAll: () => string; }'. + +104 native.addIdSubscription(db.server.dbCtxExternal, q.subscriptionBuffer!) +   ~~~~~~~~~~~~~~~~~ + +test/subscription/subscription.ts:16:40 - error TS2554: Expected 1 arguments, but got 2. + +16 hooks: getDefaultHooks(server, subTime), +   ~~~~~~~ + +test/subscription/subscription.ts:46:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +46 .include('derp') +   ~~~~~~~ + +test/subscription/subscription.ts:81:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +81 .include('lang') +   ~~~~~~~ + +test/subscription/subscription.ts:114:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +114 .include('location') +   ~~~~~~~ + +test/subscription/subscription.ts:149:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +149 .include('derp') +   ~~~~~~~ + +test/subscription/subscription.ts:194:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +194 .include('items') +   ~~~~~~~ + +test/subscription/subscriptionId.ts:15:40 - error TS2554: Expected 1 arguments, but got 2. + +15 hooks: getDefaultHooks(server, time), +   ~~~~ + +test/subscription/subscriptionId.ts:45:47 - error TS2339: Property 'subscribe' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +45 const close = clients[0].query2('user', id).subscribe((d) => { +   ~~~~~~~~~ + +test/subscription/subscriptionId.ts:51:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +51 .include('name') +   ~~~~~~~ + +test/subscription/subscriptionId.ts:107:47 - error TS2339: Property 'subscribe' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +107 const close = clients[0].query2('user', id).subscribe((d) => { +   ~~~~~~~~~ + +test/subscription/subscriptionId.ts:112:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +112 .include('name') +   ~~~~~~~ + +test/subscription/subscriptionId.ts:127:16 - error TS2339: Property 'subscriptions' does not exist on type 'DbServer'. + +127 equal(server.subscriptions.active, 0, 'remove all subs') +   ~~~~~~~~~~~~~ + +test/subscription/subscriptionIdPartial.ts:15:40 - error TS2554: Expected 1 arguments, but got 2. + +15 hooks: getDefaultHooks(server, 1), +   ~ + +test/subscription/subscriptionIdPartial.ts:49:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +49 .filter('x', '>', 5) +   ~~~~~~ + +test/subscription/subscriptionIdPartial.ts:97:47 - error TS2339: Property 'subscribe' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +97 const close = clients[0].query2('user', id).subscribe((d) => { +   ~~~~~~~~~ + +test/subscription/subscriptionIdPartial.ts:103:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +103 .include('x', 'gurk', 'rurp', 'flap') +   ~~~~~~~ + +test/subscription/subscriptionIdRemove.ts:15:40 - error TS2554: Expected 1 arguments, but got 2. + +15 hooks: getDefaultHooks(server, 1), +   ~ + +test/subscription/subscriptionIdRemove.ts:71:58 - error TS2339: Property 'subscribe' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +71 const subscription = clients[0].query2('user', id).subscribe(() => { +   ~~~~~~~~~ + +test/subscription/subscriptionIdRemove.ts:242:16 - error TS2339: Property 'subscriptions' does not exist on type 'DbServer'. + +242 equal(server.subscriptions.active, 0) +   ~~~~~~~~~~~~~ + +test/subscription/subscriptionMulti.perf.ts:15:40 - error TS2554: Expected 1 arguments, but got 2. + +15 hooks: getDefaultHooks(server, subTime), +   ~~~~~~~ + +test/subscription/subscriptionMulti.perf.ts:47:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +47 .filter('derp', '>', 1e6 - 10) +   ~~~~~~ + +test/subscription/subscriptionNow.ts:15:40 - error TS2554: Expected 1 arguments, but got 2. + +15 hooks: getDefaultHooks(server, 100), +   ~~~ + +test/subscription/subscriptionNow.ts:70:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +70 .filter('date', '<', 'now - 2s') +   ~~~~~~ + +test/subscription/subscriptionNow.ts:78:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +78 .filter('date', '<', 'now - 2s') +   ~~~~~~ + +test/subscription/subscriptionNow.ts:93:16 - error TS2339: Property 'subscriptions' does not exist on type 'DbServer'. + +93 equal(server.subscriptions.active, 0, 'Removed all active subs') +   ~~~~~~~~~~~~~ + +test/subscription/subscriptionNow.ts:94:16 - error TS2339: Property 'subscriptions' does not exist on type 'DbServer'. + +94 equal(server.subscriptions.now.listeners.size, 0, 'Remove all now listeners') +   ~~~~~~~~~~~~~ + +test/subscription/subscriptionNow.ts:117:6 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +117 .locale('en') +   ~~~~~~ + +test/subscription/subscriptionNow.ts:143:16 - error TS2339: Property 'subscriptions' does not exist on type 'DbServer'. + +143 equal(server.subscriptions.active, 0, 'Removed all active subs') +   ~~~~~~~~~~~~~ + +test/subscription/subscriptionNow.ts:144:16 - error TS2339: Property 'subscriptions' does not exist on type 'DbServer'. + +144 equal(server.subscriptions.now.listeners.size, 0, 'Remove all now listeners') +   ~~~~~~~~~~~~~ + +test/subscription/subscriptionSchemaChanges.ts:16:40 - error TS2554: Expected 1 arguments, but got 2. + +16 hooks: getDefaultHooks(server, 10), +   ~~ + +test/subscription/subscriptionSchemaChanges.ts:52:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +52 .include('derp', 'lang') +   ~~~~~~~ + +test/subscription/subscriptionSchemaChanges.ts:136:6 - error TS2551: Property 'query2' does not exist on type 'BasedDb'. Did you mean 'query'? + +136 db.query2('user').subscribe((res) => { +   ~~~~~~ + + src/index.ts:101:3 + 101 query: DbClient['query'] = function (this: BasedDb) { +    ~~~~~ + 'query' is declared here. + +test/subscription/subscriptionWorkers.perf.ts:64:7 - error TS2345: Argument of type 'BasedDb' is not assignable to parameter of type 'DbServer'. + Type 'BasedDb' is missing the following properties from type 'DbServer': dbCtxExternal, migrating, saveInProgress, activeReaders, and 17 more. + +64 db, +   ~~ + +test/subscription/subscriptionWorkers.perf.ts:71:14 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2'. + +71 .filter('fromCountry', '=', ['AE', 'NL']) +   ~~~~~~ + +test/subscription/subscriptionWorkers.perf.ts:76:16 - error TS2339: Property 'flushTime' does not exist on type 'DbClient'. + +76 client.flushTime = 0 +   ~~~~~~~~~ + +test/text/text.ts:32:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +32 await db.query2('dialog').include('id', 'fun').get(), +   ~~~~~~~ + +test/text/text.ts:47:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +47 await db.query2('dialog').include('id').get(), +   ~~~~~~~ + +test/text/text.ts:57:31 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +57 await db.query2('dialog').locale('it').include('id', 'fun').get(), +   ~~~~~~ + +test/text/text.ts:70:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +70 .locale('it') +   ~~~~~~ + +test/text/text.ts:81:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +81 .include('id', 'fun') +   ~~~~~~~ + +test/text/text.ts:100:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +100 .locale('it') +   ~~~~~~ + +test/text/text.ts:116:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +116 .include('id', 'fun') +   ~~~~~~~ + +test/text/text.ts:126:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +126 .include('id', 'fun') +   ~~~~~~~ + +test/text/text.ts:145:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +145 .locale('en') +   ~~~~~~ + +test/text/text.ts:167:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +167 await db.query2('dialog').include('id', 'fun').locale('fi').get(), +   ~~~~~~~ + +test/text/text.ts:191:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +191 await db.query2('dialog').include('id', 'fun').locale('fi').get(), +   ~~~~~~~ + +test/text/text.ts:236:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +236 .locale('fi') +   ~~~~~~ + +test/text/text.ts:252:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +252 .locale('fi') +   ~~~~~~ + +test/text/text.ts:272:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +272 .include('fun.en') +   ~~~~~~~ + +test/text/text.ts:315:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +315 .include('id', 'fun') +   ~~~~~~~ + +test/text/text.ts:334:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +334 .include('id', 'fun') +   ~~~~~~~ + +test/text/text.ts:351:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +351 await db.query2('dialog').include('id', 'fun').search('snurp', 'fun').get(), +   ~~~~~~~ + +test/text/text.ts:366:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +366 await db.query2('dialog').include('id', 'fun').search('derp', 'fun').get(), +   ~~~~~~~ + +test/text/text.ts:383:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +383 .locale('fi') +   ~~~~~~ + +test/text/text.ts:394:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +394 .locale('en') +   ~~~~~~ + +test/text/text.ts:411:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly fun: { readonly type: "text"; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +411 .include('id', 'fun') +   ~~~~~~~ + +test/text/text.ts:431:13 - error TS2322: Type '{ en: { required: true; }; fr: { required: true; }; }' is not assignable to type '((Partial>> | ("id" | ... 145 more ... | "cnr")[]) & (Partial<...> | ... 1 more ... | (...'. + Types of property 'en' are incompatible. + Object literal may only specify known properties, and 'required' does not exist in type '{ fallback: ("id" | "none" | "aa" | "ab" | "af" | "ak" | "sq" | "am" | "ar" | "an" | "hy" | "as" | "av" | "ae" | "ay" | "az" | "eu" | "be" | "bn" | "bi" | "bs" | "br" | "bg" | "my" | "ca" | ... 121 more ... | "cnr")[]; } | Partial<...> | ({ ...; } & Partial<...>) | (Partial<...> & { ...; })'. + +431 en: { required: true }, +   ~~~~~~~~ + + src/schema/schema/schema.ts:31:3 + 31 locales?: SchemaLocales +    ~~~~~~~ + The expected type comes from property 'locales' which is declared here on type 'StrictSchema' + +test/text/text.ts:453:40 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +453 deepEqual(await db.query2('country').include('*').get(), [ +   ~~~~~~~ + +test/text/text.ts:464:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +464 deepEqual(await db.query2('contestant').include('*', 'country').get(), [ +   ~~~~~~~ + +test/text/text.ts:497:29 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +497 await db.query2('dialog').locale('fi').sort('fun', 'desc').get() +   ~~~~~~ + +test/text/text.ts:524:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +524 .include('fun') +   ~~~~~~~ + +test/text/text.ts:554:31 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +554 await db.query2('dialog').include('fun').sort('fun.fi', 'desc').get(), +   ~~~~~~~ + +test/text/text.ts:567:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +567 .locale('en') +   ~~~~~~ + +test/text/text.ts:587:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +587 .locale('fi') +   ~~~~~~ + +test/text/text.ts:620:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +620 .locale('fi') +   ~~~~~~ + +test/text/text.ts:644:39 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +644 deepEqual(await db.query2('dialog').locale('fi').sort('snurf', 'desc').get(), [ +   ~~~~~~ + +test/text/text.ts:663:39 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +663 deepEqual(await db.query2('dialog').locale('fi').sort('snurf', 'desc').get(), [ +   ~~~~~~ + +test/text/text.ts:670:39 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +670 deepEqual(await db.query2('dialog').locale('fi').sort('fun').get(), [ +   ~~~~~~ + +test/text/text.ts:683:39 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +683 deepEqual(await db.query2('dialog').locale('fi').sort('fun').get(), [ +   ~~~~~~ + +test/text/text.ts:700:39 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +700 deepEqual(await db.query2('dialog').locale('fi').sort('fun').get(), [ +   ~~~~~~ + +test/text/text.ts:718:31 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +718 await db.query2('dialog').locale('fi').sort('fun').get(), +   ~~~~~~ + +test/text/text.ts:736:31 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +736 await db.query2('dialog').locale('fi').sort('fun').get(), +   ~~~~~~ + +test/text/text.ts:755:7 - error TS2322: Type 'null' is not assignable to type 'string | undefined'. + +755 fi: null, +   ~~ + +test/text/text.ts:761:31 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly dialog: { props: { readonly snurf: { type: "string"; }; readonly fun: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +761 await db.query2('dialog').locale('fi').sort('fun').get(), +   ~~~~~~ + +test/text/text.ts:932:20 - error TS2531: Object is possibly 'null'. + +932 const checksum = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/text/text.ts:932:59 - error TS2339: Property 'checksum' does not exist on type '{ id: number; article: { en: string; it: string; }; }'. + +932 const checksum = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~ + +test/text/text.ts:941:21 - error TS2531: Object is possibly 'null'. + +941 const checksum2 = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +test/text/text.ts:941:60 - error TS2339: Property 'checksum' does not exist on type '{ id: number; article: { en: string; it: string; }; }'. + +941 const checksum2 = (await db.query2('user', user1).get()).checksum +   ~~~~~~~~ + +test/text/textFallback.ts:72:8 - error TS2339: Property 'locale' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly project: { props: { readonly createdAt: { ...; }; readonly title: { ...; }; readonly description: { ...; }; readonly abstract: { ...; }; }; }; }; locales: Parti...'. + +72 .locale('nl') +   ~~~~~~ + +test/text/textFilter.ts:89:32 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly project: { props: { readonly createdAt: { ...; }; readonly title: { ...; }; readonly description: { ...; }; readonly abstract: { ...; }; }; }; }; locales: Parti...'. + +89 await db.query2('project').search(term, 'title', 'abstract').get() +   ~~~~~~ + +test/text/textFilter.ts:103:36 - error TS2339: Property 'search' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly project: { props: { readonly createdAt: { ...; }; readonly title: { ...; }; readonly description: { ...; }; readonly abstract: { ...; }; }; }; }; locales: Parti...'. + +103 await db.query2('project').search(term, 'title', 'abstract').get() +   ~~~~~~ + +test/text/textFilter.ts:190:30 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly function: { props: { readonly name: { type: "alias"; }; ... 10 more ...; readonly "measurements.history": { ...; }; }; }; readonly measurement: { ...; }; readon...'. + +190 await db.query2('event').filter('msg', 'includes', 'derp').get(), +   ~~~~~~ + +test/type-gen/examples/helloWorld/index.ts:1:31 - error TS2307: Cannot find module '@based/functions' or its corresponding type declarations. + +1 import { BasedFunction } from '@based/functions' +   ~~~~~~~~~~~~~~~~~~ + +test/type-gen/examples/query/index.ts:1:36 - error TS2307: Cannot find module '@based/functions' or its corresponding type declarations. + +1 import { BasedQueryFunction } from '@based/functions' +   ~~~~~~~~~~~~~~~~~~ + +test/update.ts:204:40 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mep: { props: { readonly a: { readonly type: "uint32"; }; readonly countryCode: { ...; }; readonly b: { ...; }; readonly c: { ...; }; }; }; readonly snurp: { .....'. + +204 equal((await db.query2('snurp', ids).range(0, 100).get()).length, 100) +   ~~~~~ + +test/update.ts:206:40 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mep: { props: { readonly a: { readonly type: "uint32"; }; readonly countryCode: { ...; }; readonly b: { ...; }; readonly c: { ...; }; }; }; readonly snurp: { .....'. + +206 equal((await db.query2('snurp', ids).range(10, 110).get()).length, 100) +   ~~~~~ + +test/update.ts:211:8 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mep: { props: { readonly a: { readonly type: "uint32"; }; readonly countryCode: { ...; }; readonly b: { ...; }; readonly c: { ...; }; }; }; readonly snurp: { .....'. + +211 .range(1e5, 1e5 + 2) +   ~~~~~ + +test/update.ts:241:43 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly mep: { props: { readonly a: { readonly type: "uint32"; }; readonly countryCode: { ...; }; readonly b: { ...; }; readonly c: { ...; }; }; }; readonly snurp: { .....'. + +241 promises.push(db.query2('snurp', i).include('a').get()) +   ~~~~~~~ + +test/upsert.ts:86:45 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { [x: string]: { partial?: boolean | undefined; ... 4 more ...; props: any; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +86 deepEqual(await client1.query2('article').include('*', '**').get(), [ +   ~~~~~~~ + +test/validation/validation.ts:162:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly json: { type: "json"; }; ... 17 more ...; readonly connections: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +162 .include('cardinality') +   ~~~~~~~ + +test/validation/validation.ts:286:29 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly json: { type: "json"; }; ... 17 more ...; readonly connections: Omit<...> & { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more .....'. + +286 await db.query2('user').include('name', 'friend').get(), +   ~~~~~~~ + +test/validation/validation.ts:605:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +605 .filter('friend.description.en', '=', 'nice') +   ~~~~~~ + +test/validation/validation.ts:663:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +663 () => db.query2('user').filter('rating', 'includes', 1).get(), +   ~~~~~~ + +test/validation/validation.ts:680:29 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +680 () => db.query2('user').filter('friend', 'includes', 1).get(), +   ~~~~~~ + +test/validation/validation.ts:708:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +708 .filter('name', 'includes', '') +   ~~~~~~ + +test/validation/validation.ts:718:8 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +718 .filter('name', 'includes', '') +   ~~~~~~ + +test/validation/validation.ts:776:27 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +776 await db.query2('user').sort('drip', 'desc').get() +   ~~~~ + +test/validation/validation.ts:780:31 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +780 await db.query2('user').sort('flurp').get() +   ~~~~ + +test/validation/validation.ts:792:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +792 await db.query2('user').sort('connections').get() +   ~~~~ + +test/validation/validation.ts:796:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +796 await db.query2('user').sort('friend').get() +   ~~~~ + +test/validation/validation.ts:800:32 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +800 await db.query2('user', 1).sort('drip').get() +   ~~~~ + +test/validation/validation.ts:803:31 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +803 await db.query2('user', []).sort('drip').get() +   ~~~~ + +test/validation/validation.ts:805:38 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +805 await db.query2('user', [1, 2, 3]).sort('drip').get() +   ~~~~ + +test/validation/validation.ts:808:29 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly todo: { props: { readonly done: { type: "boolean"; }; ... 4 more ...; readonly body: { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5...'. + +808 await db.query2('user').sort('drip').range(0, -10).get() +   ~~~~ + +test/validation/validation.ts:929:27 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +929 await db.query2('user').range(0, 5).get() +   ~~~~~ + +test/validation/validation.ts:930:27 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +930 await db.query2('user').range(1, 10).get() +   ~~~~~ + +test/validation/validation.ts:931:27 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +931 await db.query2('user').range(0, 1).get() +   ~~~~~ + +test/validation/validation.ts:932:27 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +932 await db.query2('user').range(100, 101).get() +   ~~~~~ + +test/validation/validation.ts:933:27 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +933 await db.query2('user').range(1000, 1001).get() +   ~~~~~ + +test/validation/validation.ts:934:27 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +934 await db.query2('user').range(0, undefined).get() +   ~~~~~ + +test/validation/validation.ts:937:29 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +937 await db.query2('user').range(0, 0).get() +   ~~~~~ + +test/validation/validation.ts:941:29 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +941 await db.query2('user').range(5, 5).get() +   ~~~~~ + +test/validation/validation.ts:945:29 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +945 await db.query2('user').range(4294967295, 4294967295).get() +   ~~~~~ + +test/validation/validation.ts:1024:29 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1024 await db.query2('user').range(5, 3).get() +   ~~~~~ + +test/validation/validation.ts:1027:27 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1027 await db.query2('user').filter('rating', '>', 0).range(0, 5).get() +   ~~~~~~ + +test/validation/validation.ts:1028:27 - error TS2339: Property 'sort' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1028 await db.query2('user').sort('rating').range(0, 5).get() +   ~~~~ + +test/validation/validation.ts:1029:27 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1029 await db.query2('user').include('name').range(0, 5).get() +   ~~~~~~~ + +test/validation/validation.ts:1033:6 - error TS2339: Property 'range' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly rating: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1033 .range(0, 5) +   ~~~~~ + +test/validation/validation.ts:1079:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly binaryData: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1079 .filter('binaryData', '=', Buffer.from([1, 2, 3, 4])) +   ~~~~~~ + +test/validation/validation.ts:1084:6 - error TS2339: Property 'filter' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly user: { props: { readonly name: { type: "string"; }; readonly binaryData: { ...; }; }; }; }; locales: Partial<...>; }, ... 5 more ..., undefined>'. + +1084 .filter('binaryData', '=', new Uint8Array([5, 6, 7, 8])) +   ~~~~~~ + +test/validation/validationReferences.ts:68:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly flap: { props: { readonly x: { readonly ref: "user"; readonly prop: "y"; } & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +68 .include('name', 'connections.id') +   ~~~~~~~ + +test/validation/validationReferences.ts:177:46 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly flap: { props: { readonly x: { readonly ref: "user"; readonly prop: "y"; } & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +177 await db.query2('user', userWithFriends).include('name', 'friends').get(), +   ~~~~~~~ + +test/validation/validationReferences.ts:198:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly flap: { props: { readonly x: { readonly ref: "user"; readonly prop: "y"; } & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +198 .include( +   ~~~~~~~ + +test/validation/validationReferences.ts:244:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2<{ version?: string | undefined; defaultTimezone?: string | undefined; migrations?: SchemaMigrations | undefined; hash: number; types: { readonly flap: { props: { readonly x: { readonly ref: "user"; readonly prop: "y"; } & { ...; }; }; }; readonly user: { ...; }; }; locales: Partial<...>; }, ... 5 more .....'. + +244 .include( +   ~~~~~~~ + +test/vector.ts:50:39 - error TS2339: Property 'include' does not exist on type 'BasedQuery2'. + +50 const res = await db.query2('data').include('name', 'a').get() +   ~~~~~~~ + +test/vector.ts:63:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2'. + +63 .include('name') +   ~~~~~~~ + +test/vector.ts:70:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2'. + +70 .include('name') +   ~~~~~~~ + +test/vector.ts:83:6 - error TS2339: Property 'include' does not exist on type 'BasedQuery2'. + +83 .include('name') +   ~~~~~~~ + +test/vector.ts:105:8 - error TS2339: Property 'include' does not exist on type 'BasedQuery2'. + +105 .include('name') +   ~~~~~~~ + + +Found 1233 errors in 187 files. + +Errors Files + 1 scripts/repl.ts:6 + 2 scripts/test_push_exports.ts:13 + 48 src/db-client/query2/index.ts:73 + 3 src/db-client/query2/types.ts:347 + 26 test/aggregate/basic.ts:55 + 14 test/aggregate/deep.ts:57 + 2 test/aggregate/experimental.ts:90 + 13 test/aggregate/groupBY.ts:52 + 6 test/aggregate/multiple.ts:77 + 10 test/aggregate/overall.perf.ts:41 + 19 test/aggregate/temporal.ts:32 + 5 test/aggregate/validation.ts:29 + 8 test/alias/alias.ts:43 + 1 test/alias/filter.ts:33 + 1 test/alignModify.ts:45 + 1 test/based-client/addSpecs.ts:57 + 4 test/based-client/authorize.ts:139 + 1 test/based-client/authorizeOnSpec.ts:94 + 2 test/based-client/browser/index.ts:78 + 4 test/based-client/dbQuery.ts:32 + 2 test/based-client/error.ts:140 + 1 test/based-client/functions.ts:3 + 1 test/based-client/functionsPerf.ts:2 + 20 test/based-client/get.ts:2 + 6 test/based-client/hooks.ts:2 + 1 test/based-client/http.ts:2 + 1 test/based-client/httpGet.ts:2 + 1 test/based-client/installFunctions.ts:2 + 2 test/based-client/lazyConnect.ts:2 + 1 test/based-client/memLeaks.ts:2 + 2 test/based-client/messages.ts:2 + 1 test/based-client/nestedChannelSimple.ts:2 + 13 test/based-client/nestedFunctions.ts:3 + 3 test/based-client/nestedFunctionsError.ts:2 + 4 test/based-client/nestedQuerySimple.ts:2 + 5 test/based-client/null.ts:2 + 2 test/based-client/payloadPerf.ts:85 + 5 test/based-client/persist.ts:2 + 16 test/based-client/protocolContentType.ts:3 + 3 test/based-client/query.ts:2 + 3 test/based-client/queryCache.ts:2 + 18 test/based-client/queryCtxBound.ts:2 + 2 test/based-client/queryDiff.ts:2 + 10 test/based-client/queryErrorHandling.ts:2 + 2 test/based-client/queryInstancePerf.ts:2 + 3 test/based-client/queryReusedDiff.ts:2 + 4 test/based-client/queryUint8Payload.ts:2 + 1 test/based-client/rateLimit.ts:2 + 2 test/based-client/reEvaluateAuthState.ts.ts:2 + 3 test/based-client/relay.ts:2 + 1 test/based-client/reload.ts:2 + 4 test/based-client/ssr.ts:2 + 1 test/based-client/stream.ts:2 + 1 test/based-client/streamChunks.ts:4 + 1 test/based-client/streamHttp.ts:2 + 1 test/based-client/streamNested.ts:2 + 2 test/based-client/throttle.ts:2 + 2 test/based-client/verifyAuthState.ts:2 + 5 test/bench.perf.ts:54 + 1 test/benchmarks/utils.ts:1 + 10 test/bigNode.perf.ts:37 + 6 test/binary.ts:21 + 3 test/boolean.ts:34 + 1 test/capped.ts:131 + 16 test/cardinality.ts:45 + 2 test/clientServer.perf.ts:43 + 5 test/clientServer.ts:27 + 3 test/colvec.ts:63 + 2 test/concurrency.perf.ts:46 + 4 test/copy.ts:63 + 2 test/crc32c.ts:115 + 4 test/db-schema/schemaUpdates.ts:45 + 10 test/default.ts:46 + 3 test/delete.perf.ts:58 + 7 test/delete.ts:39 + 9 test/dependent.ts:66 + 2 test/edges/edgeFilterNested.ts:45 + 1 test/edges/edgeNumbers.ts:41 + 18 test/edges/edges.ts:113 + 12 test/edges/edgesMain.ts:60 + 3 test/edges/edgesReference.ts:58 + 18 test/edges/edgesReferences.ts:100 + 5 test/edges/edgeText.ts:39 + 1 test/edges/edgeType.ts:92 + 8 test/enum.ts:34 + 2 test/errors.ts:23 + 10 test/exists.ts:32 + 4 test/expire.ts:40 + 4 test/filter/api.ts:33 + 2 test/filter/edges.ts:55 + 42 test/filter/filter.ts:44 + 4 test/filter/or.ts:21 + 2 test/filter/references.ts:51 + 6 test/filter/referencesField.ts:32 + 34 test/filter/string.ts:18 + 1 test/HLLunion.ts:86 + 14 test/hooks.ts:67 + 5 test/include/include.ts:26 + 12 test/include/includeMeta.ts:39 + 2 test/include/includeNested.ts:26 + 7 test/include/includeSlice.ts:58 + 3 test/include/referencesField.ts:34 + 6 test/include/thread.perf.ts:79 + 1 test/isModified.perf.ts:23 + 4 test/json.ts:82 + 2 test/locales.ts:39 + 2 test/mem.ts:57 + 2 test/migration.ts:227 + 2 test/modify/props/binary.ts:63 + 9 test/modify/props/boolean.ts:94 + 2 test/modify/props/cardinality.ts:74 + 3 test/modify/props/default.ts:102 + 2 test/modify/props/enum.ts:65 + 2 test/modify/props/json.ts:66 + 2 test/modify/props/mixed.ts:41 + 1 test/modify/props/numbers.ts:241 + 4 test/modify/props/object.ts:56 + 8 test/modify/props/references.ts:26 + 6 test/modify/props/string.ts:111 + 3 test/modify/props/text.ts:71 + 2 test/modify/props/timestamp.ts:100 + 6 test/modify/props/vector.ts:108 + 2 test/protocol/schema.ts:59 + 1 test/query-ast/validate.perf.ts:1 + 7 test/query/db.ts:66 + 23 test/query/types.ts:110 + 8 test/queryResponse.ts:25 + 2 test/range.ts:59 + 2 test/raw.ts:21 + 34 test/references/references.ts:61 + 14 test/references/referencesIndex.ts:37 + 6 test/references/referencesModify.ts:49 + 2 test/save/blockHash.ts:50 + 15 test/save/save.ts:197 + 1 test/save/saveEdge.ts:43 + 6 test/save/saveRange.ts:63 + 7 test/scenarios/e-commerce.ts:1 + 47 test/scenarios/northwind.ts:12 + 20 test/scenarios/nycTaxi.ts:429 + 4 test/scenarios/vote.ts:155 + 2 test/scenarios/voteEdges.ts:132 + 4 test/scenarios/voteLargeAmounts.perf.ts:138 + 5 test/scenarios/voteStorage.ts:131 + 4 test/schema/parse/edges.ts:36 + 1 test/schema/parse/infer.ts:1 + 1 test/schema/parse/references.ts:211 + 1 test/schema/parse/schema.ts:12 + 1 test/schema/props/write.ts:20 + 4 test/schema/serialize/serialize.ts:46 + 27 test/search.ts:29 + 3 test/serializeQueryDef.ts:33 + 2 test/shared/test.ts:89 + 3 test/simpleQuery.ts:36 + 33 test/singleRef.ts:100 + 2 test/singleRefQuery.ts:108 + 2 test/sort/sort.perf.ts:27 + 29 test/sort/sort.ts:56 + 4 test/sort/sortAlias.ts:28 + 3 test/sort/sortBinary.ts:38 + 3 test/sort/sortById.ts:33 + 2 test/sort/sortEnum.ts:37 + 8 test/sort/sortHll.ts:55 + 8 test/sort/sortIds.ts:31 + 1 test/sort/sortNodeId.ts:27 + 11 test/sort/sortNumber.ts:52 + 4 test/sort/sortString.ts:70 + 8 test/sort/sortTimestamp.ts:43 + 17 test/string.ts:42 + 4 test/subscription/subscription.perf.ts:36 + 6 test/subscription/subscription.ts:16 + 6 test/subscription/subscriptionId.ts:15 + 4 test/subscription/subscriptionIdPartial.ts:15 + 3 test/subscription/subscriptionIdRemove.ts:15 + 2 test/subscription/subscriptionMulti.perf.ts:15 + 8 test/subscription/subscriptionNow.ts:15 + 3 test/subscription/subscriptionSchemaChanges.ts:16 + 3 test/subscription/subscriptionWorkers.perf.ts:64 + 43 test/text/text.ts:32 + 1 test/text/textFallback.ts:72 + 3 test/text/textFilter.ts:89 + 1 test/type-gen/examples/helloWorld/index.ts:1 + 1 test/type-gen/examples/query/index.ts:1 + 4 test/update.ts:204 + 1 test/upsert.ts:86 + 31 test/validation/validation.ts:162 + 4 test/validation/validationReferences.ts:68 + 5 test/vector.ts:50 diff --git a/src/db-client/index.ts b/src/db-client/index.ts index 708273bdf7..fc08225888 100644 --- a/src/db-client/index.ts +++ b/src/db-client/index.ts @@ -121,7 +121,7 @@ export class DbClientClass< create( type: T, - obj: InferPayload, + obj?: InferPayload, opts?: ModifyOpts, ): BasedCreatePromise { return new BasedModify( @@ -129,7 +129,7 @@ export class DbClientClass< serializeCreate, this.schema!, type, - obj, + obj ?? {}, this.modifyCtx.buf, opts?.locale ? LangCode[opts.locale] : LangCode.none, ) diff --git a/src/db-client/query2/index.ts b/src/db-client/query2/index.ts index 5b953475c7..5b18c97908 100644 --- a/src/db-client/query2/index.ts +++ b/src/db-client/query2/index.ts @@ -22,7 +22,7 @@ import type { StepInput, aggFnOptions } from '../query/aggregates/types.js' import { readUint32 } from '../../utils/uint8.js' class Query< - S extends { types: any } = { types: any }, + S extends { types: any; locales?: any } = { types: any }, T extends keyof S['types'] = any, K extends | keyof ResolvedProps @@ -41,12 +41,32 @@ class Query< this.ast = ast } ast: QueryAst + + locale< + L extends string & + (S['locales'] extends Record ? keyof S['locales'] : string), + >( + locale: L, + ): NextBranch< + { types: S['types']; locales: L }, + T, + K, + IsSingle, + SourceField, + IsRoot, + EdgeProps, + Aggregate, + GroupedKey + > { + this.ast.locale = locale + return this as any + } include< F extends [ ( | 'id' | (keyof (ResolvedProps & EdgeProps) & string) - | Path + | Path | '*' | '**' | ((q: SelectFn) => AnyQuery) @@ -54,7 +74,7 @@ class Query< ...( | 'id' | (keyof (ResolvedProps & EdgeProps) & string) - | Path + | Path | '*' | '**' | ((q: SelectFn) => AnyQuery) @@ -92,9 +112,7 @@ class Query< ) => FilterBranch>, ): FilterBranch filter< - P extends - | keyof (ResolvedProps & EdgeProps) - | Path, + P extends keyof (ResolvedProps & EdgeProps) | Path, >( prop: P, op: Operator, @@ -111,11 +129,7 @@ class Query< filter: FilterFn, ) => FilterBranch>, ): FilterBranch - and< - P extends - | keyof (ResolvedProps & EdgeProps) - | Path, - >( + and

& EdgeProps) | Path>( prop: P, op: Operator, val: InferPathType, @@ -130,11 +144,7 @@ class Query< filter: FilterFn, ) => FilterBranch>, ): FilterBranch - or< - P extends - | keyof (ResolvedProps & EdgeProps) - | Path, - >( + or

& EdgeProps) | Path>( prop: P, op: Operator, val: InferPathType, @@ -653,13 +663,13 @@ type FilterMethods = { // This overload is for when the user provides NO schema argument, rely on generic default or explicit generic export function query< - S extends { types: any } = { types: any }, + S extends { types: any; locales?: any } = { types: any }, T extends keyof S['types'] & string = keyof S['types'] & string, >(type: T): Query // This overload is for when the user provides NO schema argument + ID, rely on generic default or explicit generic export function query< - S extends { types: any } = { types: any }, + S extends { types: any; locales?: any } = { types: any }, T extends keyof S['types'] & string = keyof S['types'] & string, >( type: T, @@ -667,7 +677,7 @@ export function query< ): Query export function query< - S extends { types: any }, + S extends { types: any; locales?: any }, T extends keyof S['types'] & string = keyof S['types'] & string, >( type: T, @@ -679,7 +689,7 @@ export function query< } export class BasedQuery2< - S extends { types: any } = { types: any }, + S extends { types: any; locales?: any } = { types: any }, T extends keyof S['types'] = any, K extends | keyof ResolvedProps @@ -701,6 +711,20 @@ export class BasedQuery2< if (target) this.ast.target = target this.db = db } + + testGroupedKey(): GroupedKey { + return null as any + } + testAggregate(): Aggregate { + return null as any + } + testIsSingle(): IsSingle { + return null as any + } + testK(): K { + return null as any + } + db: DbClient async get(): Promise< [GroupedKey] extends [string] @@ -745,7 +769,7 @@ export class BasedQuery2< } type FilterFn< - S extends { types: any }, + S extends { types: any; locales?: any }, T extends keyof S['types'], EdgeProps extends Record, > = FilterSignature< @@ -756,7 +780,7 @@ type FilterFn< > type FilterSignature< - S extends { types: any }, + S extends { types: any; locales?: any }, T extends keyof S['types'], EdgeProps extends Record, Result, @@ -766,11 +790,7 @@ type FilterSignature< filter: FilterFn, ) => FilterBranch>, ): Result - < - P extends - | keyof (ResolvedProps & EdgeProps) - | Path, - >( +

& EdgeProps) | Path>( prop: P, op: Operator, val: InferPathType, @@ -778,9 +798,10 @@ type FilterSignature< ): Result } -type SelectFn = < - P extends keyof ResolvedProps, ->( +type SelectFn< + S extends { types: any; locales?: any }, + T extends keyof S['types'], +> =

>( field: P, ) => Query< S, @@ -837,7 +858,7 @@ type ResolveAggregate = : never // Helper type to simplify include signature -type AnyQuery = Query< +type AnyQuery = Query< S, any, any, @@ -851,7 +872,7 @@ type AnyQuery = Query< // Helper type to simplify method return types type NextBranch< - S extends { types: any }, + S extends { types: any; locales?: any }, T extends keyof S['types'], K extends | keyof ResolvedProps diff --git a/src/db-client/query2/types.ts b/src/db-client/query2/types.ts index 0d71f0dbc8..71c7839ed0 100644 --- a/src/db-client/query2/types.ts +++ b/src/db-client/query2/types.ts @@ -7,7 +7,7 @@ export type InferSchemaOutput< > = InferType< ResolvedProps, S['types'], - S['locales'] extends Record ? S['locales'] : {} + S['locales'] extends string | Record ? S['locales'] : {} > & { id: number } type TypeMap = { @@ -55,13 +55,17 @@ export type PickOutputFromProps< ? InferProp< Props[P], S['types'], - S['locales'] extends Record ? S['locales'] : {}, + S['locales'] extends string | Record + ? S['locales'] + : {}, '-*' > : InferProp< Props[P], S['types'], - S['locales'] extends Record ? S['locales'] : {} + S['locales'] extends string | Record + ? S['locales'] + : {} > : never } & { @@ -69,7 +73,7 @@ export type PickOutputFromProps< keyof Props]: InferProp< Props[Field], S['types'], - S['locales'] extends Record ? S['locales'] : {}, + S['locales'] extends string | Record ? S['locales'] : {}, Extract['select'] > } @@ -78,7 +82,7 @@ export type PickOutputFromProps< export type InferProp< Prop, Types, - Locales extends Record = Record, + Locales extends string | Record = Record, Selection = never, > = IsSelected extends false @@ -90,10 +94,12 @@ export type InferProp< type InferPropLogic< Prop, Types, - Locales extends Record = Record, + Locales extends string | Record = Record, Selection = never, > = Prop extends { type: 'text' } - ? { [K in keyof Locales]-?: string } + ? Locales extends string + ? string + : { [K in Exclude]-?: string } : Prop extends { type: 'object'; props: infer P } ? InferType : Prop extends { type: infer T extends keyof TypeMap } @@ -133,7 +139,7 @@ type InferPropLogic< type InferType< Props, Types, - Locales extends Record = Record, + Locales extends string | Record = Record, > = { [K in keyof Props]: InferProp } @@ -192,7 +198,9 @@ export type PickOutput< ? InferProp< ResolvedProps[P], S['types'], - S['locales'] extends Record ? S['locales'] : {}, + S['locales'] extends string | Record + ? S['locales'] + : {}, '-*' > : InferSchemaOutput[P] @@ -202,7 +210,7 @@ export type PickOutput< keyof ResolvedProps]: InferProp< ResolvedProps[Field], S['types'], - S['locales'] extends Record ? S['locales'] : {}, + S['locales'] extends string | Record ? S['locales'] : {}, Extract['select'] > } @@ -237,49 +245,61 @@ export type Operator = type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // Helper to generate paths from edges -export type EdgePaths = { +export type EdgePaths< + S extends { types: any; locales?: any }, + Prop, + Depth extends number, +> = { [K in keyof FilterEdges & string]: | K | (FilterEdges[K] extends { ref: infer R extends string } - ? `${K}.${Path | 'id' | '*' | '**'}` + ? `${K}.${Path | 'id' | '*' | '**'}` : FilterEdges[K] extends { items: { ref: infer R extends string } } - ? `${K}.${Path | 'id' | '*' | '**'}` + ? `${K}.${Path | 'id' | '*' | '**'}` : never) }[keyof FilterEdges & string] -type PropsPath = [Depth] extends [never] +type PropsPath< + S extends { types: any; locales?: any }, + Props, + Depth extends number, +> = [Depth] extends [never] ? never : { [K in keyof Props & string]: | K | (Props[K] extends { ref: infer R extends string } ? `${K}.${ - | Path - | EdgePaths + | Path + | EdgePaths | 'id' | '*' | '**'}` : Props[K] extends { props: infer P } - ? `${K}.${PropsPath}` - : Props[K] extends { - items: { ref: infer R extends string } & infer Items - } - ? `${K}.${ - | Path - | EdgePaths - | 'id' - | '*' - | '**'}` - : never) + ? `${K}.${PropsPath}` + : Props[K] extends { type: 'text' } + ? S['locales'] extends string + ? never + : `${K}.${keyof (S['locales'] extends Record ? S['locales'] : Record) & string}` + : Props[K] extends { + items: { ref: infer R extends string } & infer Items + } + ? `${K}.${ + | Path + | EdgePaths + | 'id' + | '*' + | '**'}` + : never) }[keyof Props & string] export type Path< - Schema, - T extends keyof Schema, + S extends { types: any; locales?: any }, + T extends keyof S['types'], Depth extends number = 5, -> = PropsPath, Depth> +> = PropsPath, Depth> export type ResolveDotPath = T extends `${infer Head}.${infer Tail}` @@ -310,17 +330,27 @@ type InferPropsPathType< : InferPathType : Props[Head] extends { props: infer NestedProps } ? InferPropsPathType - : Props[Head] extends { - items: { ref: infer R extends string } & infer Items - } - ? Tail extends keyof FilterEdges - ? InferProp< - Items[Tail & keyof Items], - S['types'], - S['locales'] extends Record ? S['locales'] : {} - > - : InferPathType - : never + : Props[Head] extends { type: 'text' } + ? S['locales'] extends string + ? never + : Tail extends keyof (S['locales'] extends Record + ? S['locales'] + : Record) + ? string + : never + : Props[Head] extends { + items: { ref: infer R extends string } & infer Items + } + ? Tail extends keyof FilterEdges + ? InferProp< + Items[Tail & keyof Items], + S['types'], + S['locales'] extends string | Record + ? S['locales'] + : {} + > + : InferPathType + : never : never : never @@ -335,8 +365,8 @@ export type NumberPaths< S extends { types: any; locales?: any }, T extends keyof S['types'], > = { - [K in Path]: InferPathType extends number ? K : never -}[Path] + [K in Path]: InferPathType extends number ? K : never +}[Path] export type ExpandDotPath< T extends string, diff --git a/src/db-query/BasedDbQuery.ts b/src/db-query/BasedDbQuery.ts deleted file mode 100644 index ed3044bd8e..0000000000 --- a/src/db-query/BasedDbQuery.ts +++ /dev/null @@ -1,27 +0,0 @@ -// QUERY TIME -// you need to pass hooks - -// basedClient.db -// toJSON() based DB query -> .ast -// toObject() -// new BasedDbQuery(type, target?, { -//. subscribeSchema -// subscribe -// get -// }?) - -// const b = new BasedDbQuery('user').include('name') -// console.log(b.ast) - -// { -// query () { - -//} -//} query(type, ddf) -// db.query('type', target?) => new BasedDbQuery(type, target, this.hooks) - -// client.query('ui-query', dbQuery('user', 1).include('x').filter('x', '>', 10)) <-- ast -// db.query(DBQUERY-AST).subscribe() -// .include('name')) - -// const x = await db.query('user').include('name').get() diff --git a/src/db-query/BasedDbQueryResult.ts b/src/db-query/BasedDbQueryResult.ts deleted file mode 100644 index ed12a7cb72..0000000000 --- a/src/db-query/BasedDbQueryResult.ts +++ /dev/null @@ -1,19 +0,0 @@ -// const r = new BasedQueryResult(READ_SCHEMA, ?result) -// r.update(result) -// r.inspect() - -// node getter -// BASED-RESPONSE -// { data, error, checksum } -// .data -> -// for (const node of response) {} -// .map -// .id - -// length -// map -// iterator - -/* -

{useQuery().map(v =>
{v.title}
)}
-*/ diff --git a/test/query/db.ts b/test/query/db.ts index c868123e3b..98eb2629b0 100644 --- a/test/query/db.ts +++ b/test/query/db.ts @@ -17,6 +17,7 @@ await test('query db', async (t) => { street: 'string', }, }, + story: 'text', friend: { ref: 'user', prop: 'friend', @@ -193,6 +194,10 @@ await test('query db', async (t) => { address: { street: 'Cool street', }, + story: { + en: '', + nl: '', + }, }, }, ], @@ -210,6 +215,10 @@ await test('query db', async (t) => { address: { street: 'Cool street', }, + story: { + en: '', + nl: '', + }, }, }, ], diff --git a/test/text/textFilter.ts b/test/text/textFilter.ts index 9d06474d89..c441ec9831 100644 --- a/test/text/textFilter.ts +++ b/test/text/textFilter.ts @@ -6,7 +6,9 @@ import { deepEqual } from '../shared/assert.js' await test('textFilter', async (t) => { const db = await testDb(t, { locales: { - en: { /* required: true */ }, + en: { + /* required: true */ + }, nl: {}, }, types: { @@ -23,25 +25,31 @@ await test('textFilter', async (t) => { }, }, }) - const dbx = testDb(t, { - locales: { - en: { /* required: true */ }, - nl: {}, - }, - types: { - project: { - props: { - createdAt: { - type: 'timestamp', - on: 'create', + const dbx = testDb( + t, + { + locales: { + en: { + /* required: true */ + }, + nl: {}, + }, + types: { + project: { + props: { + createdAt: { + type: 'timestamp', + on: 'create', + }, + title: { type: 'text' }, + description: { type: 'text' }, + abstract: { type: 'string' }, }, - title: { type: 'text' }, - description: { type: 'text' }, - abstract: { type: 'string' }, }, }, }, - }, { noBackup: true, path: join(t.tmp, 'x') }) + { noBackup: true, path: join(t.tmp, 'x') }, + ) await db.create( 'project', diff --git a/test/validation/validation.ts b/test/validation/validation.ts index ddce7a39ac..fcc0a8f1a4 100644 --- a/test/validation/validation.ts +++ b/test/validation/validation.ts @@ -39,6 +39,7 @@ await test('update', async (t) => { }) await throws(async () => { + // @ts-expect-error await db.query2('derp', { flap: 'snru' }).get() }, true) @@ -51,6 +52,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { text: { + // @ts-expect-error en: 123, }, }) @@ -60,6 +62,7 @@ await test('update', async (t) => { db.create( 'user', { + // @ts-expect-error text: 123, }, { locale: 'en' }, @@ -68,6 +71,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { + // @ts-expect-error text: { xh: 'hello!' }, }) }) @@ -84,11 +88,13 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { + // @ts-expect-error name: 1, }) }) await throws(async () => { + // @ts-expect-error await db.create('user', { date: {} }) }) @@ -97,11 +103,13 @@ await test('update', async (t) => { }) await throws(async () => { + // @ts-expect-error await db.create('user', { on: 255 + 1 }) }) await throws(async () => { db.create('user', { + // @ts-expect-error number: 'nla', }) }) @@ -120,6 +128,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { + // @ts-expect-error derp: [1, 2, 3, 4], }) }) @@ -142,29 +151,39 @@ await test('update', async (t) => { await throws(async () => { await db.update('user', cId, { + // @ts-expect-error cardinality: ['a', 'b', 1], }) }) - deepEqual(await db.query2('user', cId).include('cardinality').get(), { - id: await cId, - cardinality: 2, - }) + deepEqual( + await db + .query2('user', await cId) + .include('cardinality') + .get(), + { + id: await cId, + cardinality: 2, + }, + ) await throws(async () => { db.create('user', { + // @ts-expect-error cardinality: [1, 2, 3, 4], }) }) await throws(async () => { db.create('user', { + // @ts-expect-error cardinality: { id: [1, 2, 3, 4] }, }) }) await throws(async () => { db.create('user', { + // @ts-expect-error friend: { id: undefined }, }) }) @@ -177,6 +196,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { + // @ts-expect-error name: 1, }) }) @@ -184,6 +204,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { name: 'jamex', + // @ts-expect-error friend: bad, }) }) @@ -191,6 +212,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { name: 'fred', + // @ts-expect-error connections: [good, bad], }) }) @@ -198,6 +220,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { name: 'wrongRating', + // @ts-expect-error u32: 'not a number', }) }) @@ -208,6 +231,7 @@ await test('update', async (t) => { async () => { db.create('user', { name: 'wrongRating', + // @ts-expect-error u32: 'not a number', }).catch((err) => { cnt++ @@ -226,6 +250,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { name: 'fred', + // @ts-expect-error connections: [good, bad], }) }) @@ -233,6 +258,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { name: 'wrongRating', + // @ts-expect-error u32: 'not a number', }) }) @@ -240,6 +266,7 @@ await test('update', async (t) => { await throws(() => db.create('user', { name: 'nope', + // @ts-expect-error randomField: true, }), ) @@ -317,6 +344,7 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { connections: { + // @ts-expect-error set: [], }, }) @@ -324,19 +352,21 @@ await test('update', async (t) => { await throws(async () => { db.create('user', { + // @ts-expect-error connections: 1, }) }) await throws(async () => { db.create('user', { + // @ts-expect-error connections: { add: ['x'], }, }) }) - const id = await db.create('user', undefined) + const id = await db.create('user') await throws( async () => { @@ -434,24 +464,30 @@ await test('update', async (t) => { 'Too small out of bounds value should throw (int8)', ) - db.create('user', { - binaryData: 'not a binary', + await throws(async () => { + db.create('user', { + // @ts-expect-error + binaryData: 'not a binary', + }) }) await throws(async () => { db.create('user', { + // @ts-expect-error binaryData: 12345, }) }) await throws(async () => { db.create('user', { + // @ts-expect-error binaryData: { some: 'object' }, }) }) await throws(async () => { db.create('user', { + // @ts-expect-error binaryData: [1, 2, 3, 4], }) }) @@ -516,10 +552,11 @@ await test('query', async (t) => { 'throw on string as id', ) + // @ts-expect-error await throws(() => db.query2('derp').get(), false, 'non existing type') - // @ts-ignore await throws( + // @ts-expect-error () => db.query2('user', 'derp derp').get(), false, 'incorrect id', @@ -532,29 +569,32 @@ await test('query', async (t) => { ) await throws( - // @ts-ignore + // @ts-expect-error () => db.query2('user', [1, 'X', {}]).get(), false, 'incorrect ids 2', ) const x = new Uint32Array(new Array(2e6).map((v) => 1)) + // @ts-expect-error await throws(() => db.query2('user', x).get(), false, 'incorrect ids 2') await throws( + // @ts-expect-error () => db.query2('user').include('derp').get(), false, 'non existing field in include', ) await throws( - // @ts-ignore + // @ts-expect-error () => db.query2('user', { $id: 1 }).get(), false, 'incorrect alias', ) await throws( + // @ts-expect-error () => db.query2('user').filter('derp', '=', true).get(), false, 'non existing field in filter', @@ -570,6 +610,7 @@ await test('query', async (t) => { await throws( () => + // @ts-expect-error db.query2('user').filter('friend.description.flap', '=', 'nice').get(), false, 'non existing lang in filter', @@ -577,70 +618,72 @@ await test('query', async (t) => { await throws( () => + // @ts-expect-error db.query2('user').filter('friend.description.flap', '=', 'nice').get(), false, 'non existing lang in filter', ) await throws( + // @ts-expect-error () => db.query2('user').filter('friend.description.fr', '=', 'nice').get(), false, 'non existing lang in filter', ) await throws( + // @ts-expect-error () => db.query2('user').include('friend.description.flap').get(), false, 'non existing lang in include #1', ) await throws( + // @ts-expect-error () => db.query2('user').include('friend.description.fr').get(), false, 'non existing lang in include #2', ) await throws( - // @ts-ignore + // @ts-expect-error () => db.query2('user').filter('friend.description.fr', 'derp', 1).get(), false, 'Filter non existing operator', ) await throws( - // @ts-ignore + // @ts-expect-error () => db.query2('user').filter('friend.description.en', '>', 1).get(), false, 'Filter incorrect operator on text', ) await throws( - // @ts-ignore () => db.query2('user').filter('rating', 'includes', 1).get(), false, 'Filter incorrect operator on uint32', ) await throws( - // @ts-ignore + // @ts-expect-error () => db.query2('user').filter('isOn', 'includes', 1).get(), false, 'Filter incorrect operator on bool', ) - await db.query2('user').filter('isOn', true).get() - await db.query2('user').filter('isOn').get() - await db.query2('user').filter('isOn', false).get() + // await db.query2('user').filter('isOn', true).get() + // await db.query2('user').filter('isOn').get() + // await db.query2('user').filter('isOn', false).get() await throws( - // @ts-ignore () => db.query2('user').filter('friend', 'includes', 1).get(), false, 'Filter incorrect operator on reference', ) await throws( - // @ts-ignore + // @ts-expect-error () => db.query2('user').filter('connections', 'like', 1).get(), false, 'Filter incorrect operator on references', @@ -679,15 +722,15 @@ await test('query', async (t) => { 'ignore empty ids', ) - deepEqual( - await db - .query2('user') - .filter('friend.description.en', '=', undefined) - .include('name') - .get(), - allData, - 'skip undefined', - ) + // deepEqual( + // await db + // .query2('user') + // .filter('friend.description.en', '=', undefined) + // .include('name') + // .get(), + // allData, + // 'skip undefined', + // ) await throws( // @ts-ignore @@ -707,6 +750,7 @@ await test('query', async (t) => { 'Incorrect payload', ) + // @ts-expect-error const q = db.query2('flap') for (let i = 0; i < 2; i++) { await throws( @@ -765,68 +809,44 @@ await test('query', async (t) => { }, false) await throws(async () => { - // @ts-ignore + // @ts-expect-error await db.query2('user').sort('drip').range('derp', -100).get() }, false) await throws(async () => { + // @ts-expect-error await db.query2('user').locale('az').get() }, false) - await throws(async () => { - await db.query2('user').search('xyz', 'derpderp').get() - }, false) + // await throws(async () => { + // // @ts-expect-error + // await db.query2('user').search('xyz', 'derpderp').get() + // }, false) - await throws(async () => { - await db.query2('user').search('xyz', 'derpderp').get() - }, false) + // await throws(async () => { + // // @ts-expect-error + // await db.query2('user').search('xyz', 'derpderp').get() + // }, false) - await throws(async () => { - await db.query2('user').search('xyz', 'blap').get() - }, false) + // await throws(async () => { + // // @ts-expect-error + // await db.query2('user').search('xyz', 'blap').get() + // }, false) - await throws(async () => { - // @ts-ignore - await db.query2('user').search([1, 2, 3, 4], 'blap').get() - }, false) + // await throws(async () => { + // // @ts-expect-error + // await db.query2('user').search([1, 2, 3, 4], 'blap').get() + // }, false) await throws(async () => { const envs = await db .query2('user') + // @ts-expect-error .filter('connections', 'includes', 0) .get() }, false) }) -// TODO This is pointless with the new initialization -await test.skip('query - no schema', async (t) => { - const db = new BasedDb({ - path: t.tmp, - }) - - await db.start({ clean: true }) - t.after(() => db.destroy()) - - setTimeout(async () => { - await db.setSchema({ - types: { - user: { - props: { - name: 'string', - }, - }, - }, - }) - }, 100) - - await throws(async () => { - await db.query2('ploink').get() - }, false) - - await db.schemaIsSet() - deepEqual(await db.query2('user').get(), []) -}) - await test('minmax', async (t) => { const db = await testDb(t, { types: { @@ -1017,7 +1037,7 @@ await test('range validation', async (t) => { }) await test('binary validation', async (t) => { - const db = await testDb(t , { + const db = await testDb(t, { types: { user: { props: { @@ -1032,21 +1052,25 @@ await test('binary validation', async (t) => { name: 'test', binaryData: Buffer.from([1, 2, 3, 4]), }) - await db.create('user', { name: 'test2', binaryData: 'binary string' }) + + // await db.create('user', { name: 'test2', binaryData: 'binary string' }) await db.create('user', { name: 'test3', binaryData: new Uint8Array([5, 6, 7, 8]), }) await throws(async () => { + // @ts-expect-error await db.create('user', { name: 'test4', binaryData: 123 }) }) await throws(async () => { + // @ts-expect-error await db.create('user', { name: 'test5', binaryData: { some: 'object' } }) }) await throws(async () => { + // @ts-expect-error await db.create('user', { name: 'test6', binaryData: [1, 2, 3] }) }) @@ -1054,25 +1078,29 @@ await test('binary validation', async (t) => { .query2('user') .filter('binaryData', '=', Buffer.from([1, 2, 3, 4])) .get() - await db.query2('user').filter('binaryData', '=', 'binary string').get() + // await db.query2('user').filter('binaryData', '=', 'binary string').get() await db .query2('user') .filter('binaryData', '=', new Uint8Array([5, 6, 7, 8])) .get() await throws(async () => { + // @ts-expect-error await db.query2('user').filter('binaryData', '=', 123).get() }) await throws(async () => { + // @ts-expect-error await db.query2('user').filter('binaryData', '=', {}).get() }) await throws(async () => { + // @ts-expect-error await db.query2('user').filter('binaryData', '=', { some: 'object' }).get() }) await throws(async () => { + // @ts-expect-error await db.query2('user').filter('binaryData', '=', [1, 2, 3]).get() }) }) diff --git a/testType.ts b/testType.ts new file mode 100644 index 0000000000..09fff2abb9 --- /dev/null +++ b/testType.ts @@ -0,0 +1,17 @@ +import type { ResolveInclude, PickOutput } from './src/db-client/query2/types.js'; +import type { BasedQuery2 } from './src/db-client/query2/index.js'; + +type TestSchema = { + types: { + user: { + props: { + name: { type: 'string' }; + }; + }; + }; +}; + +type Result = PickOutput; + +let a: Result = { id: 1, name: 'Luigi' }; + diff --git a/testType2.ts b/testType2.ts new file mode 100644 index 0000000000..935fbc8801 --- /dev/null +++ b/testType2.ts @@ -0,0 +1,24 @@ +import { testDb } from './test/shared/test.js'; +import type { DbClient } from './src/sdk.js'; + +async function main() { + const drip = ['dope', 'cringe', 'meh'] + const db = await testDb({} as any, { + locales: { + en: {}, + it: { fallback: ['en'] }, + fi: { fallback: ['en'] }, + }, + types: { + user: { + props: { + rating: 'uint32', + name: 'string', + } + } + } + }); + + const res = await db.query2('user').filter('name', 'includes', '').include('name').get(); + let b: { id: number, name: string }[] = res; +} diff --git a/testType3.ts b/testType3.ts new file mode 100644 index 0000000000..0e846f4ac8 --- /dev/null +++ b/testType3.ts @@ -0,0 +1,24 @@ +import testDb from './test/shared/test.js'; +import type { DbClient } from './src/sdk.js'; + +async function main() { + const drip = ['dope', 'cringe', 'meh'] + const db = await testDb({} as any, { + locales: { + en: {}, + it: { fallback: ['en'] }, + fi: { fallback: ['en'] }, + }, + types: { + user: { + props: { + rating: 'uint32', + name: 'string', + } + } + } + }); + + const res = await db.query2('user').filter('name', 'includes', '').include('name').get(); + let b: { id: number, name: string }[] = res; +} From 458c093c0abf75e23667c60f331bc16650613b9d Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 26 Feb 2026 09:24:46 +0100 Subject: [PATCH 446/449] Remove old TODO comment --- test/scenarios/nycTaxi.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/scenarios/nycTaxi.ts b/test/scenarios/nycTaxi.ts index 3233e1a3e2..3523ac7b12 100644 --- a/test/scenarios/nycTaxi.ts +++ b/test/scenarios/nycTaxi.ts @@ -465,7 +465,6 @@ await test.skip('taxi', async (t) => { clamp(Math.round(isNaN(x) ? 0 : x), -2147483648, 2147483647) const createTrip = async (trip: any) => { - // TODO toObject() shouldn't be needed const { id: vendor = null } = await db .query2('vendor', { vendorId: trip.VendorID }) .include('id') From 2bede7c2b97ee38756e7276da2bfd68ac94951a3 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 26 Feb 2026 09:30:03 +0100 Subject: [PATCH 447/449] The constraint is constant No need to call it for every node separately. It's probably already optimizied out of the loop but let's make it explicit. --- native/modify/modify.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/modify/modify.zig b/native/modify/modify.zig index 3f4fe7c5b0..8b61d8a287 100644 --- a/native/modify/modify.zig +++ b/native/modify/modify.zig @@ -182,6 +182,7 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 .idsWithMeta => { const refTypeId = Schema.getRefTypeIdFromFieldSchema(propSchema); const refTypeEntry = try Node.getType(db, refTypeId); + const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); const count = utils.read(u32, refs, 0); var x: usize = 4; References.preallocReferences2(db, node, propSchema, count); @@ -193,7 +194,6 @@ pub fn modifyProps(db: *DbCtx, typeEntry: Node.Type, node: Node.Node, data: []u8 const ref = try References.insertReference(db, node, propSchema, dst, meta.index, meta.withIndex); if (meta.size != 0) { const edgeProps = refs[x .. x + meta.size]; - const edgeConstraint = Schema.getEdgeFieldConstraint(propSchema); if (Node.getEdgeNode(db, edgeConstraint, ref.p.large)) |edgeNode| { const edgeType = try Node.getType(db, edgeConstraint.edge_node_type); try modifyProps(db, edgeType, edgeNode, edgeProps, items); From 56afac48b1becf2d2f159623bd6816ce9b2bcdfe Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 26 Feb 2026 09:47:46 +0100 Subject: [PATCH 448/449] Tail insertion is expected by default --- src/schema/defs/props/references.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/defs/props/references.ts b/src/schema/defs/props/references.ts index 6a16d902ef..1f4072968d 100644 --- a/src/schema/defs/props/references.ts +++ b/src/schema/defs/props/references.ts @@ -100,7 +100,7 @@ const serializeIdsAndMeta = ( id: id, isTmp: !realId, withIndex: '$index' in item, - index: item.$index, + index: item.$index ?? -1, size: 0, }) From 6af05f8378b04d674d0f6309bf170b47feb10670 Mon Sep 17 00:00:00 2001 From: Olli Vanhoja Date: Thu, 26 Feb 2026 10:22:06 +0100 Subject: [PATCH 449/449] New checksum fun --- test/binary.ts | 6 +++--- test/include/include.ts | 3 ++- test/json.ts | 5 +++-- test/queryResponse.ts | 3 ++- test/save/blockHash.ts | 5 +++-- test/text/text.ts | 5 +++-- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/test/binary.ts b/test/binary.ts index 998849fb6f..4204d59734 100644 --- a/test/binary.ts +++ b/test/binary.ts @@ -1,10 +1,10 @@ -import { BasedDb } from '../src/index.js' import { ENCODER } from '../src/utils/uint8.js' import test from './shared/test.js' import { testDb } from './shared/index.js' import { deepEqual, equal } from './shared/assert.js' import { italy } from './shared/examples.js' import { notEqual } from 'node:assert' +import { checksum as q2checksum } from '../src/db-client/query2/index.js' await test('simple', async (t) => { const db = await testDb(t, { @@ -66,13 +66,13 @@ await test('binary and crc32', async (t) => { article: new Uint8Array([1]), }) - const checksum = (await db.query2('user', user1).get()).checksum + const checksum = q2checksum(await db.query2('user', user1).get()) await db.update('user', user1, { article: new Uint8Array([2]), }) - const checksum2 = (await db.query2('user', user1).get()).checksum + const checksum2 = q2checksum(await db.query2('user', user1).get()) notEqual(checksum, checksum2, 'Checksum is not the same') }) diff --git a/test/include/include.ts b/test/include/include.ts index 9c2e0cdd7f..a82297ca37 100644 --- a/test/include/include.ts +++ b/test/include/include.ts @@ -1,6 +1,7 @@ import test from '../shared/test.js' import { testDb } from '../shared/index.js' import { deepEqual, equal } from '../shared/assert.js' +import { checksum } from '../../src/db-client/query2/index.js' await test('include ', async (t) => { const db = await testDb(t, { @@ -40,7 +41,7 @@ await test('include ', async (t) => { equal((await db.query2('user', 1).get()).id, 1) //equal((await db.query2('user', 1).get()).queryId, 3978712180) - equal((await db.query2('user').get()).checksum, 2149520223) + equal(checksum(await db.query2('user').get()), 2149520223) equal((await db.query2('user').get()).version, 4507870634704934) }) diff --git a/test/json.ts b/test/json.ts index f88b2455fa..e8787f32a2 100644 --- a/test/json.ts +++ b/test/json.ts @@ -2,6 +2,7 @@ import { notEqual } from 'node:assert' import { deepEqual } from './shared/assert.js' import test from './shared/test.js' import { testDb } from './shared/index.js' +import { checksum as q2checksum } from '../../src/db-client/query2/index.js' await test('json', async (t) => { const db = await testDb(t, { @@ -79,13 +80,13 @@ await test('json and crc32', async (t) => { article: 'a', }) - const checksum = (await db.query2('user', user1).get()).checksum + const checksum = q2checksum(await db.query2('user', user1).get()) await db.update('user', user1, { article: 'b', }) - const checksum2 = (await db.query2('user', user1).get()).checksum + const checksum2 = q2checksum(await db.query2('user', user1).get()) notEqual(checksum, checksum2, 'Checksum is not the same') }) diff --git a/test/queryResponse.ts b/test/queryResponse.ts index d295386265..6f249dc7a8 100644 --- a/test/queryResponse.ts +++ b/test/queryResponse.ts @@ -3,6 +3,7 @@ import { testDb } from './shared/index.js' import { equal } from './shared/assert.js' import { notEqual } from 'assert' import { extractNumber } from '../src/utils/index.js' +import { checksum } from '../src/db-client/query2/index.js' await test('correct version', async (t) => { const db = await testDb(t, { @@ -23,7 +24,7 @@ await test('correct version', async (t) => { equal( extractNumber(response.version), - response.checksum, + checksum(response), 'Checksum is recoverable from the 53 bit js version number', ) diff --git a/test/save/blockHash.ts b/test/save/blockHash.ts index e025b367c5..1fa8f363f1 100644 --- a/test/save/blockHash.ts +++ b/test/save/blockHash.ts @@ -7,6 +7,7 @@ import test from '../shared/test.js' import native from '../../src/native.js' import { deepEqual } from '../shared/assert.js' import { getBlockHash } from '../../src/db-server/blocks.js' +import { checksum } from '../../src/db-client/query2/index.js' const sha1 = async (path: string) => createHash('sha1') @@ -47,8 +48,8 @@ await test('isomorphic types have equal hashes', async (t) => { await client.drain() deepEqual( - (await client.query2('article').get()).checksum, - (await client.query2('story').get()).checksum, + checksum(await client.query2('article').get()), + checksum(await client.query2('story').get()), ) assert( native.equals( diff --git a/test/text/text.ts b/test/text/text.ts index 671c123463..53e176cbaf 100644 --- a/test/text/text.ts +++ b/test/text/text.ts @@ -2,6 +2,7 @@ import test from '../shared/test.js' import { italy } from '../shared/examples.js' import { deepEqual } from '../shared/assert.js' import { notEqual } from 'node:assert' +import { checksum as q2checksum } from '../../src/db-client/query2/index.js' import { testDb } from '../shared/index.js' await test('simple', async (t) => { @@ -929,7 +930,7 @@ await test('text and crc32', async (t) => { }, }) - const checksum = (await db.query2('user', user1).get()).checksum + const checksum = q2checksum(await db.query2('user', user1).get()) await db.update('user', user1, { article: { @@ -938,7 +939,7 @@ await test('text and crc32', async (t) => { }, }) - const checksum2 = (await db.query2('user', user1).get()).checksum + const checksum2 = q2checksum(await db.query2('user', user1).get()) notEqual(checksum, checksum2, 'Checksum is not the same') })