@based/db 0.0.20 → 0.0.21

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 (99) hide show
  1. package/basedDbNative.cjs +6 -2
  2. package/dist/lib/darwin_aarch64/include/selva/db.h +3 -3
  3. package/dist/lib/darwin_aarch64/include/selva/fast_memcmp.h +18 -0
  4. package/dist/lib/darwin_aarch64/include/selva/fields.h +3 -0
  5. package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
  6. package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
  7. package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
  8. package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
  9. package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
  10. package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
  11. package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
  12. package/dist/lib/linux_aarch64/include/selva/db.h +3 -3
  13. package/dist/lib/linux_aarch64/include/selva/fast_memcmp.h +18 -0
  14. package/dist/lib/linux_aarch64/include/selva/fields.h +3 -0
  15. package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
  16. package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
  17. package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
  18. package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
  19. package/dist/lib/linux_aarch64/libselva.so +0 -0
  20. package/dist/lib/linux_x86_64/include/selva/db.h +3 -3
  21. package/dist/lib/linux_x86_64/include/selva/fast_memcmp.h +18 -0
  22. package/dist/lib/linux_x86_64/include/selva/fields.h +3 -0
  23. package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
  24. package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
  25. package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
  26. package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
  27. package/dist/lib/linux_x86_64/libselva.so +0 -0
  28. package/dist/src/client/bitWise.js +0 -1
  29. package/dist/src/client/crc32.d.ts +1 -1
  30. package/dist/src/client/modify/alias.js +3 -3
  31. package/dist/src/client/modify/binary.d.ts +1 -1
  32. package/dist/src/client/modify/binary.js +7 -6
  33. package/dist/src/client/modify/cardinality.d.ts +3 -1
  34. package/dist/src/client/modify/cardinality.js +8 -4
  35. package/dist/src/client/modify/create.js +41 -5
  36. package/dist/src/client/modify/delete.js +9 -8
  37. package/dist/src/client/modify/expire.js +1 -1
  38. package/dist/src/client/modify/modify.js +3 -3
  39. package/dist/src/client/modify/references/edge.js +15 -1
  40. package/dist/src/client/modify/setCursor.js +0 -2
  41. package/dist/src/client/modify/string.d.ts +1 -1
  42. package/dist/src/client/modify/string.js +5 -3
  43. package/dist/src/client/modify/text.d.ts +1 -1
  44. package/dist/src/client/modify/text.js +52 -9
  45. package/dist/src/client/modify/types.d.ts +5 -1
  46. package/dist/src/client/modify/types.js +2 -1
  47. package/dist/src/client/modify/vector.js +4 -4
  48. package/dist/src/client/query/filter/convertFilter.js +3 -1
  49. package/dist/src/client/query/filter/createFixedFilterBuffer.js +1 -1
  50. package/dist/src/client/query/filter/createVariableFilterBuffer.js +10 -4
  51. package/dist/src/client/query/filter/parseFilterValue.js +13 -4
  52. package/dist/src/client/query/filter/primitiveFilter.js +13 -3
  53. package/dist/src/client/query/filter/toBuffer.js +13 -3
  54. package/dist/src/client/query/filter/types.d.ts +5 -3
  55. package/dist/src/client/query/filter/types.js +12 -0
  56. package/dist/src/client/query/read/read.js +7 -1
  57. package/dist/src/client/query/search/index.js +25 -19
  58. package/dist/src/client/query/serialize.d.ts +4 -0
  59. package/dist/src/client/query/serialize.js +26 -0
  60. package/dist/src/client/query/sort.d.ts +1 -1
  61. package/dist/src/client/query/sort.js +5 -5
  62. package/dist/src/client/query/subscription/run.js +1 -1
  63. package/dist/src/client/query/types.d.ts +6 -2
  64. package/dist/src/client/query/validation.js +14 -32
  65. package/dist/src/client/string.d.ts +2 -2
  66. package/dist/src/client/string.js +32 -29
  67. package/dist/src/client/tmpBuffer.d.ts +3 -0
  68. package/dist/src/client/tmpBuffer.js +20 -0
  69. package/dist/src/client/xxHash64.d.ts +1 -1
  70. package/dist/src/client/xxHash64.js +1 -1
  71. package/dist/src/index.d.ts +2 -0
  72. package/dist/src/index.js +2 -0
  73. package/dist/src/native.d.ts +14 -13
  74. package/dist/src/native.js +41 -25
  75. package/dist/src/server/csmt/draw-dot.js +2 -1
  76. package/dist/src/server/csmt/memebership-proof.d.ts +1 -1
  77. package/dist/src/server/csmt/tree.d.ts +2 -1
  78. package/dist/src/server/csmt/tree.js +10 -9
  79. package/dist/src/server/csmt/types.d.ts +4 -3
  80. package/dist/src/server/index.d.ts +5 -4
  81. package/dist/src/server/index.js +5 -6
  82. package/dist/src/server/save.js +4 -3
  83. package/dist/src/server/start.d.ts +1 -0
  84. package/dist/src/server/start.js +43 -16
  85. package/dist/src/server/tree.d.ts +1 -1
  86. package/dist/src/server/tree.js +17 -2
  87. package/dist/src/server/worker.js +2 -1
  88. package/dist/src/utils.d.ts +4 -0
  89. package/dist/src/utils.js +84 -0
  90. package/package.json +3 -3
  91. package/dist/lib/darwin_aarch64/libnode-v20.11.1.node +0 -0
  92. package/dist/lib/darwin_aarch64/libnode-v20.18.1.node +0 -0
  93. package/dist/lib/darwin_aarch64/libnode-v22.13.0.node +0 -0
  94. package/dist/lib/linux_aarch64/libnode-v20.11.1.node +0 -0
  95. package/dist/lib/linux_aarch64/libnode-v20.18.1.node +0 -0
  96. package/dist/lib/linux_aarch64/libnode-v22.13.0.node +0 -0
  97. package/dist/lib/linux_x86_64/libnode-v20.11.1.node +0 -0
  98. package/dist/lib/linux_x86_64/libnode-v20.18.1.node +0 -0
  99. package/dist/lib/linux_x86_64/libnode-v22.13.0.node +0 -0
