beanbagdb 0.5.45 → 0.5.50

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.
@@ -0,0 +1,38 @@
1
+ name: Generate and Deploy JSDoc
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - release
7
+ permissions:
8
+ contents: read
9
+ pages: write
10
+ id-token: write
11
+ jobs:
12
+ # Single deploy job since we're just deploying
13
+ deploy:
14
+ environment:
15
+ name: github-pages
16
+ url: ${{ steps.deployment.outputs.page_url }}
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v4
21
+ - name: Set up Node.js
22
+ uses: actions/setup-node@v3
23
+ with:
24
+ node-version: '18'
25
+ - name: Install dependencies
26
+ run: npm install
27
+ - name: Generate JSDoc
28
+ run: npx jsdoc -c jsdoc.json
29
+ - name: Setup Pages
30
+ uses: actions/configure-pages@v5
31
+ - name: Upload artifact
32
+ uses: actions/upload-pages-artifact@v3
33
+ with:
34
+ # Upload entire repository
35
+ path: './docs-static'
36
+ - name: Deploy to GitHub Pages
37
+ id: deployment
38
+ uses: actions/deploy-pages@v4
@@ -4,7 +4,7 @@ name: Release npm Package
4
4
  on:
5
5
  push:
6
6
  branches:
7
- - main
7
+ - release
8
8
 
9
9
  jobs:
10
10
  build:
@@ -30,8 +30,8 @@ jobs:
30
30
  run: npm install
31
31
 
32
32
  # Step 4: Run tests
33
- # - name: Run tests
34
- # run: npm test
33
+ - name: Run tests
34
+ run: npm test
35
35
 
36
36
  # Step 5: Publish to npm if tests pass
37
37
  - name: Publish to npm
@@ -0,0 +1,36 @@
1
+ name: Test code on master
2
+
3
+ # Trigger the workflow on push to the "main" branch
4
+ on:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+ id-token: write
15
+ steps:
16
+ # Step 1: Check out the code from the repository
17
+ - name: Checkout code
18
+ uses: actions/checkout@v4
19
+
20
+ # Step 2: Set up Node.js environment
21
+ - name: Setup Node.js
22
+ uses: actions/setup-node@v4
23
+ with:
24
+ node-version: '20.17.0'
25
+ cache: 'npm'
26
+ registry-url: 'https://registry.npmjs.org'
27
+
28
+ # Step 3: Install dependencies
29
+ - name: Install dependencies
30
+ run: npm install
31
+
32
+ # Step 4: Run tests
33
+ - name: Run tests
34
+ run: npm test
35
+
36
+
package/index.md ADDED
@@ -0,0 +1 @@
1
+ This is where i write the documentation
package/jsdoc.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "source": {
3
+ "include": ["src"],
4
+ "includePattern": ".+\\.js(doc|x)?$",
5
+ "excludePattern": "(^|\\/|\\\\)_"
6
+ },
7
+ "opts": {
8
+ "destination": "./docs-static",
9
+ "recurse": true,
10
+ "readme":"./index.md",
11
+ "tutorials": "./docs"
12
+ },
13
+ "plugins": [
14
+ "plugins/markdown"
15
+ ],
16
+ "templates": {
17
+ "default": {
18
+ "outputSourceFiles": false
19
+ }
20
+ }
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beanbagdb",
3
- "version": "0.5.45",
3
+ "version": "0.5.50",
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",
@@ -29,6 +29,7 @@
29
29
  "ajv": "^8.17.1",
30
30
  "chai": "^5.1.1",
31
31
  "dotenv": "^16.4.5",
32
+ "jsdoc": "^4.0.3",
32
33
  "mocha": "^10.7.3",
33
34
  "nano": "^10.1.4",
34
35
  "pouchdb": "^9.0.0",
package/src/index.js CHANGED
@@ -158,6 +158,39 @@ class BeanBagDB {
158
158
  }
159
159
  }
160
160
 
