beanbagdb 0.6.3 → 0.6.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beanbagdb",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "A JS library to introduce a schema layer to a No-SQL local database",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
package/src/index.js CHANGED
@@ -407,7 +407,7 @@ export class BeanBagDB {
407
407
  throw new DocCreationError(`${v_errors.join(",")}.`)
408
408
  }
409
409
 
410
- if(input.schema=="setting_edge"){throw new DocCreationError("This type of record can only be created through the create_edge api")}
410
+ // if(input.schema=="system_edge"){throw new DocCreationError("This type of record can only be created through the create_edge api")}
411
411
  if(Object.keys(input.data).length==0){throw new DocCreationError(`No data provided`)}
412
412
  try {
413
413
  let doc_obj = await this._insert_pre_checks(input.schema, input.data,input?.meta||{}, input?.settings||{});
@@ -564,6 +564,10 @@ export class BeanBagDB {
564
564
  // todo : what if additionalField are allowed ??
565
565
  let updated_data = { ...full_doc.data, ...allowed_updates };
566
566
 
567
+ if(full_doc.schema=="system_edge"){
568
+ // extra checks required. if everything is correct
569
+ updated_data = await this._create_edge(updated_data)
570
+ }
567
571
  updated_data = this.util_validate_data({schema:schema.schema,data: updated_data});
568
572
 
569
573
  // primary key check if multiple records can be created
@@ -622,8 +626,6 @@ export class BeanBagDB {
622
626
  }
623
627
 
624
628
 
625
-
626
-
627
629
  /**
628
630
  * Deletes a document from the database by its ID.
629
631
  *
@@ -821,22 +823,32 @@ export class BeanBagDB {
821
823
  * @param {object} node1
822
824
  * @param {object} node2
823
825
  * @param {string} edge_name
824
- * @param {*} edge_label
826
+ * @param {string} note
825
827
  * @returns {Object}
826
828
  */
827
- async create_edge(input){
828
- const {node1,node2,edge_name,edge_label=""} = input
829
+ async _create_edge(input){
830
+ console.log(input)
831
+ let {node1,node2,edge_name,note=""} = input
829
832
  this._check_ready_to_use();
830
833
  if(!edge_name){throw new ValidationError("edge_name required")}
831
- if(Object.keys(node1)==0){throw new ValidationError("node1 required")}
832
- if(Object.keys(node2)==0){throw new ValidationError("node2 required")}
833
-
834
+ if(!node1|| Object.keys(node1).length==0){throw new ValidationError("node1 required")}
835
+ if(!node2|| Object.keys(node2).length==0){throw new ValidationError("node2 required")}
836
+ // if nodes are of type string, they are assumed to be ids since they are stored in the DB as ids
837
+ if(typeof(node1)=="string"){node1 = {_id:node1}}
838
+ if(typeof(node2)=="string"){node2 = {_id:node2}}
834
839
  let n1 = await this.read(node1)
835
840
  let n2 = await this.read(node2)
841
+ if(n1.doc._id==n2.doc._id){
842
+ throw new ValidationError("Both nodes cannot be the same")
843
+ }
844
+ if(n1.doc.schema=="system_edge"|| n2.doc.schema=="system_edge"){
845
+ throw new ValidationError("A node cannot be an existing edge document")
846
+ }
836
847
  let edges_constraint
837
848
 
838
849
  try {
839
- let d = await this.read({schema:"setting_edge_constraint",data:{name:edge_name}})
850
+ // check if edge_name has a related edge_constraint
851
+ let d = await this.read({schema:"system_edge_constraint",data:{name:edge_name}})
840
852
  edges_constraint = d["doc"]["data"]
841
853
  let errors = []
842
854
  let node1id = n1.doc._id
@@ -850,7 +862,7 @@ async create_edge(input){
850
862
  node2id = n1.doc._id
851
863
  }
852
864
  }else{
853
- this.errors.push("Invalid nodes.This config of nodes not allowed")
865
+ errors.push("Invalid nodes.This config of nodes not allowed")
854
866
  }
855
867
 
856
868
  let records = await this.search({selector:{schema:"system_edge","data.edge_name":edge_name}})
@@ -870,18 +882,17 @@ async create_edge(input){
870
882
  }
871
883
 
872
884
  if(errors.length==0){
873
- let edge = await this.create({schema:"system_edge",data:{node1: node1id , node2: node1id ,edge_name:edge_name }})
874
- return edge
885
+ // let edge = await this.create({schema:"system_edge",data:})
886
+ return {node1: node1id , node2: node2id ,edge_name:edge_name ,note:note}
875
887
  }else{
876
888
  throw new RelationError(errors)
877
889
  }
878
890
 
879
891
  } catch (error) {
880
892
  if(error instanceof DocNotFoundError){
881
- let doc = {node1:"*",node2:"*",name:edge_name,label:edge_label}
893
+ let doc = {node1:"*",node2:"*",name:edge_name,note:note}
882
894
  let new_doc = await this.create({schema:"system_edge_constraint",data:doc})
883
- let edge = await this.create({schema:"system_edge",data:{node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name }})
884
- return edge
895
+ return {node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name ,note:note}
885
896
  }else{
886
897
  throw error
887
898
  }
@@ -1142,7 +1153,10 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
1142
1153
  // validate data
1143
1154
  if(!schemaDoc.active){throw new DocCreationError(`The schema "${schema}" is not active`)}
1144
1155
 
1145
- let new_data = this.util_validate_data({schema:schemaDoc.schema, data});
1156
+ let new_data
1157
+ if(schema!="system_edge"){
1158
+ new_data = this.util_validate_data({schema:schemaDoc.schema, data});
1159
+ }
1146
1160
 
1147
1161
  // validate meta
1148
1162
  if(Object.keys(meta).length>0){
@@ -1161,6 +1175,9 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
1161
1175
  if (schema == "schema") {
1162
1176
  //more checks are required
1163
1177
  this.util_validate_schema_object(new_data);
1178
+ }else if(schema == "system_edge"){
1179
+ let create_edge_after_checks = await this._create_edge(data)
1180
+ new_data = create_edge_after_checks
1164
1181
  }
1165
1182
  // @TODO : check if single record setting is set to true
1166
1183
  //console.log(schemaDoc)
@@ -11,7 +11,7 @@ export const default_app = {
11
11
  active: true,
12
12
  description: "Meta-schema or the schema for defining other schemas",
13
13
  system_generated: true,
14
- version: 0.88,
14
+ version: 1,
15
15
  title: "Schema document",
16
16
  schema: {
17
17
  type: "object",
@@ -146,7 +146,7 @@ export const default_app = {
146
146
  },
147
147
  {
148
148
  system_generated: true,
149
- version: 0.63,
149
+ version: 1,
150
150
  description:
151
151
  "To store user defined key. this can include anything like API tokens etc. There is a special method to fetch this. The values are encrypted",
152
152
  name: "system_key",
@@ -184,7 +184,7 @@ export const default_app = {
184
184
  },
185
185
  },
186
186
  {
187
- version: 0.67,
187
+ version: 1,
188
188
  system_generated: true,
189
189
  description:
190
190
  "The system relies on these settings for proper functioning or enabling optional features.",
@@ -218,13 +218,13 @@ export const default_app = {
218
218
  title: "Edge constraint",
219
219
  system_generated: true,
220
220
  active: true,
221
- version: 0.52,
221
+ version: 1,
222
222
  description:
223
223
  "To define edge constraints for simple directed graph of records.",
224
224
  schema: {
225
225
  type: "object",
226
226
  additionalProperties: true,
227
- required: ["node1", "node2", "edge_type"],
227
+ required: ["node1", "node2", "name"],
228
228
  properties: {
229
229
  node1: {
230
230
  type: "string",
@@ -275,12 +275,12 @@ export const default_app = {
275
275
  title: "Edge in the system graph",
276
276
  active: true,
277
277
  system_generated: true,
278
- version: 0.52,
278
+ version: 1,
279
279
  description: "To define edges in the simple directed graph of records.",
280
280
  schema: {
281
281
  type: "object",
282
282
  additionalProperties: true,
283
- required: ["node1", "node2", "edge_type"],
283
+ required: ["node1", "node2", "edge_name"],
284
284
  properties: {
285
285
  node1: {
286
286
  type: "string",
@@ -291,10 +291,14 @@ export const default_app = {
291
291
  edge_name: {
292
292
  type: "string",
293
293
  },
294
+ note:{
295
+ type:"string",
296
+ default:" "
297
+ }
294
298
  },
295
299
  },
296
300
  settings: {
297
- primary_keys: ["node1", "node2", "edge_type"],
301
+ primary_keys: ["node1", "node2", "edge_name"],
298
302
  non_editable_fields: ["edge_type"],
299
303
  encrypted_fields: [],
300
304
  },
@@ -304,7 +308,7 @@ export const default_app = {
304
308
  title: "Media content",
305
309
  active: true,
306
310
  system_generated: true,
307
- version: 0.62,
311
+ version: 1,
308
312
  description: "To store images as Base64",
309
313
  schema: {
310
314
  type: "object",
@@ -336,7 +340,7 @@ export const default_app = {
336
340
  system_generated: true,
337
341
  title: "System log",
338
342
  active: true,
339
- version: 0.52,
343
+ version: 1,
340
344
  description: "To define edges in the simple directed graph of records.",
341
345
  schema: {
342
346
  type: "object",
@@ -369,7 +373,7 @@ export const default_app = {
369
373
  system_generated: true,
370
374
  title: "Executable script",
371
375
  active: true,
372
- version: 0.2,
376
+ version: 1,
373
377
  description: "To create scripts that implement some logic. Can run both on browser and client.",
374
378
  schema: {
375
379
  type: "object",
package/test/couchdb.js CHANGED
@@ -1,9 +1,9 @@
1
- // const crypto = require('crypto');
1
+ import {BeanBagDB} from '../src/index.js';
2
+ import crypto from 'crypto'
2
3
  // const SDB = require("beanbagdb")
3
4
 
4
5
  import Ajv from 'ajv';
5
6
  import nano from "nano";
6
- import BeanBagDB from '../src/index.js';
7
7
 
8
8
  export class BeanBagDB_CouchDB extends BeanBagDB {
9
9
  constructor(db_url,db_name,encryption_key){
@@ -41,30 +41,87 @@ export class BeanBagDB_CouchDB extends BeanBagDB {
41
41
  }
42
42
  },
43
43
  utils:{
44
- encrypt: async (text,encryptionKey)=>{
45
- const key = crypto.scryptSync(encryptionKey, 'salt', 32); // Derive a 256-bit key
46
- const iv = crypto.randomBytes(16); // Initialization vector
47
- const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
48
- let encrypted = cipher.update(text, 'utf8', 'hex');
49
- encrypted += cipher.final('hex');
50
- return iv.toString('hex') + ':' + encrypted; // Prepend the IV for later use
44
+ encrypt: async (text, encryptionKey) => {
45
+ const encoder = new TextEncoder();
46
+ const data = encoder.encode(text); // Encode the text into bytes
47
+
48
+ // Ensure the encryption key is of valid length (16, 24, or 32 bytes for AES-GCM)
49
+ const keyBytes = encoder.encode(encryptionKey);
50
+ if (
51
+ keyBytes.length !== 16 &&
52
+ keyBytes.length !== 24 &&
53
+ keyBytes.length !== 32
54
+ ) {
55
+ throw new Error("Encryption key must be 16, 24, or 32 bytes long.");
56
+ }
57
+
58
+ // Convert encryptionKey to CryptoKey
59
+ const key = await crypto.subtle.importKey(
60
+ "raw",
61
+ keyBytes,
62
+ { name: "AES-GCM" },
63
+ false,
64
+ ["encrypt"]
65
+ );
66
+
67
+ // Create a random initialization vector (IV)
68
+ const iv = crypto.getRandomValues(new Uint8Array(12)); // 12 bytes for AES-GCM
69
+
70
+ // Encrypt the data
71
+ const encrypted = await crypto.subtle.encrypt(
72
+ { name: "AES-GCM", iv: iv },
73
+ key,
74
+ data
75
+ );
76
+
77
+ // Convert encrypted data and IV to base64 for storage
78
+ const encryptedArray = new Uint8Array(encrypted);
79
+ const encryptedText = btoa(String.fromCharCode(...encryptedArray));
80
+ const ivText = btoa(String.fromCharCode(...iv));
81
+
82
+ return ivText + ":" + encryptedText; // Store IV and encrypted text together
51
83
  },
52
- decrypt : async (encryptedText, encryptionKey)=>{
53
- const key = crypto.scryptSync(encryptionKey, 'salt', 32); // Derive a 256-bit key
54
- const [iv, encrypted] = encryptedText.split(':').map(part => Buffer.from(part, 'hex'));
55
- const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
56
- let decrypted = decipher.update(encrypted, 'hex', 'utf8');
57
- decrypted += decipher.final('utf8');
58
- return decrypted;
84
+ decrypt: async (encryptedText, encryptionKey) => {
85
+ const [ivText, encryptedData] = encryptedText.split(":");
86
+
87
+ // Convert IV and encrypted data from base64 to byte arrays
88
+ const iv = Uint8Array.from(atob(ivText), (c) => c.charCodeAt(0));
89
+ const encryptedArray = Uint8Array.from(atob(encryptedData), (c) =>
90
+ c.charCodeAt(0)
91
+ );
92
+
93
+ const encoder = new TextEncoder();
94
+
95
+ // Convert encryptionKey to CryptoKey
96
+ const key = await crypto.subtle.importKey(
97
+ "raw",
98
+ encoder.encode(encryptionKey),
99
+ { name: "AES-GCM" },
100
+ false,
101
+ ["decrypt"]
102
+ );
103
+
104
+ // Decrypt the data
105
+ const decrypted = await crypto.subtle.decrypt(
106
+ { name: "AES-GCM", iv: iv },
107
+ key,
108
+ encryptedArray
109
+ );
110
+
111
+ // Convert decrypted data back to a string
112
+ const decoder = new TextDecoder();
113
+ return decoder.decode(decrypted);
59
114
  },
60
115
  ping : ()=>{
61
116
  // @TODO ping the database to check connectivity when class is ready to use
62
117
  },
63
118
  validate_schema: (schema_obj, data_obj)=>{
64
- const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
119
+ const ajv = new Ajv({code: {esm: true},strict:false,useDefaults:true}) // options can be passed, e.g. {allErrors: true}
120
+ const data_copy = {...data_obj}
65
121
  const validate = ajv.compile(schema_obj);
66
- const valid = validate(data_obj);
67
- return {valid,validate}
122
+ const valid = validate(data_copy);
123
+
124
+ return {valid,validate,data:data_copy}
68
125
  }
69
126
  }
70
127
  }