nesoi 3.3.17 → 3.3.19

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.
@@ -68,8 +68,9 @@ class BrowserDBService extends service_1.Service {
68
68
  return async (trx, fn, services) => {
69
69
  const module = trx.engine.getModule();
70
70
  const db = await BrowserDBService.db(services[service], module);
71
- const refNames = Object.keys(module.buckets)
72
- .map(key => key.includes('::')
71
+ const refNames = Object.entries(module.buckets)
72
+ .filter(([_, val]) => val.adapter instanceof browserdb_bucket_adapter_1.BrowserDBBucketAdapter)
73
+ .map(([key]) => key.includes('::')
73
74
  ? key
74
75
  : `${module.name}::${key}`);
75
76
  const trxData = {};
@@ -17,11 +17,11 @@ export type BucketAdapterConfig = {
17
17
  * @category Adapters
18
18
  * @subcategory Entity
19
19
  * */
20
- export declare abstract class BucketAdapter<Obj extends NesoiObj> {
20
+ export declare abstract class BucketAdapter<Obj extends NesoiObj, Config extends BucketAdapterConfig = BucketAdapterConfig> {
21
21
  protected schema: $Bucket;
22
22
  nql: NQLRunner;
23
- config: BucketAdapterConfig;
24
- constructor(schema: $Bucket, nql: NQLRunner, config?: Partial<BucketAdapterConfig>);
23
+ config: Config;
24
+ constructor(schema: $Bucket, nql: NQLRunner, config?: Partial<Config>);
25
25
  /**
26
26
  * **DANGEROUS!**
27
27
  * This should only be used on inner adapters of bucket caches.
@@ -158,4 +158,4 @@ export declare abstract class BucketAdapter<Obj extends NesoiObj> {
158
158
  */
159
159
  getUpdateEpoch(obj: Obj): number;
160
160
  }
161
- export type AnyBucketAdapter = BucketAdapter<any>;
161
+ export type AnyBucketAdapter = BucketAdapter<any, any>;
@@ -16,6 +16,7 @@ class BucketAdapter {
16
16
  this.schema = schema;
17
17
  this.nql = nql;
18
18
  this.config = {
19
+ ...config,
19
20
  meta: {
20
21
  created_at: config?.meta?.created_at || 'created_at',
21
22
  created_by: config?.meta?.created_by || 'created_by',
@@ -33,10 +33,23 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
33
33
  if (part.union.sort?.length) {
34
34
  const sort = part.union.sort;
35
35
  output.sort((a, b) => {
36
+ let fallback = 0;
36
37
  for (let i = 0; i < sort.length; i++) {
37
38
  const s = sort[i];
38
39
  const a_val = tree_1.Tree.get(a, s.key);
39
40
  const b_val = tree_1.Tree.get(b, s.key);
41
+ if (a_val == null) {
42
+ if (b_val == null)
43
+ return 0;
44
+ fallback = -1;
45
+ continue;
46
+ }
47
+ else if (b_val == null) {
48
+ if (a_val == null)
49
+ return 0;
50
+ fallback = 1;
51
+ continue;
52
+ }
40
53
  if (a_val instanceof datetime_1.NesoiDatetime) {
41
54
  let d = 0;
42
55
  if (b_val instanceof datetime_1.NesoiDatetime)
@@ -90,7 +103,7 @@ class MemoryNQLRunner extends nql_engine_1.NQLRunner {
90
103
  }
91
104
  }
92
105
  }
93
- return 0;
106
+ return fallback;
94
107
  });
95
108
  }
96
109
  let totalItems = undefined;
@@ -0,0 +1,46 @@
1
+ import { BucketAdapter, BucketAdapterConfig } from './bucket_adapter';
2
+ import { ObjWithOptionalId } from "../../../../engine/data/obj";
3
+ import { AnyTrxNode } from "../../../../engine/transaction/trx_node";
4
+ import { $Bucket } from "../../..";
5
+ import { BucketCacheSync } from '../cache/bucket_cache';
6
+ export type RESTBucketAdapterConfig = {
7
+ base_url: string;
8
+ auth?: string;
9
+ };
10
+ /**
11
+ * @category Adapters
12
+ * @subcategory Entity
13
+ * */
14
+ export declare class RESTBucketAdapter<B extends $Bucket, Obj extends B['#data']> extends BucketAdapter<Obj, RESTBucketAdapterConfig & BucketAdapterConfig> {
15
+ schema: B;
16
+ constructor(schema: B, config?: RESTBucketAdapterConfig);
17
+ getQueryMeta(): {
18
+ scope: string;
19
+ avgTime: number;
20
+ };
21
+ fetch(trx: AnyTrxNode, url: string, options?: RequestInit): Promise<{
22
+ data: Obj | Obj[];
23
+ }>;
24
+ protected deleteEverything(trx: AnyTrxNode): Promise<void>;
25
+ index(trx: AnyTrxNode): Promise<Obj[]>;
26
+ get(trx: AnyTrxNode, id: Obj['id']): Promise<Obj | undefined>;
27
+ create(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
28
+ createMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
29
+ replace(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
30
+ replaceMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
31
+ patch(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
32
+ patchMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
33
+ put(trx: AnyTrxNode, obj: ObjWithOptionalId<Obj>): Promise<Obj>;
34
+ putMany(trx: AnyTrxNode, objs: ObjWithOptionalId<Obj>[]): Promise<Obj[]>;
35
+ delete(trx: AnyTrxNode, id: Obj['id']): Promise<void>;
36
+ deleteMany(trx: AnyTrxNode, ids: Obj['id'][]): Promise<void>;
37
+ syncOne(trx: AnyTrxNode, id: Obj['id'], lastObjUpdateEpoch: number): Promise<null | 'deleted' | BucketCacheSync<Obj>>;
38
+ syncOneAndPast(trx: AnyTrxNode, id: Obj['id'], lastUpdateEpoch: number): Promise<null | 'deleted' | BucketCacheSync<Obj>[]>;
39
+ syncAll(trx: AnyTrxNode, lastHash?: string, lastUpdateEpoch?: number): Promise<null | {
40
+ sync: BucketCacheSync<Obj>[];
41
+ hash: string;
42
+ updateEpoch: number;
43
+ reset: boolean;
44
+ }>;
45
+ }
46
+ export type AnyRESTBucketAdapter = RESTBucketAdapter<any, any>;
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESTBucketAdapter = void 0;
4
+ const bucket_adapter_1 = require("./bucket_adapter");
5
+ const rest_nql_1 = require("./rest.nql");
6
+ const log_1 = require("../../../../engine/util/log");
7
+ /**
8
+ * @category Adapters
9
+ * @subcategory Entity
10
+ * */
11
+ class RESTBucketAdapter extends bucket_adapter_1.BucketAdapter {
12
+ schema;
13
+ constructor(schema, config) {
14
+ const nql = new rest_nql_1.RESTNQLRunner();
15
+ super(schema, nql, config);
16
+ this.schema = schema;
17
+ nql.bind(this);
18
+ }
19
+ getQueryMeta() {
20
+ return {
21
+ scope: `rest.${this.schema.name}`,
22
+ avgTime: 10
23
+ };
24
+ }
25
+ /*
26
+ Fetch
27
+ */
28
+ async fetch(trx, url, options) {
29
+ const token = this.config.auth
30
+ ? await trx.token(this.config.auth)
31
+ : undefined;
32
+ const input = this.config.base_url + url;
33
+ return fetch(input, {
34
+ ...options,
35
+ headers: {
36
+ ...options?.headers,
37
+ Authorization: token ? `Bearer ${token}` : undefined,
38
+ }
39
+ })
40
+ .then(async (response) => ({
41
+ status: response.status,
42
+ headers: response.headers,
43
+ body: await response.json().catch(() => null)
44
+ }))
45
+ .then((response) => {
46
+ // if (response.status === 401) {
47
+ // // ...
48
+ // }
49
+ return response.body;
50
+ })
51
+ .catch((e) => {
52
+ log_1.Log.error('rest', '', e, e);
53
+ // ...
54
+ throw e;
55
+ });
56
+ }
57
+ /* Dangerous, used on cache only */
58
+ async deleteEverything(trx) {
59
+ throw new Error('Not implemented');
60
+ }
61
+ /* Read operations */
62
+ async index(trx) {
63
+ const res = await this.fetch(trx, '/', {
64
+ method: 'GET'
65
+ });
66
+ return res.data;
67
+ }
68
+ async get(trx, id) {
69
+ const res = await this.fetch(trx, '/' + id, {
70
+ method: 'GET'
71
+ });
72
+ return res.data;
73
+ }
74
+ /* Write Operations */
75
+ async create(trx, obj) {
76
+ const res = await this.fetch(trx, '/', {
77
+ method: 'POST',
78
+ body: JSON.stringify(obj)
79
+ });
80
+ return res.data;
81
+ }
82
+ async createMany(trx, objs) {
83
+ const out = [];
84
+ for (const obj of objs) {
85
+ out.push(await this.create(trx, obj));
86
+ }
87
+ return out;
88
+ }
89
+ async replace(trx, obj) {
90
+ if (!obj.id) {
91
+ throw new Error(`Object with id ${obj.id} not found for replace`);
92
+ }
93
+ const host_obj = await this.get(trx, obj.id);
94
+ if (!host_obj) {
95
+ throw new Error(`Object with id ${obj.id} not found for replace`);
96
+ }
97
+ const res = await this.fetch(trx, '/' + obj.id, {
98
+ method: 'PUT',
99
+ body: JSON.stringify(obj)
100
+ });
101
+ return res.data;
102
+ }
103
+ async replaceMany(trx, objs) {
104
+ const out = [];
105
+ for (const obj of objs) {
106
+ const output = await this.replace(trx, obj);
107
+ out.push(output);
108
+ }
109
+ return Promise.resolve(out);
110
+ }
111
+ async patch(trx, obj) {
112
+ const res = await this.fetch(trx, '/' + obj.id, {
113
+ method: 'PATCH',
114
+ body: JSON.stringify(obj)
115
+ });
116
+ return res.data;
117
+ }
118
+ async patchMany(trx, objs) {
119
+ const out = [];
120
+ for (const obj of objs) {
121
+ const output = await this.patch(trx, obj);
122
+ out.push(output);
123
+ }
124
+ return Promise.resolve(out);
125
+ }
126
+ async put(trx, obj) {
127
+ const res = await this.fetch(trx, '/' + obj.id, {
128
+ method: 'PUT',
129
+ body: JSON.stringify(obj)
130
+ });
131
+ return res.data;
132
+ }
133
+ async putMany(trx, objs) {
134
+ const out = [];
135
+ for (const obj of objs) {
136
+ const output = await this.put(trx, obj);
137
+ out.push(output);
138
+ }
139
+ return Promise.resolve(out);
140
+ }
141
+ async delete(trx, id) {
142
+ await this.fetch(trx, '/' + id, {
143
+ method: 'DELETE'
144
+ });
145
+ }
146
+ async deleteMany(trx, ids) {
147
+ for (const id of ids) {
148
+ await this.delete(trx, id);
149
+ }
150
+ }
151
+ /* Cache Operations */
152
+ async syncOne(trx, id, lastObjUpdateEpoch) {
153
+ // TODO
154
+ return null;
155
+ // // 1. Check if object was deleted
156
+ // const obj = await this.get(trx, id);
157
+ // if (!obj) {
158
+ // return 'deleted' as const;
159
+ // }
160
+ // // 2. Check if object was updated
161
+ // const updateEpoch = this.getUpdateEpoch(obj);
162
+ // const hasObjUpdated = updateEpoch > lastObjUpdateEpoch;
163
+ // if (!hasObjUpdated) {
164
+ // return null;
165
+ // }
166
+ // // 3. Return updated object and epoch
167
+ // return {
168
+ // obj,
169
+ // updateEpoch
170
+ // };
171
+ }
172
+ async syncOneAndPast(trx, id, lastUpdateEpoch) {
173
+ // TODO
174
+ return null;
175
+ // // 1. Check if object was deleted
176
+ // const obj = await this.get(trx, id);
177
+ // if (!obj) {
178
+ // return 'deleted' as const;
179
+ // }
180
+ // // 2. Check if object was updated
181
+ // const objUpdateEpoch = this.getUpdateEpoch(obj);
182
+ // const hasObjUpdated = objUpdateEpoch > lastUpdateEpoch;
183
+ // if (!hasObjUpdated) {
184
+ // return null;
185
+ // }
186
+ // // 3. Return all objects updated and the max epoch
187
+ // let updateEpoch = 0;
188
+ // const changed = (Object.values(this.data) as Obj[])
189
+ // .map(obj => {
190
+ // const epoch = this.getUpdateEpoch(obj);
191
+ // if (epoch > updateEpoch) {
192
+ // updateEpoch = epoch;
193
+ // }
194
+ // return { obj, updateEpoch: epoch };
195
+ // })
196
+ // .filter(obj => obj.updateEpoch > lastUpdateEpoch);
197
+ // if (!changed.length) {
198
+ // return null;
199
+ // }
200
+ // return changed;
201
+ }
202
+ async syncAll(trx, lastHash, lastUpdateEpoch = 0) {
203
+ // TODO
204
+ return null;
205
+ // // 1. Hash the current ids
206
+ // const idStr = Object.keys(this.data).sort().join('');
207
+ // const hash = Hash.string(idStr);
208
+ // // 2. If hash changed, return a reset sync with all objects
209
+ // if (hash !== lastHash) {
210
+ // let updateEpoch = 0;
211
+ // const sync = (Object.values(this.data) as Obj[])
212
+ // .map(obj => {
213
+ // const epoch = this.getUpdateEpoch(obj);
214
+ // if (epoch > updateEpoch) {
215
+ // updateEpoch = epoch;
216
+ // }
217
+ // return { obj, updateEpoch: epoch };
218
+ // });
219
+ // return {
220
+ // sync,
221
+ // hash,
222
+ // updateEpoch,
223
+ // reset: true
224
+ // };
225
+ // }
226
+ // // 3. Find the data that changed and return it
227
+ // let updateEpoch = 0;
228
+ // const sync = (Object.values(this.data) as Obj[])
229
+ // .map(obj => {
230
+ // const epoch = this.getUpdateEpoch(obj);
231
+ // if (epoch > updateEpoch) {
232
+ // updateEpoch = epoch;
233
+ // }
234
+ // return { obj, updateEpoch: epoch };
235
+ // })
236
+ // .filter(obj => obj.updateEpoch > lastUpdateEpoch);
237
+ // if (!sync.length) {
238
+ // return null;
239
+ // }
240
+ // return {
241
+ // sync,
242
+ // hash,
243
+ // updateEpoch,
244
+ // reset: false
245
+ // };
246
+ }
247
+ }
248
+ exports.RESTBucketAdapter = RESTBucketAdapter;
@@ -0,0 +1,15 @@
1
+ import { NQL_Result, NQLRunner } from '../query/nql_engine';
2
+ import { AnyTrxNode } from "../../../../engine/transaction/trx_node";
3
+ import { NQL_Pagination, NQL_Part } from '../query/nql.schema';
4
+ import { AnyRESTBucketAdapter } from './rest.bucket_adapter';
5
+ type Obj = Record<string, any>;
6
+ /**
7
+ * @category NQL
8
+ * */
9
+ export declare class RESTNQLRunner extends NQLRunner {
10
+ protected adapter?: AnyRESTBucketAdapter;
11
+ constructor();
12
+ bind(adapter: AnyRESTBucketAdapter): void;
13
+ run(trx: AnyTrxNode, part: NQL_Part, params: Obj[], param_templates: Record<string, string>[], pagination?: NQL_Pagination): Promise<NQL_Result>;
14
+ }
15
+ export {};
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESTNQLRunner = void 0;
4
+ const nql_engine_1 = require("../query/nql_engine");
5
+ const nql_compiler_1 = require("../query/nql_compiler");
6
+ /**
7
+ * @category NQL
8
+ * */
9
+ class RESTNQLRunner extends nql_engine_1.NQLRunner {
10
+ adapter;
11
+ constructor() {
12
+ super();
13
+ }
14
+ bind(adapter) {
15
+ this.adapter = adapter;
16
+ }
17
+ async run(trx, part, params, param_templates, pagination) {
18
+ if (!this.adapter) {
19
+ throw new Error('No adapter bound to NQL Runner');
20
+ }
21
+ const query = nql_compiler_1.NQL_Decompiler.decompile(part, params, param_templates);
22
+ const res = await this.adapter.fetch(trx, '/query', {
23
+ method: 'POST',
24
+ body: JSON.stringify({
25
+ query,
26
+ page: pagination?.page,
27
+ perPage: pagination?.perPage,
28
+ })
29
+ });
30
+ return res.data;
31
+ }
32
+ }
33
+ exports.RESTNQLRunner = RESTNQLRunner;
@@ -18,16 +18,16 @@ export type BucketConfig<M extends $Module, B extends $Bucket, Services extends
18
18
  * - **past**: Update/delete the object and update all objects modified before it, then return
19
19
  * - **all**: Update/reset the cache, then return
20
20
  */
21
- get?: 'one' | 'past' | 'all';
21
+ get?: 'eager' | 'one' | 'past' | 'all';
22
22
  /** Cache mode for `index`:
23
23
  * - **all**: Update/reset the cache, then return
24
24
  */
25
- index?: 'all';
25
+ index?: 'eager' | 'all';
26
26
  /** Cache mode for `query`:
27
27
  * - **incremental**: Query ids only, then query data for modified entries only, save them and return
28
28
  * - **all**: Update/reset the cache, then query the inner adapter and return
29
29
  */
30
- query?: 'incremental' | 'all';
30
+ query?: 'eager' | 'incremental' | 'all';
31
31
  };
32
32
  };
33
33
  };
@@ -13,6 +13,7 @@ const tree_1 = require("../../../engine/data/tree");
13
13
  const crypto_1 = require("../../../engine/util/crypto");
14
14
  const bucket_model_schema_1 = require("./model/bucket_model.schema");
15
15
  const trash_1 = require("../../../engine/data/trash");
16
+ const trx_1 = require("../../../engine/transaction/trx");
16
17
  /**
17
18
  * **This should only be used inside a `#composition` of a bucket `create`** to refer to the parent id, which doesn't exist yet.
18
19
  *
@@ -88,7 +89,7 @@ class Bucket {
88
89
  : this.getTenancyQuery(trx);
89
90
  let raw;
90
91
  // With Tenancy
91
- const adapter = this.cache || this.adapter;
92
+ const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
92
93
  if (tenancy) {
93
94
  const result = await adapter.query(trx, {
94
95
  id,
@@ -127,9 +128,9 @@ class Bucket {
127
128
  : this.getTenancyQuery(trx);
128
129
  let raws;
129
130
  // With Tenancy
130
- const adapter = this.cache || this.adapter;
131
+ const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
131
132
  if (tenancy) {
132
- const result = await adapter.query(trx, tenancy, undefined, options?.query_view ? [{ view: options?.query_view }] : undefined);
133
+ const result = await adapter.query(trx, tenancy, undefined, undefined, undefined, options?.query_view ? { view: options?.query_view } : undefined);
133
134
  raws = result.data;
134
135
  }
135
136
  // Without Tenancy
@@ -432,7 +433,7 @@ class Bucket {
432
433
  // Read old object, if safe, to check if it exists
433
434
  let oldObj;
434
435
  if (!options?.unsafe) {
435
- const adapter = this.cache || this.adapter;
436
+ const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
436
437
  // With Tenancy
437
438
  if (tenancy) {
438
439
  const result = await adapter.query(trx, {
@@ -620,7 +621,7 @@ class Bucket {
620
621
  await trx.bucket(link.bucket.short).unsafe.deleteMany(linked.map((l) => l.id));
621
622
  }
622
623
  else {
623
- await trx.bucket(link.bucket.short).delete(linked.id);
624
+ await trx.bucket(link.bucket.short).unsafe.delete(linked.id);
624
625
  }
625
626
  }
626
627
  // Delete the object itself
@@ -727,7 +728,7 @@ class Bucket {
727
728
  pagination.perPage = undefined;
728
729
  }
729
730
  // Query
730
- const adapter = this.cache || this.adapter;
731
+ const adapter = await trx_1.Trx.getCache(trx, this) || this.cache || this.adapter;
731
732
  const result = await adapter.query(trx, query, pagination, options?.params, options?.param_templates);
732
733
  if (!result.data.length)
733
734
  return result;
@@ -32,17 +32,50 @@ export declare class BucketCache<Obj extends NesoiObj> {
32
32
  private outerAdapter;
33
33
  constructor(bucket: AnyBucket, config: NonNullable<BucketConfig<any, any, any>['cache']>);
34
34
  get(trx: AnyTrxNode, id: NesoiObj['id']): Promise<any>;
35
- index(trx: AnyTrxNode): Promise<any[]>;
36
- query<MetadataOnly extends boolean>(trx: AnyTrxNode, query: NQL_AnyQuery, pagination?: NQL_Pagination, params?: Record<string, any>[], config?: {
35
+ index(trx: AnyTrxNode): Promise<{
36
+ id: Obj["id"];
37
+ }[]>;
38
+ query<MetadataOnly extends boolean>(trx: AnyTrxNode, query: NQL_AnyQuery, pagination?: NQL_Pagination, params?: Record<string, any>[], param_templates?: Record<string, string>[], config?: {
37
39
  view?: string;
38
40
  metadataOnly?: MetadataOnly;
39
41
  }): Promise<NQL_Result<MetadataOnly extends true ? {
40
42
  id: Obj['id'];
41
43
  [x: string]: any;
42
44
  } : Obj>>;
45
+ /**
46
+ * Update inner adapter with data from outer adapter.
47
+ */
48
+ sync(trx: AnyTrxNode): Promise<void>;
49
+ /**
50
+ * If doesnt exist on inner adapter, read.
51
+ * If it exists, check if it's updated (query metadata).
52
+ *
53
+ * [get.one]
54
+ * - reduces transit payload for data that doesn't change much
55
+ */
43
56
  private syncOne;
57
+ /**
58
+ * If doesnt exist on inner adapter, read.
59
+ * If it exists, read past.
60
+ *
61
+ * [get.past]
62
+ * - reduces transit payload for data that changes a little
63
+ */
44
64
  private syncOneAndPast;
65
+ /**
66
+ * Compare ids hash with outer. If it matches, read updated data.
67
+ * If not, hard resync.
68
+ *
69
+ * [get.all, index.all, query.all]
70
+ * - reduces transit payload for data that's not often deleted
71
+ */
45
72
  private syncAll;
73
+ /**
74
+ * Query metadata from outer, then query data from outer - only newer than inner data.
75
+ *
76
+ * [query.incremental]
77
+ * - reduces transit payload for data that doesn't change much
78
+ */
46
79
  private syncQuery;
47
80
  }
48
81
  export type AnyBucketCache = BucketCache<any>;
@@ -47,6 +47,14 @@ class BucketCache {
47
47
  }
48
48
  async get(trx, id) {
49
49
  const mode = this.config?.mode?.get;
50
+ if (mode === 'eager') {
51
+ log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE get.eager, ${id}`);
52
+ const sync = await this.innerAdapter.get(trx, id);
53
+ if (!sync)
54
+ return undefined;
55
+ const { __update_epoch, __sync_epoch, ...obj } = sync;
56
+ return obj;
57
+ }
50
58
  if (mode === 'one') {
51
59
  const { action, sync } = await this.syncOne(trx, id);
52
60
  log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE get.one, ${action}`);
@@ -76,21 +84,33 @@ class BucketCache {
76
84
  }
77
85
  async index(trx) {
78
86
  const mode = this.config?.mode?.index;
79
- if (mode === 'all') {
87
+ let data;
88
+ if (mode === 'eager') {
89
+ log_1.Log.debug('bucket', this.bucket.schema.name, 'CACHE index.eager');
90
+ data = await this.innerAdapter.index(trx);
91
+ }
92
+ else if (mode === 'all') {
80
93
  const { action, sync } = await this.syncAll(trx);
81
94
  log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE index.all, ${action}`);
82
- const data = [];
83
- for (const e of sync) {
84
- const { __update_epoch, __sync_epoch, ...obj } = e;
85
- data.push(obj);
86
- }
87
- return data;
95
+ data = sync;
96
+ }
97
+ else {
98
+ throw new Error(`Invalid index cache mode '${mode}'`);
88
99
  }
89
- return this.outerAdapter.index(trx);
100
+ const out = [];
101
+ for (const e of data) {
102
+ const { __update_epoch, __sync_epoch, ...obj } = e;
103
+ out.push(obj);
104
+ }
105
+ return out;
90
106
  }
91
- async query(trx, query, pagination, params, config) {
107
+ async query(trx, query, pagination, params, param_templates, config) {
92
108
  const mode = this.config?.mode?.query;
93
109
  let data;
110
+ if (mode === 'eager') {
111
+ log_1.Log.debug('bucket', this.bucket.schema.name, 'CACHE index.eager');
112
+ data = await this.innerAdapter.query(trx, query, pagination, params, param_templates, config);
113
+ }
94
114
  if (mode === 'incremental') {
95
115
  const { action, sync } = await this.syncQuery(trx, query, pagination, params);
96
116
  log_1.Log.debug('bucket', this.bucket.schema.name, `CACHE query.incremental, ${action}`);
@@ -125,6 +145,22 @@ class BucketCache {
125
145
  return { data };
126
146
  }
127
147
  /* Cache modes */
148
+ /**
149
+ * Update inner adapter with data from outer adapter.
150
+ */
151
+ async sync(trx) {
152
+ const objects = await this.outerAdapter.index(trx);
153
+ const entries = objects.map(obj => new BucketCacheEntry(obj, this.outerAdapter.getUpdateEpoch(obj), this.lastSyncEpoch));
154
+ await this.innerAdapter.deleteEverything(trx);
155
+ await this.innerAdapter.putMany(trx, entries);
156
+ }
157
+ /**
158
+ * If doesnt exist on inner adapter, read.
159
+ * If it exists, check if it's updated (query metadata).
160
+ *
161
+ * [get.one]
162
+ * - reduces transit payload for data that doesn't change much
163
+ */
128
164
  async syncOne(trx, id) {
129
165
  let localObj = await this.innerAdapter.get(trx, id);
130
166
  if (!localObj) {
@@ -148,6 +184,13 @@ class BucketCache {
148
184
  await this.innerAdapter.put(trx, localObj);
149
185
  return { action: 'update', sync: localObj };
150
186
  }
187
+ /**
188
+ * If doesnt exist on inner adapter, read.
189
+ * If it exists, read past.
190
+ *
191
+ * [get.past]
192
+ * - reduces transit payload for data that changes a little
193
+ */
151
194
  async syncOneAndPast(trx, id) {
152
195
  const localObj = await this.innerAdapter.get(trx, id);
153
196
  if (!localObj) {
@@ -170,6 +213,13 @@ class BucketCache {
170
213
  await this.innerAdapter.putMany(trx, entries);
171
214
  return { action: 'update', sync: entries.find(e => e.id === id) };
172
215
  }
216
+ /**
217
+ * Compare ids hash with outer. If it matches, read updated data.
218
+ * If not, hard resync.
219
+ *
220
+ * [get.all, index.all, query.all]
221
+ * - reduces transit payload for data that's not often deleted
222
+ */
173
223
  async syncAll(trx) {
174
224
  const sync = await this.outerAdapter.syncAll(trx, this.lastHash, this.lastUpdateEpoch);
175
225
  if (sync === null) {
@@ -188,6 +238,12 @@ class BucketCache {
188
238
  await this.innerAdapter.putMany(trx, entries);
189
239
  return { action: 'update', sync: entries };
190
240
  }
241
+ /**
242
+ * Query metadata from outer, then query data from outer - only newer than inner data.
243
+ *
244
+ * [query.incremental]
245
+ * - reduces transit payload for data that doesn't change much
246
+ */
191
247
  async syncQuery(trx, query, pagination, params) {
192
248
  // 1. Query id and epoch from outer adapter
193
249
  const outerMetadata = await this.outerAdapter.query(trx, query, pagination, params, undefined, {