161
+ validate_schema_object(schema_doc){
162
+ let errors = []
163
+ if(!schema_doc["schema"]["type"]){
164
+ errors.push("Schema must have the field schema.'type' which can only be 'object' ")
165
+ }else{
166
+ if(schema_doc["schema"]["type"]!="object"){
167
+ errors.push("The schema.'type' value is invalid.Only 'object' allowed")
168
+ }
169
+ }
170
+ if(!schema_doc["schema"]["properties"]){
171
+ errors.push("The schema.'properties' object does not exists")
172
+ }else{
173
+ if(typeof(schema_doc["schema"]["properties"])!="object"){
174
+ errors.push("Invalid schema.properties. It must be an object and must have atleast one field inside.")
175
+ }
176
+ if(Object.keys(schema_doc["schema"]["properties"]).length==0){
177
+ errors.push("You must define at least one property")
178
+ }
179
+ }
180
+
181
+ if(!schema_doc["schema"]["additionalProperties"]){
182
+ errors.push("The schema.'additionalProperties' field is required")
183
+ }else{
184
+ if(typeof(schema_doc["schema"]["additionalProperties"])!="boolean"){
185
+ errors.push("Invalid schema.additionalProperties. It must be a boolean value")
186
+ }
187
+ }
188
+
189
+ if(errors.length>0){
190
+ throw new Error("Schema validation errors- "+errors.join(","))
191
+ }
192
+ }
193
+
161
194
  /**
162
195
  * Returns a document with the provided ID
163
196
  * @param {String} doc_id - the doc Id (not the primary key)
@@ -251,7 +284,7 @@ class BeanBagDB {
251
284
  * @param {Object} data e.g {"name":"","mobile":""...}
252
285
  * @param {Object} settings (optional)
253
286
  */
