@cap-js-community/common 0.2.8 → 0.3.0

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## Version 0.3.0 - 2025-11-03
9
+
10
+ ### Fixed
11
+
12
+ - Refactor replication cache
13
+ - Change `@cap-js/sqlite` to dev dependency
14
+ - Replication cache requires `@cap-js/sqlite` as project dependency (no dev dependency)
15
+
8
16
  ## Version 0.2.8 - 2025-10-13
9
17
 
10
18
  ### Fixed
package/README.md CHANGED
@@ -30,6 +30,9 @@ Local replicated SQLite database can be queried with same query as the original
30
30
 
31
31
  ### Usage
32
32
 
33
+ > Replication cache uses SQLite as local database for productive usage.
34
+ > Ensure `@cap-js/sqlite` is installed as dependency (not as dev dependency) in your project.
35
+
33
36
  ```cds
34
37
  @cds.replicate
35
38
  entity Books {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js-community/common",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "CAP Node.js Community Common",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "engines": {
@@ -45,7 +45,6 @@
45
45
  "audit": "npm audit --only=prod"
46
46
  },
47
47
  "dependencies": {
48
- "@cap-js/sqlite": "^2.0.3",
49
48
  "commander": "^14.0.1",
50
49
  "redis": "^4.7.1",
51
50
  "verror": "^1.10.1"
@@ -53,6 +52,7 @@
53
52
  "devDependencies": {
54
53
  "@cap-js-community/common": "./",
55
54
  "@cap-js/cds-test": "^0.4.0",
55
+ "@cap-js/sqlite": "^2.0.3",
56
56
  "@sap/cds": "^9.4.3",
57
57
  "@sap/cds-common-content": "^3.0.1",
58
58
  "@sap/cds-dk": "^9.4.1",
@@ -5,8 +5,6 @@ const path = require("path");
5
5
  const fs = require("fs").promises;
6
6
 
7
7
  const cds = require("@sap/cds");
8
- const SQLiteService = require("@cap-js/sqlite");
9
-
10
8
  require("../common/promise");
11
9
 
12
10
  const Component = "replicationCache";
@@ -51,7 +49,7 @@ class ReplicationCache {
51
49
  this.group = this.options.group;
52
50
  this.log = cds.log(Component);
53
51
  this.template = null;
54
- this.cache = new Map();
52
+ this.entries = new Map();
55
53
  this.initStats();
56
54
  this.attach();
57
55
  }
@@ -64,6 +62,8 @@ class ReplicationCache {
64
62
  if (service.name === this.name) {
65
63
  const refs = ReplicationCache.replicationRefs(this.model, service, this.options.deploy);
66
64
  if (refs.length > 0) {
65
+ // @cap-js/sqlite dependency is required for replication cache
66
+ this.SQLiteService = require("@cap-js/sqlite");
67
67
  this.setup(service, refs);
68
68
  this.log.info("using replication cache", {
69
69
  service: service.name,
@@ -74,7 +74,7 @@ class ReplicationCache {
74
74
  this.options?.credentials?.database !== Constants.InMemory
75
75
  ) {
76
76
  this.log.info("Preparing replication cache template database");
77
- this.template = createDB(Tenant.Template, this.model, this.options)
77
+ this.template = createDB(this.SQLiteService, Tenant.Template, this.model, this.options)
78
78
  .then(() => {
79
79
  this.log.info("Prepared replication cache template database");
80
80
  })
@@ -220,7 +220,7 @@ class ReplicationCache {
220
220
  this.stats.used++;
221
221
  this.stats.ratio = Math.round(this.stats.used / this.stats.hits);
222
222
  this.log.debug("Replication cache was used");
223
- const db = this.cache.get(tenant).db;
223
+ const db = this.entries.get(tenant).db;
224
224
  if (this.options.measure) {
225
225
  return this.measure(
226
226
  async () => {
@@ -316,15 +316,15 @@ class ReplicationCache {
316
316
  if (refs.length === 0) {
317
317
  return;
318
318
  }
319
- let tenantCache = cached(this.cache, tenant, async () => {
320
- return new ReplicationCacheTenant(tenant, model, this.options).prepare();
319
+ let tenantCache = cached(this.entries, tenant, async () => {
320
+ return new ReplicationCacheTenant(this, tenant, model, this.options).prepare();
321
321
  });
322
322
  return (async () => {
323
323
  try {
324
324
  const prepared = Promise.resolve(tenantCache).then(async (tenantCache) => {
325
325
  const prepares = [];
326
326
  for (const ref of refs) {
327
- const entry = cached(tenantCache.cache, ref, () => {
327
+ const entry = cached(tenantCache.entries, ref, () => {
328
328
  return new ReplicationCacheEntry(this, tenantCache, ref);
329
329
  });
330
330
  entry.touched = Date.now();
@@ -346,7 +346,7 @@ class ReplicationCache {
346
346
  return Status.NotReady;
347
347
  }
348
348
  for (const ref of refs) {
349
- const entry = tenantCache.cache.get(ref);
349
+ const entry = tenantCache.entries.get(ref);
350
350
  if (!entry || entry.status !== Status.Ready) {
351
351
  return Status.NotReady;
352
352
  }
@@ -361,13 +361,13 @@ class ReplicationCache {
361
361
  }
362
362
 
363
363
  async prepared(tenant, ref) {
364
- const tenants = tenant ? [tenant] : this.cache.keys();
364
+ const tenants = tenant ? [tenant] : this.entries.keys();
365
365
  for (const id of tenants) {
366
- const tenant = await this.cache.get(id);
366
+ const tenant = await this.entries.get(id);
367
367
  if (tenant) {
368
- const refs = ref ? [ref] : tenant.cache.keys();
368
+ const refs = ref ? [ref] : tenant.entries.keys();
369
369
  for (const ref of refs) {
370
- const entry = tenant.cache.get(ref);
370
+ const entry = tenant.entries.get(ref);
371
371
  if (entry) {
372
372
  await entry.prepared;
373
373
  }
@@ -377,13 +377,13 @@ class ReplicationCache {
377
377
  }
378
378
 
379
379
  async clear(tenant, ref) {
380
- const tenants = tenant ? [tenant] : this.cache.keys();
380
+ const tenants = tenant ? [tenant] : this.entries.keys();
381
381
  for (const id of tenants) {
382
- const tenant = await this.cache.get(id);
382
+ const tenant = await this.entries.get(id);
383
383
  if (tenant) {
384
- const refs = ref ? [ref] : tenant.cache.keys();
384
+ const refs = ref ? [ref] : tenant.entries.keys();
385
385
  for (const ref of refs) {
386
- const entry = tenant.cache.get(ref);
386
+ const entry = tenant.entries.get(ref);
387
387
  if (entry) {
388
388
  await entry.clear();
389
389
  this.log.debug("Replication cache cleared", {
@@ -404,10 +404,10 @@ class ReplicationCache {
404
404
  }
405
405
 
406
406
  async prune(tenant) {
407
- const maxSize = this.options.size / this.cache.size;
408
- const tenants = tenant ? [tenant] : this.cache.keys();
407
+ const maxSize = this.options.size / this.entries.size;
408
+ const tenants = tenant ? [tenant] : this.entries.keys();
409
409
  for (const id of tenants) {
410
- const tenant = await this.cache.get(id);
410
+ const tenant = await this.entries.get(id);
411
411
  const size = await this.size(tenant.id);
412
412
  let diff = size - maxSize;
413
413
  if (diff > 0) {
@@ -415,12 +415,12 @@ class ReplicationCache {
415
415
  tenant,
416
416
  diff,
417
417
  });
418
- const refs = [...tenant.cache.keys()];
419
- refs.sort((ref1, ref2) => tenant.cache.get(ref1).touched - tenant.cache.get(ref2).touched);
418
+ const refs = [...tenant.entries.keys()];
419
+ refs.sort((ref1, ref2) => tenant.entries.get(ref1).touched - tenant.entries.get(ref2).touched);
420
420
  const pruneRefs = [];
421
421
  for (const ref of refs) {
422
422
  pruneRefs.push(ref);
423
- const entry = tenant.cache.get(ref);
423
+ const entry = tenant.entries.get(ref);
424
424
  if (entry) {
425
425
  diff -= entry.size;
426
426
  if (diff <= 0) {
@@ -429,7 +429,7 @@ class ReplicationCache {
429
429
  }
430
430
  }
431
431
  for (const ref of pruneRefs) {
432
- const entry = tenant.cache.get(ref);
432
+ const entry = tenant.entries.get(ref);
433
433
  this.log.debug("Replication cache prunes ref for tenant", {
434
434
  tenant,
435
435
  ref,
@@ -444,13 +444,13 @@ class ReplicationCache {
444
444
 
445
445
  async size(tenant, ref) {
446
446
  let size = 0;
447
- const tenants = tenant ? [tenant] : this.cache.keys();
447
+ const tenants = tenant ? [tenant] : this.entries.keys();
448
448
  for (const id of tenants) {
449
- const tenant = await this.cache.get(id);
449
+ const tenant = await this.entries.get(id);
450
450
  if (tenant) {
451
- const refs = ref ? [ref] : tenant.cache.keys();
451
+ const refs = ref ? [ref] : tenant.entries.keys();
452
452
  for (const ref of refs) {
453
- const entry = tenant.cache.get(ref);
453
+ const entry = tenant.entries.get(ref);
454
454
  if (entry) {
455
455
  size += entry.size;
456
456
  }
@@ -461,7 +461,7 @@ class ReplicationCache {
461
461
  }
462
462
 
463
463
  async tenantSize(id) {
464
- const tenant = await this.cache.get(id);
464
+ const tenant = await this.entries.get(id);
465
465
  if (tenant) {
466
466
  return await tenant.db.tx(async (tx) => {
467
467
  const result = await tx.run(
@@ -578,16 +578,17 @@ class ReplicationCache {
578
578
  }
579
579
 
580
580
  class ReplicationCacheTenant {
581
- constructor(tenant, model, options) {
581
+ constructor(cache, tenant, model, options) {
582
+ this.cache = cache;
582
583
  this.id = tenant;
583
584
  this.model = model;
584
585
  this.options = options;
585
586
  this.csn = model.definitions;
586
- this.cache = new Map();
587
+ this.entries = new Map();
587
588
  }
588
589
 
589
590
  async prepare() {
590
- this.db = await createDB(this.id, this.model, this.options);
591
+ this.db = await createDB(this.cache.SQLiteService, this.id, this.model, this.options);
591
592
  return this;
592
593
  }
593
594
  }
@@ -697,7 +698,7 @@ class ReplicationCacheEntry {
697
698
  async load(thread) {
698
699
  this.timestamp = Date.now();
699
700
  await this.clear();
700
- if (thread && cds.context && this.service instanceof SQLiteService) {
701
+ if (thread && cds.context && this.service instanceof this.cache.SQLiteService) {
701
702
  const srcTx = this.service.tx(cds.context);
702
703
  await this.db.tx({ tenant: this.tenant.id }, async (destTx) => {
703
704
  await this.loadRecords(srcTx, destTx);
@@ -803,7 +804,7 @@ class ReplicationCacheEntry {
803
804
 
804
805
  module.exports = ReplicationCache;
805
806
 
806
- async function createDB(tenant, model, options) {
807
+ async function createDB(DBService, tenant, model, options) {
807
808
  const filePath = await dbPath(tenant, options);
808
809
  cds.log(Component).debug("Preparing replication cache database", {
809
810
  tenant,
@@ -813,7 +814,7 @@ async function createDB(tenant, model, options) {
813
814
  const templateDatabase = await dbPath(Tenant.Template, options);
814
815
  await fs.copyFile(templateDatabase, filePath);
815
816
  }
816
- const db = new SQLiteService(tenant ?? Tenant.Default, model, {
817
+ const db = new DBService(tenant ?? Tenant.Default, model, {
817
818
  kind: "sqlite",
818
819
  impl: "@cap-js/sqlite",
819
820
  multiTenant: false,