@@ -1,7 +1,9 @@
1
1
  import native from '../native.js';
2
2
  import { readUint32 } from './bitWise.js';
3
+ import makeTmpBuffer from './tmpBuffer.js';
3
4
  const DECODER = new TextDecoder('utf-8');
4
- // add encoder
5
+ const ENCODER = new TextEncoder();
6
+ const { getUint8Array: getTmpBuffer } = makeTmpBuffer(4096); // the usual page size?
5
7
  // type 0 = no compression; 1 = deflate
6
8
  // [lang] [type] [uncompressed size 4] [compressed string] [crc32]
7
9
  // var cnt = 0
@@ -11,24 +13,28 @@ const DECODER = new TextDecoder('utf-8');
11
13
  export const write = (buf, value, offset, noCompression, lang) => {
12
14
  value = value.normalize('NFKD');
13
15
  buf[offset] = lang || 0;
16
+ const { written: l } = ENCODER.encodeInto(value, buf.subarray(offset + 2));
17
+ let crc = native.crc32(buf.subarray(offset + 2, offset + 2 + l));
14
18
  // 50 maybe if lvl 1
15
19
  if (value.length > 200 && !noCompression) {
16
- const s = Buffer.byteLength(value, 'utf8');
17
- buf.write(value, offset + 6 + s, 'utf8');
18
- let crc = native.crc32(buf.subarray(offset + 6 + s, offset + 6 + 2 * s));
19
- const size = native.compress(buf, offset + 6, s);
20
+ buf.copyWithin(offset + 6 + l, offset + 2, offset + 2 + l);
21
+ const size = native.compress(buf, offset + 6, l);
20
22
  if (size === 0) {
21
23
  buf[offset + 1] = 0; // not compressed
22
- const len = buf.write(value, offset + 2, 'utf8');
23
- buf[offset + len + 2] = crc;
24
- buf[offset + len + 3] = crc >>>= 8;
25
- buf[offset + len + 4] = crc >>>= 8;
26
- buf[offset + len + 5] = crc >>>= 8;
27
- return len + 6;
24
+ ENCODER.encodeInto(value, buf.subarray(offset + 2));
25
+ buf[offset + l + 2] = crc;
26
+ buf[offset + l + 3] = crc >>>= 8;
27
+ buf[offset + l + 4] = crc >>>= 8;
28
+ buf[offset + l + 5] = crc >>>= 8;
29
+ return l + 6;
28
30
  }
29
31
  else {
32
+ let len = l;
30
33
  buf[offset + 1] = 1; // compressed
31
- buf.writeUInt32LE(s, offset + 2);
34
+ buf[offset + 2] = len;
35
+ buf[offset + 3] = len >>>= 8;
36
+ buf[offset + 4] = len >>>= 8;
37
+ buf[offset + 5] = len >>>= 8;
32
38
  buf[offset + size + 6] = crc;
33
39
  buf[offset + size + 7] = crc >>>= 8;
34
40
  buf[offset + size + 8] = crc >>>= 8;
@@ -38,23 +44,19 @@ export const write = (buf, value, offset, noCompression, lang) => {
38
44
  }
39
45
  else {
40
46
  buf[offset + 1] = 0; // not compressed
41
- const len = buf.write(value, offset + 2, 'utf8');
42
- let crc = native.crc32(buf.subarray(offset + 2, offset + len + 2));
43
- buf[offset + len + 2] = crc;
44
- buf[offset + len + 3] = crc >>>= 8;
45
- buf[offset + len + 4] = crc >>>= 8;
46
- buf[offset + len + 5] = crc >>>= 8;
47
- return len + 6;
47
+ buf[offset + l + 2] = crc;
48
+ buf[offset + l + 3] = crc >>>= 8;
49
+ buf[offset + l + 4] = crc >>>= 8;
50
+ buf[offset + l + 5] = crc >>>= 8;
51
+ return l + 6;
48
52
  }
49
53
  };
50
- let tmpCompressBlock;
51
54
  export const compress = (str) => {
52
- if (!tmpCompressBlock || tmpCompressBlock.byteLength < str.length * 3) {
53
- tmpCompressBlock = Buffer.allocUnsafe(str.length * 3);
54
- }
55
- const s = write(tmpCompressBlock, str, 0, false);
56
- const nBuffer = Buffer.allocUnsafe(s);
57
- tmpCompressBlock.copy(nBuffer, 0, 0, s);
55
+ const len = ENCODER.encode(str).byteLength;
56
+ const tmpCompressBlock = getTmpBuffer(len * 3);
57
+ const l = write(tmpCompressBlock, str, 0, false);
58
+ const nBuffer = new Uint8Array(l);
59
+ nBuffer.set(tmpCompressBlock.subarray(0, l));
58
60
  return nBuffer;
59
61
  };
60
62
  export const decompress = (val) => {
@@ -64,9 +66,10 @@ export const read = (val, offset, len) => {
64
66
  const type = val[offset + 1];
65
67
  if (type == 1) {
66
68
  const origSize = readUint32(val, offset + 2);
67
- const newBuffer = Buffer.allocUnsafe(origSize);
68
- native.decompress(Buffer.from(val), newBuffer, offset + 6, len - 6);
69
- return newBuffer.toString('utf8');
69
+ const newBuffer = getTmpBuffer(origSize);
70
+ // deflate in browser for this...
71
+ native.decompress(val, newBuffer, offset + 6, len - 6);
72
+ return DECODER.decode(newBuffer);
70
73
  }
71
74
  else if (type == 0) {
72
75
  return DECODER.decode(val.subarray(offset + 2, len + offset - 4));
@@ -0,0 +1,3 @@
1
+ export default function makeTmpBuffer(initialSize: number): {
2
+ getUint8Array: (size: number) => Uint8Array;
3
+ };
@@ -0,0 +1,20 @@
1
+ export default function makeTmpBuffer(initialSize) {
2
+ // @ts-ignore
3
+ let tmpBuffer = new ArrayBuffer(initialSize, { maxByteLength: initialSize });
4
+ return {
5
+ getUint8Array: (size) => {
6
+ const opts = {
7
+ maxByteLength: Math.min(Math.round(1.5 * size), 274877906944)
8
+ };
9
+ // @ts-ignore
10
+ if (tmpBuffer.maxByteLength < size) {
11
+ // @ts-ignore
12
+ tmpBuffer = new ArrayBuffer(size, opts);
13
+ }
14
+ // @ts-ignore
15
+ tmpBuffer.resize(size);
16
+ return new Uint8Array(tmpBuffer);
17
+ }
18
+ };
19
+ }
20
+ //# sourceMappingURL=tmpBuffer.js.map
@@ -1 +1 @@
1
- export declare const xxHash64: (buf: Buffer, target?: Buffer, index?: number) => Buffer;
1
+ export declare const xxHash64: (buf: Buffer | Uint8Array, target?: Uint8Array | Buffer, index?: number) => Uint8Array | Buffer;
@@ -1,7 +1,7 @@
1
1
  import native from '../native.js';
2
2
  export const xxHash64 = (buf, target, index) => {
3
3
  if (!target) {
4
- target = Buffer.allocUnsafe(8);
4
+ target = new Uint8Array(8);
5
5
  index = 0;
6
6
  }
7
7
  native.xxHash64(buf, target, index);
@@ -8,6 +8,8 @@ export { ModifyCtx };
8
8
  export { DbClient, DbServer };
9
9
  export { xxHash64 } from './client/xxHash64.js';
10
10
  export { crc32 } from './client/crc32.js';
11
+ export * from './client/query/serialize.js';
12
+ export * from './utils.js';
11
13
  export declare class BasedDb {
12
14
  #private;
13
15
  client: DbClient;
package/dist/src/index.js CHANGED
@@ -9,6 +9,8 @@ export { ModifyCtx }; // TODO move this somewhere
9
9
  export { DbClient, DbServer };
10
10
  export { xxHash64 } from './client/xxHash64.js';
11
11
  export { crc32 } from './client/crc32.js';
12
+ export * from './client/query/serialize.js';
13
+ export * from './utils.js';
12
14
  export class BasedDb {
13
15
  client;
14
16
  server;
@@ -1,8 +1,7 @@
1
1
  declare const _default: {
2
- historyAppend(history: any, typeId: number, nodeId: number): any;
2
+ historyAppend(history: any, typeId: number, nodeId: number, dbCtx: any): any;
3
3
  historyCreate(pathname: string, mainLen: number): any;
4
4
  workerCtxInit: () => void;
5
- markMerkleBlock: (buf: Buffer) => any;
6
5
  externalFromInt(address: BigInt): any;
7
6
  intFromExternal(external: any): BigInt;
8
7
  modify: (data: Buffer, types: Buffer, dbCtx: any) => any;
@@ -10,22 +9,24 @@ declare const _default: {
10
9
  start: (id: number) => any;
11
10
  stop: (dbCtx: any) => any;
12
11
  saveCommon: (path: string, dbCtx: any) => number;
13
- saveRange: (path: string, typeCode: number, start: number, end: number, dbCtx: any, hashOut: Buffer) => number;
14
- loadCommon: (path: string, dbCtx: any) => number;
15
- loadRange: (path: string, dbCtx: any) => number;
16
- updateSchemaType: (prefix: number, buf: Buffer, dbCtx: any) => any;
12
+ saveRange: (path: string, typeCode: number, start: number, end: number, dbCtx: any, hashOut: Uint8Array) => number;
13
+ loadCommon: (path: string, dbCtx: any) => void;
14
+ loadRange: (path: string, dbCtx: any) => void;
15
+ updateSchemaType: (prefix: number, buf: Uint8Array, dbCtx: any) => any;
17
16
  getTypeInfo: (typeId: number, dbCtx: any) => any;
18
- getNodeRangeHash: (typeId: number, start: number, end: number, bufOut: Buffer, dbCtx: any) => any;
17
+ getNodeRangeHash: (typeId: number, start: number, end: number, bufOut: Uint8Array, dbCtx: any) => any;
19
18
  createHash: () => {
20
- update: (buf: Buffer) => any;
21
- digest: (encoding?: BufferEncoding) => Buffer | string;
19
+ update: (buf: Buffer | Uint8Array) => any;
20
+ digest: (encoding?: "hex") => Uint8Array | string;
22
21
  reset: () => void;
23
22
  };
24
- compress: (buf: Buffer, offset: number, stringSize: number) => any;
25
- decompress: (input: Buffer, output: Buffer, offset: number, len: number) => any;
26
- crc32: (buf: Buffer) => any;
23
+ compress: (buf: Buffer | Uint8Array, offset: number, stringSize: number) => any;
24
+ decompress: (input: Buffer | Uint8Array, output: Buffer | Uint8Array, offset: number, len: number) => any;
25
+ crc32: (buf: Buffer | Uint8Array) => any;
27
26
  createSortIndex: (buf: Buffer, dbCtx: any) => any;
28
27
  destroySortIndex: (buf: Buffer, dbCtx: any) => any;
29
- xxHash64: (buf: Buffer, target: Buffer, index: number) => any;
28
+ xxHash64: (buf: Buffer | Uint8Array, target: Buffer | Uint8Array, index: number) => any;
29
+ base64encode: (dst: Uint8Array, src: Uint8Array, lineMax: number) => Uint8Array;
30
+ equals: (a: Uint8Array, b: Uint8Array) => boolean;
30
31
  };
31
32
  export default _default;
@@ -1,20 +1,26 @@
1
1
  // @ts-ignore
2
2
  import db from '../../basedDbNative.cjs';
3
- var compressor = null;
4
- var decompressor = null;
3
+ import { bufToHex } from './utils.js';
4
+ const selvaIoErrlog = new Uint8Array(256);
5
+ const textEncoder = new TextEncoder();
6
+ var compressor = db.createCompressor();
7
+ var decompressor = db.createDecompressor();
8
+ function SelvaIoErrlogToString(buf) {
9
+ let i;
10
+ let len = (i = buf.indexOf(0)) >= 0 ? i : buf.byteLength;
11
+ return new TextDecoder().decode(selvaIoErrlog.slice(0, len));
12
+ }
5
13
  export default {
6
- historyAppend(history, typeId, nodeId) {
7
- return db.historyCreate(history, typeId, nodeId);
14
+ historyAppend(history, typeId, nodeId, dbCtx) {
15
+ return db.historyAppend(history, typeId, nodeId, dbCtx);
8
16
  },
9
17
  historyCreate(pathname, mainLen) {
10
- return db.historyCreate(Buffer.from(pathname), mainLen + 16 - (mainLen % 16));
18
+ const pathBuf = textEncoder.encode(pathname + '\0');
19
+ return db.historyCreate(pathBuf, mainLen + 16 - (mainLen % 16));
11
20
  },
12
21
  workerCtxInit: () => {
13
22
  return db.workerCtxInit();
14
23
  },
15
- markMerkleBlock: (buf) => {
16
- // pstart,
17
- },
18
24
  externalFromInt(address) {
19
25
  return db.externalFromInt(address);
20
26
  },
@@ -35,20 +41,26 @@ export default {
35
41
  return db.stop(dbCtx);
36
42
  },
37
43
  saveCommon: (path, dbCtx) => {
38
- const buf = Buffer.concat([Buffer.from(path), Buffer.from([0])]);
39
- return db.saveCommon(buf, dbCtx);
44
+ const pathBuf = textEncoder.encode(path + '\0');
45
+ return db.saveCommon(pathBuf, dbCtx);
40
46
  },
41
47
  saveRange: (path, typeCode, start, end, dbCtx, hashOut) => {
42
- const buf = Buffer.concat([Buffer.from(path), Buffer.from([0])]);
43
- return db.saveRange(buf, typeCode, start, end, dbCtx, hashOut);
48
+ const pathBuf = textEncoder.encode(path + '\0');
49
+ return db.saveRange(pathBuf, typeCode, start, end, dbCtx, hashOut);
44
50
  },
45
51
  loadCommon: (path, dbCtx) => {
46
- const buf = Buffer.concat([Buffer.from(path), Buffer.from([0])]);
47
- return db.loadCommon(buf, dbCtx);
52
+ const pathBuf = textEncoder.encode(path + '\0');
53
+ const err = db.loadCommon(pathBuf, dbCtx, selvaIoErrlog);
54
+ if (err) {
55
+ throw new Error(`Failed to load common. selvaError: ${err} cause:\n${SelvaIoErrlogToString(selvaIoErrlog)}`);
56
+ }
48
57
  },
49
58
  loadRange: (path, dbCtx) => {
50
- const buf = Buffer.concat([Buffer.from(path), Buffer.from([0])]);
51
- return db.loadRange(buf, dbCtx);
59
+ const pathBuf = textEncoder.encode(path + '\0');
60
+ const err = db.loadRange(pathBuf, dbCtx, selvaIoErrlog);
61
+ if (err) {
62
+ throw new Error(`Failed to load a range. selvaError: ${err} cause:\n${SelvaIoErrlogToString(selvaIoErrlog)}`);
63
+ }
52
64
  },
53
65
  updateSchemaType: (prefix, buf, dbCtx) => {
54
66
  return db.updateSchema(prefix, buf, dbCtx);
@@ -67,9 +79,14 @@ export default {
67
79
  return hash;
68
80
  },
69
81
  digest: (encoding) => {
70
- const buf = Buffer.allocUnsafe(16);
82
+ const buf = new Uint8Array(16);
71
83
  db.hashDigest(state, buf);
72
- return encoding ? buf.toString(encoding) : buf;
84
+ if (encoding === 'hex') {
85
+ return bufToHex(buf);
86
+ }
87
+ else {
88
+ return buf;
89
+ }
73
90
  },
74
91
  reset: () => {
75
92
  db.hashReset(state);
@@ -77,17 +94,10 @@ export default {
77
94
  };
78
95
  return hash;
79
96
  },
80
- // needs to pass dbCtx: any
81
97
  compress: (buf, offset, stringSize) => {
82
- if (compressor === null) {
83
- compressor = db.createCompressor();
84
- }
85
98
  return db.compress(compressor, buf, offset, stringSize);
86
99
  },
87
100
  decompress: (input, output, offset, len) => {
88
- if (decompressor === null) {
89
- decompressor = db.createDecompressor();
90
- }
91
101
  return db.decompress(decompressor, input, output, offset, len);
92
102
  },
93
103
  crc32: (buf) => {
@@ -102,5 +112,11 @@ export default {
102
112
  xxHash64: (buf, target, index) => {
103
113
  return db.xxHash64(buf, target, index);
104
114
  },
115
+ base64encode: (dst, src, lineMax) => {
116
+ return db.base64encode(dst, src, lineMax);
117
+ },
118
+ equals: (a, b) => {
119
+ return !!db.equals(a, b);
120
+ },
105
121
  };
106
122
  //# sourceMappingURL=native.js.map
@@ -1,5 +1,6 @@
1
+ import { base64encode } from '../../utils.js';
1
2
  function makeLabel(node) {
2
- return `${node.key}\n${node.hash.toString('base64').substring(0, 5)}`;
3
+ return `${node.key}\n${base64encode(node.hash, 0).substring(0, 5)}`;
3
4
  }
4
5
  export default function draw(csmt) {
5
6
  const root = csmt.getRoot();
@@ -3,5 +3,5 @@ export declare enum Direction {
3
3
  Left = "L",
4
4
  Right = "R"
5
5
  }
6
- export type Proof = [TreeKey | null, TreeKey | null] | [TreeKey | Buffer, TreeKey | Direction][];
6
+ export type Proof = [TreeKey | null, TreeKey | null] | [TreeKey | Uint8Array, TreeKey | Direction][];
7
7
  export default function membershipProof(root: TreeNode | null, k: TreeKey): Proof;
@@ -1,2 +1,3 @@
1
- import { Csmt } from './types.js';
1
+ import { Hash, Csmt } from './types.js';
2
+ export declare function hashEq(a: Hash, b: Hash): boolean;
2
3
  export declare function createTree(createHash: () => any): Csmt;
@@ -1,12 +1,13 @@
1
1
  import { TreeKeyNil } from './types.js';
2
2
  import { distance, min, max } from './tree-utils.js';
3
3
  import membershipProof from './memebership-proof.js';
4
+ import { equals } from '../../utils.js';
5
+ export function hashEq(a, b) {
6
+ return equals(a, b);
7
+ }
4
8
  export function createTree(createHash) {
5
9
  let root = null;
6
- const emptyHash = genHash(Buffer.from(''));
7
- function genHash(s) {
8
- return createHash().update(s).digest();
9
- }
10
+ const emptyHash = createHash().digest();
10
11
  function genNodeHash(lHash, rHash) {
11
12
  return createHash().update(lHash).update(rHash).digest();
12
13
  }
@@ -123,7 +124,7 @@ export function createTree(createHash) {
123
124
  if (nodeA &&
124
125
  nodeB &&
125
126
  nodeA.key === nodeB.key &&
126
- nodeA.hash.compare(nodeB.hash) === 0) {
127
+ hashEq(nodeA.hash, nodeB.hash)) {
127
128
  return;
128
129
  }
129
130
  // No hash match
@@ -134,7 +135,7 @@ export function createTree(createHash) {
134
135
  // Check if this is a leaf that is missing from the right tree
135
136
  if (nodeA && !leftA && !rightA) {
136
137
  const bHash = diffB.get(nodeA.key);
137
- if (bHash && bHash.compare(nodeA.hash) === 0) {
138
+ if (bHash && hashEq(bHash, nodeA.hash)) {
138
139
  // The same leaf appears to exist in both trees
139
140
  diffB.delete(nodeA.key);
140
141
  }
@@ -146,7 +147,7 @@ export function createTree(createHash) {
146
147
  // Check if this is a leaf that is missing from the left tree
147
148
  if (nodeB && !leftB && !rightB) {
148
149
  const aHash = diffA.get(nodeB.key);
149
- if (aHash && aHash.compare(nodeB.hash) === 0) {
150
+ if (aHash && hashEq(aHash, nodeB.hash)) {
150
151
  diffA.delete(nodeB.key);
151
152
  }
152
153
  else {
@@ -198,8 +199,8 @@ export function createTree(createHash) {
198
199
  return {
199
200
  getRoot: () => root,
200
201
  insert: (k, h, data = null) => {
201
- if (!(h instanceof Buffer)) {
202
- throw new TypeError('`h` must be a Buffer');
202
+ if (!(h instanceof Uint8Array)) { // TODO can we extract the name somehow from Hash?
203
+ throw new TypeError('`h` must be a Uint8Array'); // TODO can we extract the name somehow from Hash?
203
204
  }
204
205
  const newLeaf = createLeaf(k, h, data);
205
206
  root = root ? insert(root, newLeaf) : newLeaf;
@@ -1,14 +1,15 @@
1
1
  import { Proof } from './memebership-proof.js';
2
2
  export type TreeKey = number;
3
3
  export declare const TreeKeyNil = 0;
4
+ export type Hash = Uint8Array;
4
5
  export interface TreeNode {
5
- hash: Buffer;
6
+ hash: Hash;
6
7
  key: TreeKey;
7
8
  data?: any;
8
9
  left: TreeNode | null;
9
10
  right: TreeNode | null;
10
11
  }
11
- export type KeyHashPair = [TreeKey, Buffer];
12
+ export type KeyHashPair = [TreeKey, Hash];
12
13
  export interface TreeDiff {
13
14
  left: KeyHashPair[];
14
15
  right: KeyHashPair[];
@@ -21,7 +22,7 @@ export interface Csmt {
21
22
  /**
22
23
  * Insert a new key-hash pair.
23
24
  */
24
- insert: (k: TreeKey, h: Buffer, data?: any) => void;
25
+ insert: (k: TreeKey, h: Hash, data?: any) => void;
25
26
  /**
26
27
  * Delete a key-hash pair from the tree.
27
28
  */
@@ -36,8 +36,8 @@ export declare class DbServer {
36
36
  merkleTree: ReturnType<typeof createTree>;
37
37
  dirtyRanges: Set<number>;
38
38
  csmtHashFun: {
39
- update: (buf: Buffer) => any;
40
- digest: (encoding?: BufferEncoding) => Buffer | string;
39
+ update: (buf: Buffer | Uint8Array) => any;
40
+ digest: (encoding?: "hex") => Uint8Array | string;
41
41
  reset: () => void;
42
42
  };
43
43
  workers: DbWorker[];
@@ -55,11 +55,12 @@ export declare class DbServer {
55
55
  });
56
56
  start(opts?: {
57
57
  clean?: boolean;
58
+ hosted?: boolean;
58
59
  }): Promise<void>;
59
60
  save(): void | Promise<void>;
60
61
  createCsmtHashFun: () => {
61
- update: (buf: Buffer) => any;
62
- digest: (encoding?: BufferEncoding) => Buffer | string;
62
+ update: (buf: Buffer | Uint8Array) => any;
63
+ digest: (encoding?: "hex") => Uint8Array | string;
63
64
  reset: () => void;
64
65
  };
65
66
  sortIndexes: {
@@ -2,9 +2,8 @@ import native from '../native.js';
2
2
  import { rm, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { getPropType, langCodesMap, } from '@based/schema';
5
- import {
6
- // genRootId,
7
- updateTypeDefs, schemaToSelvaBuffer, } from '@based/schema/def';
5
+ import { updateTypeDefs, schemaToSelvaBuffer, } from '@based/schema/def';
6
+ import { hashEq } from './csmt/index.js';
8
7
  import { start } from './start.js';
9
8
  import { foreachDirtyBlock, makeCsmtKey, makeCsmtKeyFromNodeId, } from './tree.js';
10
9
  import { save } from './save.js';
@@ -251,7 +250,7 @@ export class DbServer {
251
250
  const hash = Buffer.allocUnsafe(16);
252
251
  native.getNodeRangeHash(typeId, start, end, hash, this.dbCtxExternal);
253
252
  if (oldLeaf) {
254
- if (oldLeaf.hash.equals(hash)) {
253
+ if (hashEq(oldLeaf.hash, hash)) {
255
254
  return;
256
255
  }
257
256
  try {
@@ -331,7 +330,7 @@ export class DbServer {
331
330
  const type = this.schemaTypesParsed[types[i]];
332
331
  // TODO should not crash!
333
332
  try {
334
- native.updateSchemaType(type.id, s[i], this.dbCtxExternal);
333
+ native.updateSchemaType(type.id, new Uint8Array(s[i]), this.dbCtxExternal);
335
334
  }
336
335
  catch (err) {
337
336
  console.error('Cannot update schema on selva', type.type, err, s[i]);
@@ -465,7 +464,7 @@ export class DbServer {
465
464
  }
466
465
  this.stopped = true;
467
466
  clearTimeout(this.cleanupTimer);
468
- this.unlistenExit();
467
+ this?.unlistenExit();
469
468
  try {
470
469
  if (!noSave) {
471
470
  await this.save();
@@ -3,6 +3,7 @@ import { writeFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { destructureCsmtKey, foreachDirtyBlock } from './tree.js';
5
5
  import { writeFileSync } from 'node:fs';
6
+ import { bufToHex } from '../utils.js';
6
7
  const WRITELOG_FILE = 'writelog.json';
7
8
  const COMMON_SDB_FILE = 'common.sdb';
8
9
  const block_sdb_file = (typeId, start, end) => `${typeId}_${start}_${end}.sdb`;
@@ -19,7 +20,7 @@ export function save(db, sync = false) {
19
20
  foreachDirtyBlock(db, (mtKey, typeId, start, end) => {
20
21
  const file = block_sdb_file(typeId, start, end);
21
22
  const path = join(db.fileSystemPath, file);
22
- const hash = Buffer.allocUnsafe(16);
23
+ const hash = new Uint8Array(16);
23
24
  err = native.saveRange(path, typeId, start, end, db.dbCtxExternal, hash);
24
25
  if (err) {
25
26
  console.error(`Save ${typeId}:${start}-${end} failed: ${err}`);
@@ -53,7 +54,7 @@ export function save(db, sync = false) {
53
54
  db.merkleTree.visitLeafNodes((leaf) => {
54
55
  const [typeId] = destructureCsmtKey(leaf.key);
55
56
  const data = leaf.data;
56
- dumps[typeId].push({ ...data, hash: leaf.hash.toString('hex') });
57
+ dumps[typeId].push({ ...data, hash: bufToHex(leaf.hash) });
57
58
  });
58
59
  const data = {
59
60
  ts,
@@ -63,7 +64,7 @@ export function save(db, sync = false) {
63
64
  };
64
65
  const mtRoot = db.merkleTree.getRoot();
65
66
  if (mtRoot) {
66
- data.hash = mtRoot.hash.toString('hex');
67
+ data.hash = bufToHex(mtRoot.hash);
67
68
  }
68
69
  const filePath = join(db.fileSystemPath, WRITELOG_FILE);
69
70
  const content = JSON.stringify(data);
@@ -2,4 +2,5 @@ import { DbServer } from './index.js';
2
2
  import './worker.js';
3
3
  export declare function start(db: DbServer, opts: {
4
4
  clean?: boolean;
5
+ hosted?: boolean;
5
6
  }): Promise<void>;
@@ -3,13 +3,14 @@ import { DbWorker } from './index.js';
3
3
  import native from '../native.js';
4
4
  import { rm, mkdir, readFile } from 'node:fs/promises';
5
5
  import { join } from 'node:path';
6
- import { createTree } from './csmt/index.js';
6
+ import { createTree, hashEq } from './csmt/index.js';
7
7
  import { foreachBlock } from './tree.js';
8
8
  import { availableParallelism } from 'node:os';
9
9
  import exitHook from 'exit-hook';
10
10
  import './worker.js';
11
11
  import { save } from './save.js';
12
12
  import { DEFAULT_BLOCK_CAPACITY } from '@based/schema/def';
13
+ import { bufToHex, hexToBuf } from '../utils.js';
13
14
  const SCHEMA_FILE = 'schema.json';
14
15
  const WRITELOG_FILE = 'writelog.json';
15
16
  const makeCsmtKey = (typeId, start) => typeId * 4294967296 + start;
@@ -28,15 +29,23 @@ export async function start(db, opts) {
28
29
  try {
29
30
  writelog = JSON.parse((await readFile(join(path, WRITELOG_FILE))).toString());
30
31
  // Load the common dump
31
- native.loadCommon(join(path, writelog.commonDump), db.dbCtxExternal);
32
+ try {
33
+ native.loadCommon(join(path, writelog.commonDump), db.dbCtxExternal);
34
+ }
35
+ catch (e) {
36
+ console.log(e.message);
37
+ throw e;
38
+ }
32
39
  // Load all range dumps
33
40
  for (const typeId in writelog.rangeDumps) {
34
41
  const dumps = writelog.rangeDumps[typeId];
35
42
  for (const dump of dumps) {
36
43
  const fname = dump.file;
37
- const err = native.loadRange(join(path, fname), db.dbCtxExternal);
38
- if (err) {
39
- console.log(`Failed to load a range. file: "${fname}": ${err}`);
44
+ try {
45
+ native.loadRange(join(path, fname), db.dbCtxExternal);
46
+ }
47
+ catch (e) {
48
+ console.log(e.message);
40
49
  }
41
50
  }
42
51
  }
@@ -46,13 +55,22 @@ export async function start(db, opts) {
46
55
  db.putSchema(JSON.parse(schema.toString()), true);
47
56
  }
48
57
  }
49
- catch (err) { }
58
+ catch (err) {
59
+ // TODO In some cases we really should give up!
60
+ }
50
61
  // The merkle tree should be empty at start.
51
62
  if (!db.merkleTree || db.merkleTree.getRoot()) {
52
63
  db.merkleTree = createTree(db.createCsmtHashFun);
53
64
  }
54
- for (const key in db.schemaTypesParsed) {
55
- const def = db.schemaTypesParsed[key];
65
+ // FDN-791 CSMT is unstable (not history independent)
66
+ // For now we just sort the types to ensure that we always
67
+ // load in the same order.
68
+ const types = Object.keys(db.schemaTypesParsed).sort((a, b) => db.schemaTypesParsed[a].id - db.schemaTypesParsed[b].id).reduce((obj, key) => {
69
+ obj[key] = db.schemaTypesParsed[key];
70
+ return obj;
71
+ }, {});
72
+ for (const key in types) {
73
+ const def = types[key];
56
74
  const [total, lastId] = native.getTypeInfo(def.id, db.dbCtxExternal);
57
75
  def.total = total;
58
76
  def.lastId = writelog?.types[def.id]?.lastId || lastId;
@@ -71,10 +89,10 @@ export async function start(db, opts) {
71
89
  });
72
90
  }
73
91
  if (writelog?.hash) {
74
- const oldHash = Buffer.from(writelog.hash, 'hex');
92
+ const oldHash = hexToBuf(writelog.hash);
75
93
  const newHash = db.merkleTree.getRoot()?.hash;
76
- if (!oldHash.equals(newHash)) {
77
- console.error(`WARN: CSMT hash mismatch: ${writelog.hash} != ${newHash.toString('hex')}`);
94
+ if (!hashEq(oldHash, newHash)) {
95
+ console.error(`WARN: CSMT hash mismatch: ${writelog.hash} != ${bufToHex(newHash)}`);
78
96
  }
79
97
  }
80
98
  // start workers
@@ -84,10 +102,19 @@ export async function start(db, opts) {
84
102
  while (i--) {
85
103
  db.workers[i] = new DbWorker(address, db);
86
104
  }
87
- db.unlistenExit = exitHook((signal) => {
88
- console.log(`Exiting with signal: ${signal}`);
89
- save(db, true);
90
- console.log('Successfully saved.');
91
- });
105
+ if (!opts?.hosted) {
106
+ db.unlistenExit = exitHook(async (signal) => {
107
+ const blockSig = () => { };
108
+ const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
109
+ // A really dumb way to block signals temporarily while saving.
110
+ // This is needed because there is no way to set the process signal mask
111
+ // in Node.js.
112
+ signals.forEach((sig) => process.on(sig, blockSig));
113
+ console.log(`Exiting with signal: ${signal}`);
114
+ save(db, true);
115
+ console.log('Successfully saved.');
116
+ signals.forEach((sig) => process.off(sig, blockSig));
117
+ });
118
+ }
92
119
  }
93
120
  //# sourceMappingURL=start.js.map
@@ -9,5 +9,5 @@ export type CsmtNodeRange = {
9
9
  export declare const makeCsmtKey: (typeId: number, start: number) => number;
10
10
  export declare const destructureCsmtKey: (key: number) => number[];
11
11
  export declare const makeCsmtKeyFromNodeId: (typeId: number, blockCapacity: number, nodeId: number) => number;
12
- export declare function foreachBlock(db: DbServer, def: SchemaTypeDef, cb: (start: number, end: number, hash: Buffer) => void): Promise<void>;
12
+ export declare function foreachBlock(db: DbServer, def: SchemaTypeDef, cb: (start: number, end: number, hash: Uint8Array) => void): Promise<void>;
13
13
  export declare function foreachDirtyBlock(db: DbServer, cb: (mtKey: number, typeId: number, start: number, end: number) => void): Promise<void>;