254
- async insert(schema, data, settings = {}) {
287
+ async insert(schema, data, meta= {},settings = {}) {
255
288
  try {
256
289
  let doc_obj = await this._insert_pre_checks(schema, data, settings);
257
290
  let new_rec = await this.db_api.insert(doc_obj);
@@ -370,6 +403,11 @@ class BeanBagDB {
370
403
 
371
404
  //////// Helper method ////////
372
405
 
406
+ _generate_random_link(){
407
+ const dictionary = ['rain', 'mars', 'banana', 'earth', 'kiwi', 'mercury', 'fuji', 'hurricane', 'matterhorn', 'snow', 'saturn', 'jupiter', 'peach', 'wind', 'pluto', 'apple', 'k2', 'storm', 'venus', 'denali', 'cloud', 'sunshine', 'mango', 'drizzle', 'pineapple', 'aconcagua', 'gasherbrum', 'apricot', 'neptune', 'fog', 'orange', 'blueberry', 'kilimanjaro', 'uranus', 'grape', 'storm', 'montblanc', 'lemon', 'chooyu', 'raspberry', 'cherry', 'thunder', 'vinson', 'breeze', 'elbrus', 'everest', 'parbat', 'makalu', 'nanga', 'kangchenjunga', 'lightning', 'cyclone', 'comet', 'asteroid', 'pomegranate', 'nectarine', 'clementine', 'strawberry', 'tornado', 'avalanche', 'andes', 'rockies', 'himalayas', 'pyrenees', 'carpathians', 'cascade', 'etna', 'vesuvius', 'volcano', 'tundra', 'whirlwind', 'iceberg', 'eclipse', 'zephyr', 'tropic', 'monsoon', 'aurora'];
408
+ return Array.from({ length: 4 }, () => dictionary[Math.floor(Math.random() * dictionary.length)]).join('-');
409
+ }
410
+
373
411
  _check_required_fields(requiredFields,obj){
374
412
  for (const field of requiredFields) {
375
413
  if (!obj[field]) {throw new Error(`${field} is required`);}
@@ -452,7 +490,8 @@ class BeanBagDB {
452
490
  meta: {
453
491
  createdOn: this._get_now_unix_timestamp(),
454
492
  tags: [],
455
- app :{}
493
+ app :{},
494
+ link : this._generate_random_link() // there is a link by default. overwrite this if user provided one but only before checking if it is unique
456
495
  },
457
496
  schema: schema_name,
458
497
  };
@@ -527,7 +566,7 @@ class BeanBagDB {
527
566
  * @param {Object} schema
528
567
  * @param {Object} data
529
568
  */
530
- async _insert_pre_checks(schema, data, settings = {}) {
569
+ async _insert_pre_checks(schema, data,meta={} ,settings = {}) {
531
570
  // schema search
532
571
  let sch_search = await this.search({
533
572
  selector: { schema: "schema", "data.name": schema },
@@ -539,8 +578,24 @@ class BeanBagDB {
539
578
  // validate data
540
579
  this.validate_data(schemaDoc.schema, data);
541
580
 
581
+ // validate meta
582
+ this.validate_data(sys_sch.editable_metadata_schema, meta);
583
+
584
+ // duplicate meta.link check
585
+ if(meta.link){
586
+ let link_search = await this.search({ selector: {"meta.link":meta.link} });
587
+ console.log(link_search);
588
+ if (link_search.docs.length > 0) {
589
+ throw new Error("This link already exists in the database");
590
+ }
591
+ }
592
+
542
593
  // special checks for special docs
543
594
  // @TODO : for schema dos: settings fields must be in schema field
595
+ if(schema=="schema"){
596
+ //more checks are required
597
+ this.validate_schema_object(data)
598
+ }
544
599
  // @TODO : check if single record setting is set to true
545
600
 
546
601
  // duplicate check
@@ -15,17 +15,20 @@ export const schema_schema = {
15
15
  minLength: 5,
16
16
  maxLength: 50,
17
17
  pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
18
+ description:"This is the name of the schema.It cannot be changed later"
18
19
  },
19
20
  description:{
20
21
  type:"string",
21
- minLength:1,
22
- maxLength:1000
22
+ minLength:0,
23
+ maxLength:1000,
24
+ description:"A small description of what data in this schema stores."
23
25
  },
24
26
  schema: {
25
27
  type: "object",
26
28
  additionalProperties: true,
27
29
  minProperties: 1,
28
30
  maxProperties: 50,
31
+ description:"This must be a valid JSON Schema which will be used to validate documents created with this schema.See this https://tour.json-schema.org/",
29
32
  },
30
33
  settings: {
31
34
  type: "object",
@@ -45,7 +48,7 @@ export const schema_schema = {
45
48
  items: {
46
49
  type: "string",
47
50
  },
48
- maxItems: 20,
51
+ maxItems: 50,
49
52
  },
50
53
  encrypted_fields: {
51
54
  type: "array",
@@ -53,7 +56,7 @@ export const schema_schema = {
53
56
  items: {
54
57
  type: "string",
55
58
  },
56
- maxItems: 10,
59
+ maxItems: 50,
57
60
  },
58
61
  single_record: {
59
62
  type: "boolean",
@@ -68,7 +71,7 @@ export const schema_schema = {
68
71
  },
69
72
  settings: {
70
73
  primary_key: ["name"],
71
- editable_fields: ["schema", "settings"],
74
+ editable_fields: ["schema", "settings","description"],
72
75
  },
73
76
  };
74
77
 
@@ -175,6 +178,12 @@ export const editable_metadata_schema = {
175
178
  items:{type:"string"},
176
179
  default:[],
177
180
  maxItems: 40,
181
+ },
182
+ link:{
183
+ type:"string",
184
+ minLength:2,
185
+ maxLength:2000,
186
+ pattern: "^[a-zA-Z0-9-]+$"
178
187
  }
179
188
  }
180
189
  }
package/test/init.test.js CHANGED
@@ -1,83 +1,13 @@
1
- // to test initialization of the BeanBagDB class. using in memory pouch db for testing to avoid additional setup.
2
- import PouchDB from 'pouchdb';
3
- import pouchdbFind from 'pouchdb-find';
4
- PouchDB.plugin(pouchdbFind)
5
- import { scryptSync, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
6
- const db_name = "test_database_24"
7
- const pdb = new PouchDB(db_name);
8
- import Ajv from 'ajv';
9
-
10
- const doc_obj = {
11
- name: db_name,
12
- encryption_key: "qwertyuiopaqwsde1254",
13
- api: {
14
- insert: async (doc) => {
15
- const result = await pdb.post(doc);
16
- return result;
17
- },
18
- // delete: ()=>{db1.destroy},
19
- update: async (doc) => {
20
- const result = await pdb.put(doc);
21
- return result;
22
- },
23
- search: async (query) => {
24
- const results = await pdb.find(query);
25
- return results; // of the form {docs:[],...}
26
- },
27
- get: async (id) => {
28
- const data = await pdb.get(id);
29
- return data;
30
- },
31
- delete: async (id) => {
32
- const doc = await pdb.get(id);
33
- const resp = await pdb.remove(doc);
34
- return resp;
35
- },
36
- createIndex: async (filter) => {
37
- const data = await pdb.createIndex(filter);
38
- return data;
39
- },
40
- },
41
- utils: {
42
- encrypt: (text, encryptionKey) => {
43
- const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
44
- const iv = randomBytes(16); // Initialization vector
45
- const cipher = createCipheriv("aes-256-cbc", key, iv);
46
- let encrypted = cipher.update(text, "utf8", "hex");
47
- encrypted += cipher.final("hex");
48
- return iv.toString("hex") + ":" + encrypted; // Prepend the IV for later use
49
- },
50
- decrypt: (encryptedText, encryptionKey) => {
51
- const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
52
- const [iv, encrypted] = encryptedText
53
- .split(":")
54
- .map((part) => Buffer.from(part, "hex"));
55
- const decipher = createDecipheriv("aes-256-cbc", key, iv);
56
- let decrypted = decipher.update(encrypted, "hex", "utf8");
57
- decrypted += decipher.final("utf8");
58
- return decrypted;
59
- },
60
- ping: () => {
61
- // @TODO ping the database to check connectivity when class is ready to use
62
- },
63
- validate_schema: (schema_obj, data_obj)=>{
64
- const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
65
- const validate = ajv.compile(schema_obj);
66
- const valid = validate(data_obj);
67
- return {valid,validate}
68
- }
69
- },
70
- }
71
-
72
- let the_correct_object = {};
73
-
1
+ // to test initialization of the BeanBagDB class
2
+ import { get_pdb_doc } from './pouchdb.js';
74
3
  import { throws, strictEqual } from "assert";
4
+ import BeanBagDB from '../src/index.js';
75
5
 
76
- import BeanBagDB from '../src/index.js';
77
6
  /**
78
7
  * Initial setup
79
8
  * database is the global var where the beanbag class is initialized
80
9
  */
10
+
81
11
  const test_set1 = [
82
12
  ["name", "sample"],
83
13
  ["encryption_key", "sample_key"],
@@ -210,6 +140,7 @@ describe("Successful database class init", async () => {
210
140
  });
211
141
 
212
142
  it("DB init successful", () => {
143
+ let doc_obj = get_pdb_doc("test_database_24","qwertyuiopaqwsde1254")
213
144
  database = new BeanBagDB(doc_obj);
214
145
  strictEqual(
215
146
  database instanceof BeanBagDB,
@@ -1 +1,145 @@
1
- // to test database operations. assuming the class is initialized successfully
1
+ // to test database operations. assuming the class is initialized successfully
2
+ // to test initialization of the BeanBagDB class
3
+ import { get_pdb_doc } from './pouchdb.js';
4
+ import { throws, strictEqual,rejects } from "assert";
5
+ import BeanBagDB from '../src/index.js';
6
+
7
+
8
+
9
+ let database // this is the global db object
10
+
11
+ describe("Successful database class init (required for further testing) ", async () => {
12
+ it("DB init successful", () => {
13
+ let doc_obj = get_pdb_doc("test_database_25","qwertyuiopaqwsde1254")
14
+ database = new BeanBagDB(doc_obj);
15
+ strictEqual(
16
+ database instanceof BeanBagDB,
17
+ true,
18
+ "The variable is initialized successfully"
19
+ );
20
+ });
21
+ });
22
+
23
+
24
+
25
+
26
+ describe("Testing creation of schema docs", async () => {
27
+ let schema_docs_invalid = [
28
+ {
29
+ name: "",
30
+ description: "",
31
+ schema: {},
32
+ settings: {},
33
+ },
34
+ {
35
+ name: "nos",
36
+ description: "",
37
+ schema: {},
38
+ settings: {},
39
+ },
40
+ {
41
+ name: "contact",
42
+ description: "",
43
+ schema: {},
44
+ settings: {},
45
+ },
46
+ {
47
+ name: "contact",
48
+ description: "This can be left blank",
49
+ schema: {},
50
+ settings: {},
51
+ },
52
+ {
53
+ name: "contact",
54
+ description: "This can be left blank",
55
+ schema: {
56
+ "abc":"something"
57
+ },
58
+ settings: {},
59
+ },
60
+ {
61
+ name: "contact",
62
+ description: "This can be left blank",
63
+ schema: {
64
+ "type":"something",
65
+ },
66
+ settings: {},
67
+ },
68
+ {
69
+ name: "contact",
70
+ description: "This can be left blank",
71
+ schema: {
72
+ "type":"object",
73
+ },
74
+ settings: {},
75
+ },
76
+ {
77
+ name: "contact",
78
+ description: "This can be left blank",
79
+ schema: {
80
+ "type":"object",
81
+ "properties":"something"
82
+ },
83
+ settings: {},
84
+ },
85
+ {
86
+ name: "contact",
87
+ description: "This can be left blank",
88
+ schema: {
89
+ "type":"object",
90
+ "properties":{}
91
+ },
92
+ settings: {},
93
+ },
94
+ {
95
+ name: "contact",
96
+ description: "This can be left blank",
97
+ schema: {
98
+ "type":"object",
99
+ "properties":{"name":{"type":"string"}}
100
+ },
101
+ settings: {},
102
+ },
103
+ {
104
+ name: "contact",
105
+ description: "This can be left blank",
106
+ schema: {
107
+ "type":"object",
108
+ "properties":{"name":{"type":"string"}},
109
+ "additionalProperties":"no"
110
+ },
111
+ settings: {},
112
+ },
113
+ // {
114
+ // name: "contact",
115
+ // description: "This can be left blank",
116
+ // schema: {
117
+ // "type":"object",
118
+ // "properties":{"name":{"type":"string"}},
119
+ // "additionalProperties":true
120
+ // },
121
+ // settings: {},
122
+ // },
123
+ ];
124
+
125
+ let doc_obj = get_pdb_doc("test_database_25", "qwertyuiopaqwsde1254");
126
+ let database = new BeanBagDB(doc_obj);
127
+ database.initialize_db();
128
+ database.ready();
129
+ // schema_docs_invalid.forEach(doc=>{})
130
+ for (let index = 0; index < schema_docs_invalid.length; index++) {
131
+ const element = schema_docs_invalid[index];
132
+
133
+ it("throws error", async () => {
134
+ await rejects(
135
+ async () => {
136
+ await database.insert("schema", element);
137
+ },
138
+ Error
139
+ );
140
+ });
141
+
142
+ }
143
+
144
+ });
145
+
@@ -0,0 +1,73 @@
1
+ // this is a pouch db instance of beanbagdb used for testing.
2
+ import Ajv from 'ajv';
3
+ import PouchDB from 'pouchdb';
4
+ import pouchdbFind from 'pouchdb-find';
5
+ PouchDB.plugin(pouchdbFind)
6
+ import { scryptSync, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
7
+
8
+
9
+ export const get_pdb_doc = (dbname,secret)=>{
10
+ const pdb = new PouchDB(dbname);
11
+ const doc_obj = {
12
+ name: dbname,
13
+ encryption_key: secret,
14
+ api: {
15
+ insert: async (doc) => {
16
+ const result = await pdb.post(doc);
17
+ return result;
18
+ },
19
+ // delete: ()=>{db1.destroy},
20
+ update: async (doc) => {
21
+ const result = await pdb.put(doc);
22
+ return result;
23
+ },
24
+ search: async (query) => {
25
+ const results = await pdb.find(query);
26
+ return results; // of the form {docs:[],...}
27
+ },
28
+ get: async (id) => {
29
+ const data = await pdb.get(id);
30
+ return data;
31
+ },
32
+ delete: async (id) => {
33
+ const doc = await pdb.get(id);
34
+ const resp = await pdb.remove(doc);
35
+ return resp;
36
+ },
37
+ createIndex: async (filter) => {
38
+ const data = await pdb.createIndex(filter);
39
+ return data;
40
+ },
41
+ },
42
+ utils: {
43
+ encrypt: (text, encryptionKey) => {
44
+ const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
45
+ const iv = randomBytes(16); // Initialization vector
46
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
47
+ let encrypted = cipher.update(text, "utf8", "hex");
48
+ encrypted += cipher.final("hex");
49
+ return iv.toString("hex") + ":" + encrypted; // Prepend the IV for later use
50
+ },
51
+ decrypt: (encryptedText, encryptionKey) => {
52
+ const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
53
+ const [iv, encrypted] = encryptedText
54
+ .split(":")
55
+ .map((part) => Buffer.from(part, "hex"));
56
+ const decipher = createDecipheriv("aes-256-cbc", key, iv);
57
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
58
+ decrypted += decipher.final("utf8");
59
+ return decrypted;
60
+ },
61
+ ping: () => {
62
+ // @TODO ping the database to check connectivity when class is ready to use
63
+ },
64
+ validate_schema: (schema_obj, data_obj)=>{
65
+ const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
66
+ const validate = ajv.compile(schema_obj);
67
+ const valid = validate(data_obj);
68
+ return {valid,validate}
69
+ }
70
+ },
71
+ }
72
+ return doc_obj
73
+ }
package/test/test1.js CHANGED
@@ -154,4 +154,25 @@
154
154
  // // let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
155
155
  // // await db.ready();
156
156
  // // db.load_plugin("sample",pl1)
157
- // // })();
157
+ // // })();
158
+
159
+ import { get_pdb_doc } from './pouchdb.js';
160
+ import { throws, strictEqual } from "assert";
161
+ import BeanBagDB from '../src/index.js';
162
+
163
+ (async()=>{
164
+ let schema_docs_invalid = [
165
+ {
166
+ "name":"",
167
+ "description":"",
168
+ "schema":{},
169
+ "settings":{}
170
+ }
171
+
172
+ ]
173
+ let doc_obj = get_pdb_doc("test_database_26","qwertyuiopaqwsde1254")
174
+ let database = new BeanBagDB(doc_obj);
175
+ database.initialize_db()
176
+ database.ready()
177
+ let a = await database.insert("schema",schema_docs_invalid[0])
178
+ })()