@based/db 0.0.46 → 0.0.47

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.
Binary file
Binary file
@@ -8,6 +8,7 @@ import { DbServer } from '../server/index.js';
8
8
  import { TransformFns } from '../server/migrate/index.js';
9
9
  import { ModifyOpts } from './modify/types.js';
10
10
  import { OnClose, OnData, OnError } from './query/subscription/types.js';
11
+ import { SubStore } from './query/subscription/index.js';
11
12
  export type DbClientHooks = {
12
13
  setSchema(schema: StrictSchema, fromStart?: boolean, transformFns?: TransformFns): Promise<DbServer['schema']>;
13
14
  flushModify(buf: Uint8Array): Promise<{
@@ -15,7 +16,7 @@ export type DbClientHooks = {
15
16
  dbWriteTime?: number;
16
17
  }>;
17
18
  getQueryBuf(buf: Uint8Array): Promise<Uint8Array>;
18
- subscribe(q: BasedDbQuery, onData: OnData, onError?: OnError): OnClose;
19
+ subscribe(q: BasedDbQuery, onData: (buf: Uint8Array) => ReturnType<OnData>, onError?: OnError): OnClose;
19
20
  };
20
21
  type DbClientOpts = {
21
22
  hooks: DbClientHooks;
@@ -26,6 +27,7 @@ type DbClientOpts = {
26
27
  type DbClientSchema = DbServer['schema'];
27
28
  export declare class DbClient {
28
29
  constructor({ hooks, maxModifySize, flushTime, debug, }: DbClientOpts);
30
+ subs: Map<BasedDbQuery, SubStore>;
29
31
  flushTime: number;
30
32
  flushReady: () => void;
31
33
  flushIsReady: Promise<void>;
@@ -30,6 +30,7 @@ export class DbClient {
30
30
  debugMode(this);
31
31
  }
32
32
  }
33
+ subs = new Map();
33
34
  flushTime;
34
35
  flushReady;
35
36
  flushIsReady;
@@ -75,9 +76,14 @@ export class DbClient {
75
76
  if (this.modifyCtx.len > 8) {
76
77
  console.info('Modify cancelled - schema updated');
77
78
  }
79
+ // cancel modify queue
78
80
  const resCtx = this.modifyCtx.ctx;
79
81
  this.modifyCtx.reset();
80
82
  execCtxQueue(resCtx, true);
83
+ // resubscribe
84
+ for (const [q, store] of this.subs) {
85
+ store.resubscribe(q);
86
+ }
81
87
  if (this.listeners?.schema) {
82
88
  for (const cb of this.listeners.schema) {
83
89
  cb(this.schema);
@@ -180,10 +186,14 @@ export class DbClient {
180
186
  return expire(this, type, typeof id === 'number' ? id : id.tmpId, seconds);
181
187
  }
182
188
  destroy() {
183
- this.modifyCtx.len = 0;
189
+ this.stop();
184
190
  this.modifyCtx.db = null; // Make sure we don't have a circular ref and leak mem
185
191
  }
186
192
  stop() {
193
+ for (const [, { onClose }] of this.subs) {
194
+ onClose();
195
+ }
196
+ this.subs.clear();
187
197
  this.modifyCtx.len = 0;
188
198
  }
189
199
  // For more advanced / internal usage - use isModified instead for most cases
@@ -1,7 +1,7 @@
1
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
- import { OnData, OnError } from './subscription/index.js';
4
+ import { OnData, OnError, OnClose } from './subscription/index.js';
5
5
  import { DbClient } from '../index.js';
6
6
  import { LangName } from '@based/schema';
7
7
  import { FilterAst, FilterBranchFn, FilterOpts } from './filter/types.js';
@@ -48,7 +48,7 @@ export declare class BasedDbQuery extends QueryBranch<BasedDbQuery> {
48
48
  buffer: Uint8Array;
49
49
  register(): void;
50
50
  locale(locale: LangName): this;
51
- subscribe(onData: OnData, onError?: OnError): import("./subscription/types.js").OnClose;
51
+ subscribe(onData: OnData, onError?: OnError): OnClose;
52
52
  _getSync(dbCtxExternal: any): BasedQueryResponse;
53
53
  toBuffer(): Uint8Array;
54
54
  }
@@ -1,4 +1,14 @@
1
1
  import { BasedDbQuery } from '../BasedDbQuery.js';
2
+ import { BasedQueryResponse } from '../BasedIterable.js';
2
3
  import { OnData, OnError, OnClose } from './types.js';
4
+ export declare class SubStore {
5
+ listeners: Map<OnData, OnError>;
6
+ onClose: OnClose;
7
+ response?: BasedQueryResponse;
8
+ checksum?: number;
9
+ len?: number;
10
+ subscribe(q: BasedDbQuery): void;
11
+ resubscribe(q: BasedDbQuery): void;
12
+ }
3
13
  export declare const subscribe: (q: BasedDbQuery, onData: OnData, onError?: OnError) => OnClose;
4
14
  export * from './types.js';
@@ -1,11 +1,66 @@
1
+ import { BasedQueryResponse } from '../BasedIterable.js';
1
2
  import { includeField } from '../query.js';
2
3
  import { registerQuery } from '../registerQuery.js';
4
+ export class SubStore {
5
+ listeners;
6
+ onClose;
7
+ response;
8
+ checksum;
9
+ len;
10
+ subscribe(q) {
11
+ if (!q.def.include.stringFields.size && !q.def.references.size) {
12
+ includeField(q.def, '*');
13
+ }
14
+ registerQuery(q);
15
+ this.onClose = q.db.hooks.subscribe(q, (res) => {
16
+ if (!this.response) {
17
+ this.response = new BasedQueryResponse(q.id, q.def, res, 0);
18
+ }
19
+ else {
20
+ this.response.result = res;
21
+ this.response.end = res.byteLength;
22
+ }
23
+ const checksum = this.response.checksum;
24
+ const len = res.byteLength;
25
+ if (this.len !== len || this.checksum !== checksum) {
26
+ for (const [onData] of this.listeners) {
27
+ onData(this.response);
28
+ }
29
+ this.len = len;
30
+ this.checksum = checksum;
31
+ }
32
+ }, (err) => {
33
+ for (const [, onError] of this.listeners) {
34
+ onError(err);
35
+ }
36
+ });
37
+ }
38
+ resubscribe(q) {
39
+ this.onClose();
40
+ q.reBuildQuery();
41
+ this.response = null;
42
+ this.subscribe(q);
43
+ }
44
+ }
3
45
  export const subscribe = (q, onData, onError) => {
4
- if (!q.def.include.stringFields.size && !q.def.references.size) {
5
- includeField(q.def, '*');
46
+ if (!q.db.subs.has(q)) {
47
+ const store = new SubStore();
48
+ store.listeners = new Map([[onData, onError]]);
49
+ store.subscribe(q);
50
+ q.db.subs.set(q, store);
51
+ }
52
+ else {
53
+ const store = q.db.subs.get(q);
54
+ store.listeners.set(onData, onError);
6
55
  }
7
- registerQuery(q);
8
- return q.db.hooks.subscribe(q, onData, onError);
56
+ return () => {
57
+ const store = q.db.subs.get(q);
58
+ store.listeners.delete(onData);
59
+ if (!store.listeners.size) {
60
+ q.db.subs.delete(q);
61
+ store.onClose();
62
+ }
63
+ };
9
64
  };
10
65
  export * from './types.js';
11
66
  //# sourceMappingURL=index.js.map
package/dist/src/index.js CHANGED
@@ -3,9 +3,7 @@ import { ModifyCtx } from './client/flushModify.js';
3
3
  import { DbServer } from './server/index.js';
4
4
  import { DbClient } from './client/index.js';
5
5
  import { wait } from '@saulx/utils';
6
- import { debugMode, debugServer } from './utils.js';
7
- import { BasedQueryResponse } from './client/query/BasedIterable.js';
8
- import { registerQuery } from './client/query/registerQuery.js';
6
+ import { debugMode, debugServer, getDefaultHooks } from './utils.js';
9
7
  export * from './client/modify/modify.js';
10
8
  export { compress, decompress };
11
9
  export { ModifyCtx }; // TODO move this somewhere
@@ -51,66 +49,7 @@ export class BasedDb {
51
49
  });
52
50
  const client = new DbClient({
53
51
  maxModifySize,
54
- hooks: {
55
- subscribe(q, onData, onError) {
56
- let killed = false;
57
- let timer;
58
- let prevChecksum;
59
- let lastLen = 0;
60
- let response;
61
- const get = async () => {
62
- const schemaVersion = q.def.schemaChecksum;
63
- if (schemaVersion && schemaVersion !== server.schema.hash) {
64
- q.reBuildQuery();
65
- registerQuery(q);
66
- response = undefined;
67
- timer = setTimeout(get, 0);
68
- return;
69
- }
70
- if (killed) {
71
- return;
72
- }
73
- const res = await server.getQueryBuf(q.buffer);
74
- if (killed) {
75
- return;
76
- }
77
- if (!response) {
78
- response = new BasedQueryResponse(q.id, q.def, res, 0);
79
- }
80
- else {
81
- response.result = res;
82
- response.end = res.byteLength;
83
- }
84
- const checksum = response.checksum;
85
- if (lastLen != res.byteLength || checksum != prevChecksum) {
86
- onData(response);
87
- lastLen = res.byteLength;
88
- prevChecksum = checksum;
89
- }
90
- timer = setTimeout(get, 200);
91
- };
92
- get();
93
- return () => {
94
- killed = true;
95
- clearTimeout(timer);
96
- };
97
- },
98
- setSchema(schema, fromStart) {
99
- return Promise.resolve(server.setSchema(schema, fromStart));
100
- },
101
- flushModify(buf) {
102
- const d = performance.now();
103
- const offsets = server.modify(buf);
104
- const dbWriteTime = performance.now() - d;
105
- return Promise.resolve({
106
- offsets,
107
- dbWriteTime,
108
- });
109
- },
110
- getQueryBuf(buf) {
111
- return Promise.resolve(server.getQueryBuf(buf));
112
- },
113
- },
52
+ hooks: getDefaultHooks(server),
114
53
  });
115
54
  this.server = server;
116
55
  this.client = client;
@@ -1,6 +1,21 @@
1
1
  import { DbServer } from './server/index.js';
2
+ import { BasedDbQuery } from './client/query/BasedDbQuery.js';
3
+ import { OnError } from './client/query/subscription/types.js';
4
+ import { StrictSchema } from '@based/schema';
2
5
  export declare const DECODER: TextDecoder;
3
6
  export declare const ENCODER: TextEncoder;
4
7
  export declare const debugMode: (target: any, getInfo?: any) => void;
5
8
  export declare const debugServer: (server: DbServer) => void;
6
9
  export declare const schemaLooseEqual: (a: any, b: any, key?: string) => boolean;
10
+ export declare const getDefaultHooks: (server: DbServer, subInterval?: number) => {
11
+ subscribe(q: BasedDbQuery, onData: (res: Uint8Array) => void, onError: OnError): () => void;
12
+ setSchema(schema: StrictSchema, fromStart: boolean): Promise<StrictSchema & {
13
+ lastId: number;
14
+ hash?: number;
15
+ }>;
16
+ flushModify(buf: Uint8Array): Promise<{
17
+ offsets: Record<number, number>;
18
+ dbWriteTime: number;
19
+ }>;
20
+ getQueryBuf(buf: Uint8Array): Promise<Uint8Array>;
21
+ };
package/dist/src/utils.js CHANGED
@@ -90,4 +90,50 @@ export const schemaLooseEqual = (a, b, key) => {
90
90
  }
91
91
  return true;
92
92
  };
93
+ export const getDefaultHooks = (server, subInterval = 200) => {
94
+ return {
95
+ subscribe(q, onData, onError) {
96
+ let timer;
97
+ let killed = false;
98
+ const poll = async () => {
99
+ const res = await server.getQueryBuf(q.buffer);
100
+ if (killed) {
101
+ return;
102
+ }
103
+ if (res.byteLength >= 8) {
104
+ onData(res);
105
+ }
106
+ else if (res.byteLength === 1 && res[0] === 0) {
107
+ console.info('schema mismatch, should resolve after update');
108
+ // ignore update and stop polling
109
+ return;
110
+ }
111
+ else {
112
+ onError(new Error('unexpected error'));
113
+ }
114
+ timer = setTimeout(poll, subInterval);
115
+ };
116
+ poll();
117
+ return () => {
118
+ clearTimeout(timer);
119
+ killed = true;
120
+ };
121
+ },
122
+ setSchema(schema, fromStart) {
123
+ return Promise.resolve(server.setSchema(schema, fromStart));
124
+ },
125
+ flushModify(buf) {
126
+ const d = performance.now();
127
+ const offsets = server.modify(buf);
128
+ const dbWriteTime = performance.now() - d;
129
+ return Promise.resolve({
130
+ offsets,
131
+ dbWriteTime,
132
+ });
133
+ },
134
+ getQueryBuf(buf) {
135
+ return Promise.resolve(server.getQueryBuf(buf));
136
+ },
137
+ };
138
+ };
93
139
  //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@based/db",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",