@based/db 0.0.43 → 0.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/lib/darwin_aarch64/include/selva/fields.h +0 -2
  2. package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
  3. package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
  4. package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
  5. package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
  6. package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
  7. package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
  8. package/dist/lib/darwin_aarch64/libnode-v24.node +0 -0
  9. package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
  10. package/dist/lib/linux_aarch64/include/selva/fields.h +0 -2
  11. package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
  12. package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
  13. package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
  14. package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
  15. package/dist/lib/linux_aarch64/libnode-v24.node +0 -0
  16. package/dist/lib/linux_aarch64/libselva.so +0 -0
  17. package/dist/lib/linux_x86_64/include/selva/fields.h +0 -2
  18. package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
  19. package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
  20. package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
  21. package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
  22. package/dist/lib/linux_x86_64/libnode-v24.node +0 -0
  23. package/dist/lib/linux_x86_64/libselva.so +0 -0
  24. package/dist/src/client/flushModify.d.ts +2 -0
  25. package/dist/src/client/flushModify.js +60 -31
  26. package/dist/src/client/index.d.ts +5 -5
  27. package/dist/src/client/index.js +14 -8
  28. package/dist/src/client/query/BasedDbQuery.d.ts +9 -2
  29. package/dist/src/client/query/BasedDbQuery.js +78 -7
  30. package/dist/src/client/query/display.js +3 -2
  31. package/dist/src/client/query/registerQuery.js +1 -0
  32. package/dist/src/client/query/toBuffer.js +11 -1
  33. package/dist/src/client/query/types.d.ts +1 -0
  34. package/dist/src/index.js +8 -1
  35. package/dist/src/server/index.d.ts +2 -2
  36. package/dist/src/server/index.js +24 -22
  37. package/package.json +3 -3
@@ -377,8 +377,6 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
377
377
 
378
378
  /**
379
379
  * Destroy all fields of a node.
380
- * This will set nr_fields = 0, making setting new field values impossible
381
- * regardless wether the schema defines fields for this node.
382
380
  */
383
381
  SELVA_EXPORT
384
382
  void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx)
@@ -377,8 +377,6 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
377
377
 
378
378
  /**
379
379
  * Destroy all fields of a node.
380
- * This will set nr_fields = 0, making setting new field values impossible
381
- * regardless wether the schema defines fields for this node.
382
380
  */
383
381
  SELVA_EXPORT
384
382
  void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx)
Binary file
@@ -377,8 +377,6 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
377
377
 
378
378
  /**
379
379
  * Destroy all fields of a node.
380
- * This will set nr_fields = 0, making setting new field values impossible
381
- * regardless wether the schema defines fields for this node.
382
380
  */
383
381
  SELVA_EXPORT
384
382
  void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx)
Binary file
@@ -27,6 +27,8 @@ export declare class ModifyCtx {
27
27
  markTypeDirty(schema: SchemaTypeDef): void;
28
28
  updateMax(): void;
29
29
  getData(lastIds: Record<number, number>): Uint8Array;
30
+ reset(): void;
30
31
  }
32
+ export declare const execCtxQueue: (resCtx: ModifyCtx["ctx"], error?: boolean) => void;
31
33
  export declare const flushBuffer: (db: DbClient) => Promise<void>;
32
34
  export declare const startDrain: (db: DbClient) => void;
@@ -1,3 +1,4 @@
1
+ import { writeUint64 } from '@saulx/utils';
1
2
  // TODO This definitely shouldn't be copy-pasted here from server/tree.ts
2
3
  const makeCsmtKeyFromNodeId = (typeId, blockCapacity, nodeId) => {
3
4
  const tmp = nodeId - +!(nodeId % blockCapacity);
@@ -8,9 +9,10 @@ export class ModifyCtx {
8
9
  this.max = db.maxModifySize;
9
10
  this.db = db;
10
11
  this.buf = new Uint8Array(db.maxModifySize);
12
+ this.reset();
11
13
  }
12
14
  // default values
13
- len = 0;
15
+ len = 8;
14
16
  id = -1;
15
17
  hasSortField = -1;
16
18
  hasSortText = -1;
@@ -79,17 +81,50 @@ export class ModifyCtx {
79
81
  view.setFloat64(i, key, true);
80
82
  i += 8;
81
83
  }
82
- data[i++] = this.len;
83
- data[i++] = this.len >>> 8;
84
- data[i++] = this.len >>> 16;
85
- data[i++] = this.len >>> 24;
84
+ const lenMinusSchemaHash = this.len - 8;
85
+ data[i++] = lenMinusSchemaHash;
86
+ data[i++] = lenMinusSchemaHash >>> 8;
87
+ data[i++] = lenMinusSchemaHash >>> 16;
88
+ data[i++] = lenMinusSchemaHash >>> 24;
86
89
  return data;
87
90
  }
91
+ reset() {
92
+ this.dirtyTypes.clear();
93
+ this.dirtyRanges.clear();
94
+ writeUint64(this.buf, this.db.schema?.hash || 0, 0);
95
+ this.len = 8;
96
+ this.prefix0 = -1;
97
+ this.prefix1 = -1;
98
+ this.lastMain = -1;
99
+ // should these also be reset in setcursor?
100
+ this.hasSortText = -1;
101
+ this.hasSortField = -1;
102
+ this.max = this.db.maxModifySize;
103
+ this.ctx = {};
104
+ }
88
105
  }
106
+ export const execCtxQueue = (resCtx, error) => {
107
+ if (resCtx.queue?.size) {
108
+ const queue = resCtx.queue;
109
+ resCtx.queue = null;
110
+ if (error) {
111
+ for (const [resolve] of queue) {
112
+ // should we throw?
113
+ resolve(null);
114
+ }
115
+ }
116
+ else {
117
+ for (const [resolve, res] of queue) {
118
+ resolve(res.getId());
119
+ }
120
+ }
121
+ }
122
+ };
89
123
  export const flushBuffer = (db) => {
90
124
  const ctx = db.modifyCtx;
91
125
  let flushPromise;
92
- if (ctx.len) {
126
+ if (ctx.len > 8) {
127
+ // always has schema hash
93
128
  const lastIds = {};
94
129
  const data = ctx.getData(lastIds);
95
130
  const resCtx = ctx.ctx;
@@ -98,37 +133,31 @@ export const flushBuffer = (db) => {
98
133
  .flushModify(data)
99
134
  .then(({ offsets, dbWriteTime }) => {
100
135
  db.writeTime += dbWriteTime ?? 0;
101
- resCtx.offsets = offsets;
102
- for (const typeId in lastIds) {
103
- if (typeId in offsets) {
104
- const lastId = lastIds[typeId] + offsets[typeId];
105
- const def = db.schemaTypesParsedById[typeId];
106
- const delta = lastId - def.lastId;
107
- if (delta > 0) {
108
- def.lastId += delta;
109
- def.total += delta;
136
+ if (offsets) {
137
+ resCtx.offsets = offsets;
138
+ for (const typeId in lastIds) {
139
+ if (typeId in offsets) {
140
+ const lastId = lastIds[typeId] + offsets[typeId];
141
+ const def = db.schemaTypesParsedById[typeId];
142
+ const delta = lastId - def.lastId;
143
+ if (delta > 0) {
144
+ def.lastId += delta;
145
+ def.total += delta;
146
+ }
147
+ }
148
+ else {
149
+ console.error('Panic: No offset returned in flushModify');
110
150
  }
111
151
  }
112
- else {
113
- console.error('Panic: No offset returned in flushModify');
114
- }
152
+ execCtxQueue(resCtx);
115
153
  }
116
- if (resCtx.queue?.size) {
117
- const queue = resCtx.queue;
118
- resCtx.queue = null;
119
- for (const [resolve, res] of queue) {
120
- resolve(res.getId());
121
- }
154
+ else {
155
+ console.info('Modify cancelled - schema mismatch');
156
+ execCtxQueue(resCtx, true);
122
157
  }
123
158
  db.flushReady();
124
159
  });
125
- ctx.dirtyTypes.clear();
126
- ctx.dirtyRanges.clear();
127
- ctx.len = 0;
128
- ctx.prefix0 = -1;
129
- ctx.prefix1 = -1;
130
- ctx.max = db.maxModifySize;
131
- ctx.ctx = {};
160
+ ctx.reset();
132
161
  }
133
162
  else {
134
163
  db.flushReady();
@@ -23,9 +23,7 @@ type DbClientOpts = {
23
23
  flushTime?: number;
24
24
  debug?: boolean;
25
25
  };
26
- type DbClientSchema = StrictSchema & {
27
- lastId: number;
28
- };
26
+ type DbClientSchema = DbServer['schema'];
29
27
  export declare class DbClient {
30
28
  constructor({ hooks, maxModifySize, flushTime, debug, }: DbClientOpts);
31
29
  flushTime: number;
@@ -43,11 +41,13 @@ export declare class DbClient {
43
41
  o: Record<string, any>;
44
42
  p: Promise<number | ModifyRes>;
45
43
  }>;
46
- schemaChecksum: number;
47
44
  schemaProcessing: number;
48
45
  schemaPromise: Promise<DbServer['schema']>;
49
46
  setSchema(schema: Schema, fromStart?: boolean, transformFns?: TransformFns): Promise<StrictSchema>;
50
- putLocalSchema(schema: any): DbClientSchema;
47
+ putLocalSchema(schema: DbServer['schema']): StrictSchema & {
48
+ lastId: number;
49
+ hash?: number;
50
+ };
51
51
  create(type: string, obj?: CreateObj, opts?: ModifyOpts): ModifyRes;
52
52
  copy(type: string, target: number | ModifyRes, objOrTransformFn?: Record<string, any> | ((item: Record<string, any>) => Promise<any>)): Promise<ModifyRes>;
53
53
  query(type: string, id?: number | ModifyRes | (number | ModifyRes)[] | QueryByAliasObj | QueryByAliasObj[] | Uint32Array): BasedDbQuery;
@@ -1,7 +1,7 @@
1
1
  import { parse } from '@based/schema';
2
2
  import { create } from './modify/create.js';
3
3
  import { updateTypeDefs, schemaToSelvaBuffer, } from '@based/schema/def';
4
- import { flushBuffer, ModifyCtx, startDrain } from './flushModify.js';
4
+ import { execCtxQueue, flushBuffer, ModifyCtx, startDrain, } from './flushModify.js';
5
5
  import { BasedDbQuery } from './query/BasedDbQuery.js';
6
6
  import { ModifyState } from './modify/ModifyRes.js';
7
7
  import { upsert } from './modify/upsert.js';
@@ -43,7 +43,6 @@ export class DbClient {
43
43
  modifyCtx;
44
44
  maxModifySize;
45
45
  upserting = new Map();
46
- schemaChecksum;
47
46
  schemaProcessing;
48
47
  schemaPromise;
49
48
  async setSchema(schema, fromStart, transformFns) {
@@ -52,6 +51,8 @@ export class DbClient {
52
51
  if (schemaLooseEqual(strictSchema, this.schema)) {
53
52
  return this.schema;
54
53
  }
54
+ // drain current things
55
+ await this.drain();
55
56
  const checksum = hash(strictSchema);
56
57
  if (checksum !== this.schemaProcessing) {
57
58
  this.schemaProcessing = checksum;
@@ -63,15 +64,20 @@ export class DbClient {
63
64
  return this.putLocalSchema(remoteSchema);
64
65
  }
65
66
  putLocalSchema(schema) {
66
- const checksum = hash(schema);
67
- if (this.schemaChecksum === checksum) {
67
+ if (this.schema && this.schema.hash === schema.hash) {
68
68
  return this.schema;
69
69
  }
70
- this.schemaChecksum = checksum;
71
70
  this.schema = schema;
72
71
  updateTypeDefs(this.schema, this.schemaTypesParsed, this.schemaTypesParsedById);
73
72
  // Adds bidrectional refs on defs
74
73
  schemaToSelvaBuffer(this.schemaTypesParsed);
74
+ // this has to happen before the listeners
75
+ if (this.modifyCtx.len > 8) {
76
+ console.info('Modify cancelled - schema updated');
77
+ }
78
+ const resCtx = this.modifyCtx.ctx;
79
+ this.modifyCtx.reset();
80
+ execCtxQueue(resCtx, true);
75
81
  if (this.listeners?.schema) {
76
82
  for (const cb of this.listeners.schema) {
77
83
  cb(this.schema);
@@ -88,11 +94,11 @@ export class DbClient {
88
94
  .get()
89
95
  .toObject();
90
96
  if (typeof objOrTransformFn === 'function') {
91
- const { id, ...props } = await objOrTransformFn(item);
97
+ const { id: _, ...props } = await objOrTransformFn(item);
92
98
  return this.create(type, props);
93
99
  }
94
100
  if (typeof objOrTransformFn === 'object' && objOrTransformFn !== null) {
95
- const { id, ...props } = item;
101
+ const { id: _, ...props } = item;
96
102
  await Promise.all(Object.keys(objOrTransformFn).map(async (key) => {
97
103
  const val = objOrTransformFn[key];
98
104
  if (val === null) {
@@ -113,7 +119,7 @@ export class DbClient {
113
119
  }));
114
120
  return this.create(type, props);
115
121
  }
116
- const { id, ...props } = item;
122
+ const { id: _, ...props } = item;
117
123
  return this.create(type, props);
118
124
  }
119
125
  query(type, id) {
@@ -1,4 +1,4 @@
1
- import { QueryDef, Operator, QueryByAliasObj } from './query.js';
1
+ import { QueryDef, QueryTarget, Operator, QueryByAliasObj } from './query.js';
2
2
  import { BasedQueryResponse } from './BasedIterable.js';
3
3
  import { Search } from './search/index.js';
4
4
  import { OnData, OnError } from './subscription/index.js';
@@ -8,9 +8,14 @@ import { FilterAst, FilterBranchFn, FilterOpts } from './filter/types.js';
8
8
  export { QueryByAliasObj };
9
9
  export type SelectFn = (field: string) => BasedDbReferenceQuery;
10
10
  export type BranchInclude = (select: SelectFn) => any;
11
+ export type QueryCommand = {
12
+ method: string;
13
+ args: any[];
14
+ };
11
15
  export declare class QueryBranch<T> {
12
16
  db: DbClient;
13
17
  def: QueryDef;
18
+ queryCommands: QueryCommand[];
14
19
  constructor(db: DbClient, def: QueryDef);
15
20
  sort(field: string, order?: 'asc' | 'desc'): T;
16
21
  filter<O extends Operator>(field: string, operator?: O | boolean, value?: any, opts?: FilterOpts<O>): T;
@@ -35,7 +40,9 @@ declare class GetPromise extends Promise<BasedQueryResponse> {
35
40
  export declare class BasedDbQuery extends QueryBranch<BasedDbQuery> {
36
41
  #private;
37
42
  skipValidation: boolean;
38
- constructor(db: DbClient, type: string, id?: QueryByAliasObj | number | Uint32Array | (QueryByAliasObj | number)[], skipValidation?: boolean);
43
+ target: QueryTarget;
44
+ constructor(db: DbClient, type: string, rawTarget?: QueryByAliasObj | number | Uint32Array | (QueryByAliasObj | number)[], skipValidation?: boolean);
45
+ reBuildQuery(): void;
39
46
  id: number;
40
47
  get(): GetPromise;
41
48
  buffer: Uint8Array;
@@ -15,16 +15,29 @@ import { concatUint8Arr } from '@saulx/utils';
15
15
  export class QueryBranch {
16
16
  db;
17
17
  def;
18
+ queryCommands;
18
19
  constructor(db, def) {
19
20
  this.db = db;
20
21
  this.def = def;
21
22
  }
22
23
  sort(field, order = 'asc') {
24
+ if (this.queryCommands) {
25
+ this.queryCommands.push({
26
+ method: 'filter',
27
+ args: [field, order],
28
+ });
29
+ }
23
30
  sort(this.def, field, order);
24
31
  // @ts-ignore
25
32
  return this;
26
33
  }
27
34
  filter(field, operator, value, opts) {
35
+ if (this.queryCommands) {
36
+ this.queryCommands.push({
37
+ method: 'filter',
38
+ args: [field, operator, value, opts],
39
+ });
40
+ }
28
41
  const f = convertFilter(this.def, field, operator, value, opts);
29
42
  if (!f) {
30
43
  // @ts-ignore
@@ -35,11 +48,23 @@ export class QueryBranch {
35
48
  return this;
36
49
  }
37
50
  filterBatch(f) {
51
+ if (this.queryCommands) {
52
+ this.queryCommands.push({
53
+ method: 'filterBatch',
54
+ args: [f],
55
+ });
56
+ }
38
57
  filter(this.db, this.def, f, this.def.filter);
39
58
  // @ts-ignore
40
59
  return this;
41
60
  }
42
61
  search(query, field, opts, ...fields) {
62
+ if (this.queryCommands) {
63
+ this.queryCommands.push({
64
+ method: 'search',
65
+ args: [query, field, opts, ...fields],
66
+ });
67
+ }
43
68
  if (ArrayBuffer.isView(query)) {
44
69
  // @ts-ignore
45
70
  vectorSearch(this.def, query, field, opts ?? {});
@@ -95,23 +120,47 @@ export class QueryBranch {
95
120
  return this;
96
121
  }
97
122
  groupBy(field) {
123
+ if (this.queryCommands) {
124
+ this.queryCommands.push({
125
+ method: 'groupBy',
126
+ args: [field],
127
+ });
128
+ }
98
129
  groupBy(this.def, field);
99
130
  // only works with aggregates for now
100
131
  // @ts-ignore
101
132
  return this;
102
133
  }
103
134
  count(field = '$count') {
135
+ if (this.queryCommands) {
136
+ this.queryCommands.push({
137
+ method: 'count',
138
+ args: [field],
139
+ });
140
+ }
104
141
  const p = field.split('.');
105
142
  addAggregate(2 /* AggregateType.COUNT */, this.def, p);
106
143
  // @ts-ignore
107
144
  return this;
108
145
  }
109
146
  sum(...fields) {
147
+ if (this.queryCommands) {
148
+ this.queryCommands.push({
149
+ method: 'sum',
150
+ args: fields,
151
+ });
152
+ }
110
153
  addAggregate(1 /* AggregateType.SUM */, this.def, fields);
111
154
  // @ts-ignore
112
155
  return this;
113
156
  }
114
157
  or(field, operator, value, opts) {
158
+ if (this.queryCommands) {
159
+ this.queryCommands.push({
160
+ method: 'or',
161
+ args: [field, operator, value, opts],
162
+ });
163
+ }
115
164
  if (typeof field === 'function') {
116
165
  const f = new FilterBranch(this.db, filterOr(this.db, this.def, [], this.def.filter), this.def);
117
166
  field(f);
@@ -127,6 +176,9 @@ export class QueryBranch {
127
176
  return this;
128
177
  }
129
178
  range(start, end = DEF_RANGE_PROP_LIMIT) {
179
+ if (this.queryCommands) {
180
+ this.queryCommands.push({ method: 'range', args: [start, end] });
181
+ }
130
182
  const offset = start;
131
183
  const limit = end - start;
132
184
  if (validateRange(this.def, offset, limit)) {
@@ -141,6 +193,9 @@ export class QueryBranch {
141
193
  return this;
142
194
  }
143
195
  include(...fields) {
196
+ if (this.queryCommands) {
197
+ this.queryCommands.push({ method: 'include', args: fields });
198
+ }
144
199
  for (const f of fields) {
145
200
  if (typeof f === 'string') {
146
201
  includeField(this.def, f);
@@ -206,22 +261,23 @@ class GetPromise extends Promise {
206
261
  }
207
262
  export class BasedDbQuery extends QueryBranch {
208
263
  skipValidation = false;
209
- constructor(db, type, id, skipValidation) {
264
+ target;
265
+ constructor(db, type, rawTarget, skipValidation) {
210
266
  const target = {
211
267
  type,
212
268
  };
213
- if (id) {
214
- if (isAlias(id)) {
215
- target.alias = id;
269
+ if (rawTarget) {
270
+ if (isAlias(rawTarget)) {
271
+ target.alias = rawTarget;
216
272
  }
217
273
  else {
218
- if (Array.isArray(id) || id instanceof Uint32Array) {
274
+ if (Array.isArray(rawTarget) || rawTarget instanceof Uint32Array) {
219
275
  // TODO ADD MULTI ALIAS
220
276
  // @ts-ignore
221
- target.ids = id;
277
+ target.ids = rawTarget;
222
278
  }
223
279
  else {
224
- target.id = id;
280
+ target.id = rawTarget;
225
281
  }
226
282
  }
227
283
  }
@@ -230,6 +286,21 @@ export class BasedDbQuery extends QueryBranch {
230
286
  }
231
287
  const def = createQueryDef(db, QueryDefType.Root, target, skipValidation);
232
288
  super(db, def);
289
+ this.db = db;
290
+ this.skipValidation = skipValidation;
291
+ this.queryCommands = [];
292
+ this.target = target;
293
+ }
294
+ reBuildQuery() {
295
+ this.id = undefined;
296
+ this.buffer = undefined;
297
+ const def = createQueryDef(this.db, QueryDefType.Root, this.target, this.skipValidation);
298
+ this.def = def;
299
+ const q = this.queryCommands;
300
+ this.queryCommands = [];
301
+ for (const command of q) {
302
+ this[command.method](...command.args);
303
+ }
233
304
  }
234
305
  #getInternal = async (resolve, reject) => {
235
306
  if (!this.def.include.stringFields.size && !this.def.references.size) {
@@ -215,8 +215,9 @@ const inspectObject = (object, q, path, level, isLast, isFirst, isObject, depth)
215
215
  str += picocolors.blue(v);
216
216
  str += picocolors.italic(picocolors.dim(` ${key.indexOf('count') >= 0 ? ' count' : ' sum'}`));
217
217
  }
218
- // str += ',\n'
219
- // str += picocolors.blue(v)
218
+ else {
219
+ str += picocolors.blue(v);
220
+ }
220
221
  }
221
222
  else if (typeof v === 'object' && v) {
222
223
  inspectObject(v, q, key, level + 2, false, false, true, depth) + '';
@@ -4,6 +4,7 @@ import { defToBuffer } from './toBuffer.js';
4
4
  import { handleErrors } from './validation.js';
5
5
  export const registerQuery = (q) => {
6
6
  if (!q.id) {
7
+ q.def.schemaChecksum = q.db.schema.hash;
7
8
  const b = defToBuffer(q.db, q.def);
8
9
  const buf = concatUint8Arr(b);
9
10
  let id = native.crc32(buf);
@@ -3,7 +3,7 @@ import { QueryDefType, QueryType } from './types.js';
3
3
  import { includeToBuffer } from './include/toBuffer.js';
4
4
  import { filterToBuffer } from './query.js';
5
5
  import { searchToBuffer } from './search/index.js';
6
- import { ENCODER } from '@saulx/utils';
6
+ import { ENCODER, writeUint64 } from '@saulx/utils';
7
7
  import { aggregateToBuffer, isRootCountOnly } from './aggregates/aggregation.js';
8
8
  const byteSize = (arr) => {
9
9
  return arr.reduce((a, b) => {
@@ -65,6 +65,11 @@ export function defToBuffer(db, def) {
65
65
  result.push(buf);
66
66
  // ignore this for now...
67
67
  // result.push(...include)
68
+ if (def.type === QueryDefType.Root) {
69
+ const checksum = new Uint8Array(8);
70
+ writeUint64(checksum, def.schemaChecksum ?? 0, 0);
71
+ result.push(checksum);
72
+ }
68
73
  return result;
69
74
  }
70
75
  if (def.type === QueryDefType.Root) {
@@ -246,6 +251,11 @@ export function defToBuffer(db, def) {
246
251
  metaEdgeBuffer[2] = edgesSize >>> 8;
247
252
  result.push(metaEdgeBuffer, ...edges);
248
253
  }
254
+ if (def.type === QueryDefType.Root) {
255
+ const checksum = new Uint8Array(8);
256
+ writeUint64(checksum, def.schemaChecksum ?? 0, 0);
257
+ result.push(checksum);
258
+ }
249
259
  return result;
250
260
  }
251
261
  //# sourceMappingURL=toBuffer.js.map
@@ -86,6 +86,7 @@ export type QueryDefAggregation = {
86
86
  totalResultsPos: number;
87
87
  };
88
88
  export type QueryDefShared = {
89
+ schemaChecksum?: number;
89
90
  errors: QueryError[];
90
91
  lang: LangCode;
91
92
  filter: QueryDefFilter;
package/dist/src/index.js CHANGED
@@ -5,6 +5,7 @@ import { DbClient } from './client/index.js';
5
5
  import { wait } from '@saulx/utils';
6
6
  import { debugMode, debugServer } from './utils.js';
7
7
  import { BasedQueryResponse } from './client/query/BasedIterable.js';
8
+ import { registerQuery } from './client/query/registerQuery.js';
8
9
  export * from './client/modify/modify.js';
9
10
  export { compress, decompress };
10
11
  export { ModifyCtx }; // TODO move this somewhere
@@ -57,6 +58,12 @@ export class BasedDb {
57
58
  let lastLen = 0;
58
59
  let response;
59
60
  const get = async () => {
61
+ const schemaVersion = q.def.schemaChecksum;
62
+ if (schemaVersion && schemaVersion !== server.schema.hash) {
63
+ q.reBuildQuery();
64
+ registerQuery(q);
65
+ response = undefined;
66
+ }
60
67
  const res = await server.getQueryBuf(q.buffer);
61
68
  if (!response) {
62
69
  response = new BasedQueryResponse(q.id, q.def, res, 0);
@@ -71,7 +78,7 @@ export class BasedDb {
71
78
  lastLen = res.byteLength;
72
79
  prevChecksum = checksum;
73
80
  }
74
- setTimeout(get, 200);
81
+ timer = setTimeout(get, 200);
75
82
  };
76
83
  get();
77
84
  return () => {
@@ -23,7 +23,7 @@ export declare class DbWorker {
23
23
  updateCtx(address: BigInt): Promise<void>;
24
24
  getQueryBuf(buf: Uint8Array): Promise<Uint8Array>;
25
25
  }
26
- type OnSchemaChange = (schema: StrictSchema) => void;
26
+ type OnSchemaChange = (schema: DbServer['schema']) => void;
27
27
  export declare class DbServer {
28
28
  #private;
29
29
  modifyDirtyRanges: Float64Array;
@@ -99,7 +99,7 @@ export declare class DbServer {
99
99
  lastId: number;
100
100
  hash?: number;
101
101
  }>;
102
- modify(buf: Uint8Array): Record<number, number>;
102
+ modify(buf: Uint8Array): Record<number, number> | null;
103
103
  addToQueryQueue(resolve: any, buf: any): void;
104
104
  getQueryBuf(buf: Uint8Array, fromQueue?: boolean): Promise<Uint8Array>;
105
105
  onQueryEnd(): void;
@@ -12,6 +12,7 @@ import { fileURLToPath } from 'node:url';
12
12
  import { setTimeout } from 'node:timers/promises';
13
13
  import { migrate } from './migrate/index.js';
14
14
  import { debugServer, schemaLooseEqual } from '../utils.js';
15
+ import { readUint16, readUint32, readUint64, writeUint64 } from '@saulx/utils';
15
16
  import { hash } from '@saulx/hash';
16
17
  export const SCHEMA_FILE = 'schema.json';
17
18
  export const WRITELOG_FILE = 'writelog.json';
@@ -28,12 +29,6 @@ class SortIndex {
28
29
  idx;
29
30
  cnt = 0;
30
31
  }
31
- function readUint16LE(buf, off) {
32
- return buf[off] | (buf[off + 1] << 8);
33
- }
34
- function readUint32LE(buf, off) {
35
- return (buf[off] | (buf[off + 1] << 8) | (buf[off + 2] << 16) | (buf[off + 3] << 24));
36
- }
37
32
  export class DbWorker {
38
33
  constructor(address, db) {
39
34
  const { port1, port2 } = new MessageChannel();
@@ -362,14 +357,16 @@ export class DbServer {
362
357
  // TODO fix this add it in schema at least
363
358
  const data = [2, 1, 0, 0, 0, 1, 9, 1, 0, 0, 0, 7, 1, 0, 1];
364
359
  const blockKey = makeCsmtKey(1, 1);
365
- const buf = new Uint8Array(data.length + 2 + 8 + 4);
360
+ const buf = new Uint8Array(8 + data.length + 2 + 8 + 4);
366
361
  const view = new DataView(buf.buffer, 0, buf.byteLength);
362
+ // set schema hash
363
+ writeUint64(buf, this.schema.hash, 0);
367
364
  // add content
368
- buf.set(data);
365
+ buf.set(data, 8);
369
366
  // add typesLen
370
- view.setFloat64(data.length, 0, true);
367
+ view.setFloat64(8 + data.length, 0, true);
371
368
  // add dirty key
372
- view.setFloat64(data.length + 2, blockKey, true);
369
+ view.setFloat64(8 + data.length + 2, blockKey, true);
373
370
  // add dataLen
374
371
  view.setUint32(buf.length - 4, data.length, true);
375
372
  this.modify(buf);
@@ -380,14 +377,19 @@ export class DbServer {
380
377
  return this.schema;
381
378
  }
382
379
  modify(buf) {
380
+ const schemaHash = readUint64(buf, 0);
381
+ if (schemaHash !== this.schema.hash) {
382
+ return null;
383
+ }
384
+ buf = buf.subarray(8);
383
385
  const offsets = {};
384
- const dataLen = readUint32LE(buf, buf.length - 4);
385
- let typesSize = readUint16LE(buf, dataLen);
386
+ const dataLen = readUint32(buf, buf.length - 4);
387
+ let typesSize = readUint16(buf, dataLen);
386
388
  let i = dataLen + 2;
387
389
  while (typesSize--) {
388
- const typeId = readUint16LE(buf, i);
390
+ const typeId = readUint16(buf, i);
389
391
  i += 2;
390
- const startId = readUint32LE(buf, i);
392
+ const startId = readUint32(buf, i);
391
393
  const def = this.schemaTypesParsedById[typeId];
392
394
  let offset = def.lastId - startId;
393
395
  if (offset < 0) {
@@ -397,7 +399,7 @@ export class DbServer {
397
399
  buf[i++] = offset >>> 8;
398
400
  buf[i++] = offset >>> 16;
399
401
  buf[i++] = offset >>> 24;
400
- const lastId = readUint32LE(buf, i);
402
+ const lastId = readUint32(buf, i);
401
403
  i += 4;
402
404
  def.lastId = lastId + offset;
403
405
  offsets[typeId] = offset;
@@ -416,14 +418,14 @@ export class DbServer {
416
418
  return;
417
419
  }
418
420
  const end = buf.length - 4;
419
- const dataLen = readUint32LE(buf, end);
420
- let typesSize = readUint16LE(buf, dataLen);
421
+ const dataLen = readUint32(buf, end);
422
+ let typesSize = readUint16(buf, dataLen);
421
423
  const typesLen = typesSize * 10;
422
424
  const types = buf.subarray(dataLen + 2, dataLen + typesLen + 2);
423
425
  const data = buf.subarray(0, dataLen);
424
426
  let i = dataLen + 2;
425
427
  while (typesSize--) {
426
- const typeId = readUint16LE(buf, i);
428
+ const typeId = readUint16(buf, i);
427
429
  const def = this.schemaTypesParsedById[typeId];
428
430
  const key = makeCsmtKeyFromNodeId(def.id, def.blockCapacity, def.lastId);
429
431
  this.dirtyRanges.add(key);
@@ -475,13 +477,13 @@ export class DbServer {
475
477
  else {
476
478
  const queryType = buf[0];
477
479
  if (queryType == 2) {
478
- const s = 13 + readUint16LE(buf, 11);
479
- const sortLen = readUint16LE(buf, s);
480
+ const s = 13 + readUint16(buf, 11);
481
+ const sortLen = readUint16(buf, s);
480
482
  if (sortLen) {
481
- const typeId = readUint16LE(buf, 1);
483
+ const typeId = readUint16(buf, 1);
482
484
  const sort = buf.slice(s + 2, s + 2 + sortLen);
483
485
  const field = sort[1];
484
- const start = readUint16LE(sort, 3);
486
+ const start = readUint16(sort, 3);
485
487
  let sortIndex = this.getSortIndex(typeId, field, start, 0);
486
488
  if (!sortIndex) {
487
489
  if (this.processingQueries) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@based/db",
3
- "version": "0.0.43",
3
+ "version": "0.0.44",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -38,9 +38,9 @@
38
38
  "basedDbNative.cjs"
39
39
  ],
40
40
  "dependencies": {
41
- "@based/schema": "5.0.0-alpha.15",
41
+ "@based/schema": "5.0.0-alpha.16",
42
42
  "@saulx/hash": "^3.0.0",
43
- "@saulx/utils": "^6.6.0",
43
+ "@saulx/utils": "^6.7.0",
44
44
  "exit-hook": "^4.0.0",
45
45
  "picocolors": "^1.1.0",
46
46
  "@based/crc32c": "^1.0.0"