beanbagdb 0.5.1 → 0.5.2

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,10 +1,14 @@
1
1
  {
2
2
  "name": "beanbagdb",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "A JS library to introduce a schema layer to a No-SQL local database",
5
- "main": "src/index.js",
5
+ "main": "dist/beanbagdb.cjs.js",
6
+ "module": "dist/beanbagdb.esm.js",
7
+ "type": "module",
6
8
  "scripts": {
7
- "test": "mocha"
9
+ "test": "mocha",
10
+ "build": "rollup -c",
11
+ "prepublishOnly": "npm run build"
8
12
  },
9
13
  "repository": {
10
14
  "type": "git",
@@ -32,7 +36,13 @@
32
36
  "pouchdb-find": "^9.0.0"
33
37
  },
34
38
  "devDependencies": {
39
+ "@rollup/plugin-commonjs": "^26.0.1",
40
+ "@rollup/plugin-json": "^6.1.0",
41
+ "@rollup/plugin-node-resolve": "^15.2.3",
42
+ "@rollup/plugin-replace": "^5.0.7",
43
+ "@rollup/plugin-terser": "^0.4.4",
35
44
  "chai": "^5.1.1",
36
- "mocha": "^10.7.3"
45
+ "mocha": "^10.7.3",
46
+ "rollup": "^4.21.2"
37
47
  }
38
48
  }
@@ -0,0 +1,47 @@
1
+ import resolve from '@rollup/plugin-node-resolve';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import json from '@rollup/plugin-json'; // JSON plugin to import package.json
4
+ import replace from '@rollup/plugin-replace';
5
+ import pkg from './package.json' assert { type: "json" }; // Import package.json directly
6
+
7
+ export default [
8
+ // For Node.js (CommonJS and ESM)
9
+ {
10
+ input: 'src/index.js',
11
+ output: [
12
+ {
13
+ file: pkg.main, // Points to the CJS build in package.json
14
+ format: 'cjs', // CommonJS format
15
+ sourcemap: true,
16
+ },
17
+ {
18
+ file: pkg.module, // Points to the ES module build in package.json
19
+ format: 'esm', // ES module format
20
+ sourcemap: true,
21
+ },
22
+ ],
23
+ plugins: [
24
+ resolve(), // Help Rollup find Node modules
25
+ commonjs(), // Convert CommonJS modules to ES6
26
+ json(), // Allow importing package.json without import assertion issues
27
+ ],
28
+ },
29
+ // For browsers (ESM with a version number injected)
30
+ {
31
+ input: 'src/index.js',
32
+ output: {
33
+ file: 'dist/beanbagdb.esm.js', // Browser-friendly ESM build
34
+ format: 'esm', // ES module format
35
+ sourcemap: true,
36
+ },
37
+ plugins: [
38
+ resolve(),
39
+ commonjs(), // Convert CommonJS modules to ES6
40
+ json(),
41
+ replace({
42
+ preventAssignment: true,
43
+ 'process.env.PACKAGE_VERSION': JSON.stringify(pkg.version), // Inject version from package.json
44
+ })
45
+ ],
46
+ },
47
+ ];
@@ -0,0 +1,580 @@
1
+ import Ajv from "ajv";
2
+ import * as sys_sch from "./system_schema.js";
3
+ // import { version } from "../package.json" assert {type :"json"};
4
+ import { getPackageVersion } from "./utils.js";
5
+ /**
6
+ * This the core class. it is not very useful in itself but can be used to generate a sub class for a specific database for eg CouchDB.
7
+ * It takes a db_instance argument, which , this class relies on perform CRUD operations on the data.
8
+ * Why have a "dumb" class ? : So that the core functionalities remains in a single place and the multiple Databases can be supported.
9
+ */
10
+ class BeanBagDB {
11
+ /**
12
+ * @param {object} db_instance - Database object
13
+ * db_instance object contains 3 main keys :
14
+ * - `name` : the name of the local database
15
+ * - `encryption_key`: this is required for encrypting documents
16
+ * - `api` : this is an object that must contain database specific functions. This includes : `insert(doc)`: takes a doc and runs the db insertion function, `update(updated_doc)` : gets the updated document and updates it in the DB, `search(query)`: takes a query to fetch data from the DB (assuming array of JSON is returned ), `get(id)`: takes a document id and returns its content, `createIndex(filter)`: to create an index in the database based on a filter
17
+ * - `utils` : this includes `encrypt`, `decrypt`
18
+ */
19
+ constructor(db_instance) {
20
+ // data validation checks
21
+ this._check_required_fields(["name", "encryption_key", "api", "utils"],db_instance)
22
+ this._check_required_fields(["insert", "update", "delete", "search","get","createIndex"],db_instance.api)
23
+ this._check_required_fields(["encrypt", "decrypt","ping"],db_instance.utils)
24
+
25
+ if(db_instance.encryption_key.length>20){throw new Error("encryption_key must have at least 20 letters")}
26
+ // db name should not be blank,
27
+
28
+ this.name = db_instance.name;
29
+ this.encryption_key = db_instance.encryption_key;
30
+
31
+ this.db_api = db_instance.api;
32
+ this.utils = db_instance.utils;
33
+
34
+ this._version = ""
35
+ this.ready_check = { initialized: false, latest: false };
36
+ console.log("Run ready() now");
37
+
38
+ this.plugins = {}
39
+ }
40
+
41
+ /**
42
+ * This is to check if the database is ready to be used. It it important to run this after the class is initialized.
43
+ */
44
+ async ready() {
45
+ console.log("Checking...");
46
+ // @TODO : ping the database
47
+ this._version = await getPackageVersion()
48
+ this.ready_check = await this._check_ready_to_use();
49
+ if (this.ready_check.initialized) {
50
+ console.log("Ready to use!");
51
+ }
52
+ }
53
+
54
+ check_if_ready(){
55
+ return this.ready_check.ready
56
+ }
57
+
58
+ /**
59
+ * Initializes the database making it ready to be used. Typically, required to run after every time package is updated to a new version.
60
+ * See the documentation on the architecture of the DB to understand what default schemas are required for a smooth functioning of the database
61
+ */
62
+ async initialize_db() {
63
+ try {
64
+ if (this.ready_check.initialized == false) {
65
+ // add the meta-schemas doc
66
+ let schema_schema_doc = this._get_blank_doc("schema");
67
+ schema_schema_doc.data = sys_sch.schema_schema;
68
+ await this.db_api.insert(schema_schema_doc);
69
+ // add system schemas
70
+ let keys = Object.keys(sys_sch.system_schemas);
71
+ for (let index = 0; index < keys.length; index++) {
72
+ const element = sys_sch.system_schemas[keys[index]];
73
+ let schema_record = this._get_blank_schema_doc(
74
+ "schema",
75
+ sys_sch.schema_schema["schema"],
76
+ element
77
+ );
78
+ await this.db_api.insert(schema_record);
79
+ }
80
+ // create an index
81
+ await this.db_api.createIndex({
82
+ index: { fields: ["schema", "data", "meta"] },
83
+ });
84
+ console.log("Database Indexed.");
85
+ // create the log doc
86
+ const log_schema = sys_sch.system_schemas["logs"]["schema"];
87
+ let log_doc = this._get_blank_schema_doc("system_logs", log_schema, {
88
+ logs: [
89
+ {
90
+ message: `Database is initialized with version ${this._version}.`,
91
+ on: this._get_now_unix_timestamp(),
92
+ human_date: new Date().toLocaleString(),
93
+ },
94
+ ],
95
+ });
96
+ await this.db_api.insert(log_doc);
97
+ // create the setting doc
98
+ const setting_schema = sys_sch.system_schemas["settings"]["schema"];
99
+ let setting_doc = this._get_blank_schema_doc(
100
+ "system_settings",
101
+ setting_schema,
102
+ {
103
+ name: "beanbagdb_version",
104
+ value: this._version,
105
+ user_editable: false,
106
+ }
107
+ );
108
+ await this.db_api.insert(setting_doc);
109
+ // finally update the flags
110
+ this.ready_check.initialized = true;
111
+ this.ready_check.latest = true;
112
+ console.log("Database initialized");
113
+ } else {
114
+ console.log("Database already initialized");
115
+ if (!this.ready_check.latest) {
116
+ // update to latest schema
117
+ this._update_system_schema();
118
+ } else {
119
+ console.log("Database already up to date");
120
+ }
121
+ }
122
+ } catch (error) {
123
+ console.log(error);
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Adds indexes for all the schemas in the data base. This is important to make search faster. This must be done every time a new schema is introduced in the database
130
+ */
131
+ async update_indexes() {
132
+ // @TODO check this. i don't the index created this way are actually useful in search.
133
+ let all_schemas_docs = await this.db_api.search({
134
+ selector: { schema: "schema" },
135
+ });
136
+ let indexes = [];
137
+ all_schemas_docs.docs.map((item) => {
138
+ Object.keys(item.data.schema.properties).map((key) => {
139
+ indexes.push("data." + key);
140
+ });
141
+ });
142
+ await this.db_api.createIndex({ index: { fields: indexes } });
143
+ }
144
+
145
+ /**
146
+ * Validates a data object against a provided JSON schema
147
+ * It relies on the Ajv package to make the validation.
148
+ * @param {Object} schema_obj - The JSON schema object to validate against
149
+ * @param {Object} data_obj - The data object to validate
150
+ * @throws {Error} If the data object does not conform to the schema
151
+ */
152
+ validate_data(schema_obj, data_obj) {
153
+ const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
154
+ const validate = ajv.compile(schema_obj);
155
+ const valid = validate(data_obj);
156
+ if (!valid) {
157
+ console.log(validate.errors);
158
+ throw new Error(validate.errors);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Returns a document with the provided ID
164
+ * @param {String} doc_id - the doc Id (not the primary key)
165
+ * @param {Boolean} include_schema - whether to include the schema doc as well
166
+ * @returns {Object} {doc} or {doc,schema}
167
+ */
168
+ async get(doc_id,include_schema=false) {
169
+ let doc = await this.db_api.get(doc_id);
170
+ let schema = await this.get_schema_doc(doc.schema);
171
+ doc = this._decrypt_doc(schema, doc);
172
+ if(include_schema){
173
+ return {doc,schema}
174
+ }
175
+ return {doc};
176
+ }
177
+
178
+ /**
179
+ * Returns schema document for the given schema name s
180
+ * @param {String} schema_name - Schema name
181
+ */
182
+ async get_schema_doc(schema_name) {
183
+ let schemaSearch = await this.db_api.search({
184
+ selector: { schema: "schema", "data.name": schema_name },
185
+ });
186
+ if (schemaSearch.docs.length == 0) {
187
+ throw new Error("Schema not found");
188
+ }
189
+ return schemaSearch.docs[0]["data"];
190
+ }
191
+
192
+ /**
193
+ * Fetches a document based on a given schema and primary key.
194
+ * In case schema has a single record, leave the primary_key blank `[]`
195
+ * Can also be used to get special system docs such as settings
196
+ * @param {String} schema_name
197
+ * @param {Object} primary_key
198
+ * @returns object
199
+ */
200
+ async get_doc(schema_name, primary_key = {}) {
201
+ let s_doc = await this.get_schema_doc(schema_name);
202
+ let doc_obj;
203
+ if (
204
+ s_doc["settings"]["primary_keys"] &&
205
+ s_doc["settings"]["primary_keys"].length > 0
206
+ ) {
207
+ let A = s_doc["settings"]["primary_keys"];
208
+ let search_criteria = { schema: schema_name };
209
+ A.forEach((itm) => {
210
+ if (!primary_key[itm]) {
211
+ throw new Error(
212
+ "Incomplete Primary key set. Required field(s) : " + A.join(",")
213
+ );
214
+ }
215
+ search_criteria["data." + itm] = primary_key[itm];
216
+ });
217
+ let s = await this.search({ selector: search_criteria });
218
+ doc_obj = s.docs[0];
219
+ } else {
220
+ let s = await this.search({ selector: { schema: schema_name } });
221
+ if (s.docs.length > 1) {
222
+ throw new Error(
223
+ "Invalid schema. At least one primary key must be defined or set the singleRecord option to true. "
224
+ );
225
+ }
226
+ doc_obj = s.docs[0];
227
+ }
228
+ doc_obj = this._decrypt_doc(s_doc, doc_obj);
229
+ return doc_obj;
230
+ }
231
+
232
+ /**
233
+ * Searches for documents in the database for the specified query. The query are Mango queries.
234
+ * One field is mandatory : Schema
235
+ * E.g
236
+ * @param {Object} criteria
237
+ */
238
+ async search(criteria) {
239
+ if (!criteria["selector"]) {
240
+ throw new Error("Invalid search query.");
241
+ }
242
+ if (!criteria["selector"]["schema"]) {
243
+ throw new Error("The search criteria must contain the schema");
244
+ }
245
+ const results = await this.db_api.search(criteria);
246
+ return results;
247
+ }
248
+
249
+ /**
250
+ * Inserts a doc for the given schema
251
+ * @param {String} schema e.g "contact"
252
+ * @param {Object} data e.g {"name":"","mobile":""...}
253
+ * @param {Object} settings (optional)
254
+ */
255
+ async insert(schema, data, settings = {}) {
256
+ try {
257
+ let doc_obj = await this._insert_pre_checks(schema, data, settings);
258
+ let new_rec = await this.db_api.insert(doc_obj);
259
+ return { id: new_rec["id"] };
260
+ } catch (error) {
261
+ console.log(error);
262
+ throw error;
263
+ }
264
+ }
265
+
266
+
267
+
268
+
269
+ //** Update data */
270
+ /**
271
+ * Update data and meta of a doc.
272
+ *
273
+ * - Q: Which data fields can be edited ?
274
+ * - A: Depends on the setting.editable_fields. If this is blank, then all fields are editable.
275
+ * - Q: Are primary key fields editable ?
276
+ * - A: Yes. before making the update, a check is done to ensue the primary key policy is not violated
277
+ *
278
+ * @param {String} doc_id
279
+ * @param {String} rev_id
280
+ * @param {*} schema_name
281
+ * @param {doc_obj} updates {data:{},meta:{}}, need not be the full document, just the new values of all/some fields
282
+ * @param {Boolean} save_conflict = true -
283
+ * @returns
284
+ */
285
+ async update(doc_id, rev_id, updates, update_source="api",save_conflict = true) {
286
+ // making a big assumption here : primary key fields cannot be edited
287
+ // so updating the doc will not generate primary key conflicts
288
+ let req_data = await this.get(doc_id,true);
289
+ let schema = req_data.schema // await this.get_schema_doc(schema_name);
290
+ let full_doc = req_data.doc // await this.get(doc_id)["doc"];
291
+
292
+ // @TODO fix this : what to do if the rev id does not match
293
+ // if (full_doc["_rev"] != rev_id) {
294
+ // // throw error , save conflicting doc separately by default
295
+ // if (save_conflict) {
296
+ // // save conflicting doc todo
297
+ // }
298
+ // }
299
+
300
+ // blank check
301
+
302
+ // update new value depending on settings.editable_fields (if does not exists, all fields are editable)
303
+ let edit_fields = Object.keys(schema.schema.properties)
304
+ if(schema.settings["editable_fields"]&&schema.settings["editable_fields"].length>0){
305
+ edit_fields = schema.settings["editable_fields"]
306
+ }
307
+
308
+ // now generate the new doc with updates
309
+ let allowed_updates = this._filterObject(updates.data,edit_fields);
310
+ let updated_data = { ...full_doc.data, ...allowed_updates };
311
+
312
+ // validate data
313
+ this.validate_data(schema.schema, updated_data);
314
+
315
+ // primary key check if multiple records can be created
316
+ if(schema.settings["single_record"]==false){
317
+ if(schema.settings["primary_keys"]&&schema.settings["primary_keys"].length>0){
318
+ let pri_fields = schema.settings["primary_keys"]
319
+ let search_criteria = {schema:schema.name}
320
+ pri_fields.map(itm=>{search_criteria["data."+itm] = updated_data[itm]})
321
+ let search = await this.search({selection:search_criteria})
322
+ if(search.docs.length>0){
323
+ if(search.docs.length==1){
324
+ let thedoc = search.docs[0]
325
+ if(thedoc["_id"]!=doc_id){
326
+ throw new Error("Update not allowed. Document with the same primary key already exists")
327
+ }
328
+ }else{
329
+ throw new Error("There is something wrong with the schema")
330
+ }
331
+ }
332
+ }
333
+ }
334
+
335
+ // encrypt the data
336
+
337
+ full_doc["data"] = updated_data
338
+ full_doc = this._encrypt_doc(schema,full_doc);
339
+
340
+ if(updates.meta){
341
+ let m_sch = sys_sch.editable_metadata_schema
342
+ let editable_fields = Object.keys(m_sch["properties"])
343
+ let allowed_meta = this._filterObject(updates.meta,editable_fields)
344
+ this.validate_data(m_sch,allowed_meta)
345
+ full_doc["meta"] = {...full_doc["meta"],...allowed_meta}
346
+ }
347
+
348
+ full_doc.meta["updated_on"] = this._get_now_unix_timestamp()
349
+ full_doc.meta["updated_by"] = update_source
350
+ let up = await this.db_api.update(full_doc);
351
+ return up;
352
+ }
353
+
354
+ async delete(doc_id) {
355
+ await this.db_api.delete(doc_id)
356
+ }
357
+
358
+
359
+ async load_plugin(plugin_name,plugin_module){
360
+ this.plugins[plugin_name] = {}
361
+ for (let func_name in plugin_module){
362
+ if(typeof plugin_module[func_name]=='function'){
363
+ this.plugins[plugin_name][func_name] = plugin_module[func_name].bind(null,this)
364
+ }
365
+ }
366
+ // Check if the plugin has an on_load method and call it
367
+ if (typeof this.plugins[plugin_name].on_load === 'function') {
368
+ await this.plugins[plugin_name].on_load();
369
+ }
370
+ }
371
+
372
+ //////// Helper method ////////
373
+
374
+ _check_required_fields(requiredFields,obj){
375
+ for (const field of requiredFields) {
376
+ if (!obj[field]) {throw new Error(`${field} is required`);}
377
+ }
378
+ }
379
+
380
+ /**
381
+ *
382
+ * @param {*} obj
383
+ * @param {*} fields
384
+ * @returns
385
+ */
386
+ _filterObject(obj, fields) {
387
+ return fields.reduce((filteredObj, field) => {
388
+ if (Object.prototype.hasOwnProperty.call(obj, field)) {
389
+ filteredObj[field] = obj[field];
390
+ }
391
+ return filteredObj;
392
+ }, {});
393
+ }
394
+
395
+ /**
396
+ * Checks if the selected database is initialized for working with BeanBagDB. Also throws a warning if package version does not match with database version.
397
+ * Every time a database is initialized, a setting document `beanbagdb_version` is added. If this does not exists, the database is not initialized. If it exists but does not match the current version, a warning is shown.
398
+ * @returns {object} {initialized:boolean,latest:boolean}
399
+ */
400
+ async _check_ready_to_use() {
401
+ // @TODO check if ready to use in major API methods
402
+ let check = { initialized: false, latest: false };
403
+ // @TODO this is not really fool proof. check all the required docs, they have the system_generated flag
404
+ // what if some user mistakenly modifies or deletes some of the required docs ?
405
+ let version_search = await this.db_api.search({
406
+ selector: { schema: "system_settings", "data.name": "beanbagdb_version" },
407
+ });
408
+ if (version_search.docs.length > 0) {
409
+ let doc = version_search.docs[0];
410
+ check.initialized = true;
411
+ check.latest = doc["data"]["value"] == this._version;
412
+ }
413
+ if (check.initialized == false) {
414
+ console.warn(
415
+ "This database is not ready to be used. It is not initialized. Run `initialize_db()` first"
416
+ );
417
+ }
418
+ if ((check.latest == false) & (check.initialized == true)) {
419
+ console.warn(
420
+ "This database is not updated with the latest version. Run `initialize_db()` again to update to the latest version"
421
+ );
422
+ }
423
+ return check;
424
+ }
425
+
426
+ /**
427
+ * To update the system schema or reset to a stable version to ensure functioning of the BeanBagDB
428
+ */
429
+ async _update_system_schema() {
430
+ console.log("Todo");
431
+ }
432
+
433
+ /**
434
+ * Returns the current Unix timestamp in seconds.
435
+ * divide by 1000 (Date.now gives ms) to convert to seconds. 1 s = 1000 ms
436
+ * @returns {number}
437
+ */
438
+ _get_now_unix_timestamp() {
439
+ return Math.floor(Date.now() / 1000);
440
+ }
441
+
442
+ /**
443
+ * Generates a blank database json object. All objects in the database follow the same structure
444
+ * @param {string} schema_name
445
+ * @returns {object}
446
+ */
447
+ _get_blank_doc(schema_name) {
448
+ if (!schema_name) {
449
+ throw new Error("Schema name not provided for the blank doc");
450
+ }
451
+ let doc = {
452
+ data: {},
453
+ meta: {
454
+ createdOn: this._get_now_unix_timestamp(),
455
+ tags: [],
456
+ app :{}
457
+ },
458
+ schema: schema_name,
459
+ };
460
+ return doc;
461
+ }
462
+
463
+ /**
464
+ * Generates a blank schema doc ready to be inserted to the database. Note that no validation is done. This is for internal use
465
+ * @param {string} schema_name
466
+ * @param {Object} schema_object
467
+ * @param {Object} data
468
+ * @returns {Object}
469
+ */
470
+ _get_blank_schema_doc(schema_name, schema_object, data) {
471
+ this.validate_data(schema_object, data);
472
+ let obj = this._get_blank_doc(schema_name);
473
+ obj["data"] = data;
474
+ return obj;
475
+ }
476
+
477
+ /**
478
+ * Decrypts a given document using it's schema. The list of encrypted fields : schema_obj.settings.encrypted_fields
479
+ * @param {Object} schema_obj
480
+ * @param {Object} doc_obj
481
+ * @returns {Object}
482
+ */
483
+ _decrypt_doc(schema_obj, doc_obj) {
484
+ if (
485
+ schema_obj.settings["encrypted_fields"] &&
486
+ schema_obj.settings["encrypted_fields"].length > 0
487
+ ) {
488
+ schema_obj.settings["encrypted_fields"].forEach((itm) => {
489
+ doc_obj.data[itm] = this.utils.decrypt(
490
+ doc_obj.data[itm],
491
+ this.encryption_key
492
+ );
493
+ });
494
+ }
495
+ return { ...doc_obj };
496
+ }
497
+
498
+ /**
499
+ * Encrypts a given doc using it's schema obj.
500
+ * @param {Object} schema_obj
501
+ * @param {Object} doc_obj
502
+ * @returns {Object}
503
+ */
504
+ _encrypt_doc(schema_obj, doc_obj) {
505
+
506
+ if (
507
+ schema_obj.settings["encrypted_fields"] &&
508
+ schema_obj.settings["encrypted_fields"].length > 0
509
+ ) {
510
+ // console.log(schema_obj,doc_obj)
511
+ schema_obj.settings["encrypted_fields"].forEach((itm) => {
512
+ doc_obj.data[itm] = this.utils.encrypt(
513
+ doc_obj.data[itm],
514
+ this.encryption_key
515
+ );
516
+ });
517
+ }
518
+ return { ...doc_obj };
519
+ }
520
+
521
+ /**
522
+ * Checks if the new document is valid and ready to be inserted in the DB.
523
+ * List of checks:
524
+ * - fetch the schema object and validate the data object against the schema
525
+ * - check if the doc with same primary keys already exists
526
+ * - replace encrypted fields with encrypted values
527
+ * - return the doc
528
+ * @param {Object} schema
529
+ * @param {Object} data
530
+ */
531
+ async _insert_pre_checks(schema, data, settings = {}) {
532
+ // schema search
533
+ let sch_search = await this.search({
534
+ selector: { schema: "schema", "data.name": schema },
535
+ });
536
+ if (sch_search.docs.length == 0) {
537
+ throw new Error("Invalid Schema");
538
+ }
539
+ let schemaDoc = sch_search.docs[0]["data"];
540
+ // validate data
541
+ this.validate_data(schemaDoc.schema, data);
542
+
543
+ // special checks for special docs
544
+ // @TODO : for schema dos: settings fields must be in schema field
545
+ // @TODO : check if single record setting is set to true
546
+
547
+ // duplicate check
548
+ if (
549
+ schemaDoc.settings["primary_keys"] &&
550
+ schemaDoc.settings["primary_keys"].length > 0
551
+ ) {
552
+ let primary_obj = { schema: schema };
553
+ schemaDoc.settings["primary_keys"].map((ky) => {
554
+ primary_obj["data." + ky] = data[ky];
555
+ });
556
+ console.log(primary_obj);
557
+ let prim_search = await this.search({ selector: primary_obj });
558
+ console.log(prim_search);
559
+ if (prim_search.docs.length > 0) {
560
+ throw new Error("Doc already exists");
561
+ }
562
+ }
563
+ // encrypt if required
564
+ let new_data = { ...data };
565
+ if (
566
+ schemaDoc.settings["encrypted_fields"] &&
567
+ schemaDoc.settings["encrypted_fields"].length > 0
568
+ ) {
569
+ schemaDoc.settings["encrypted_fields"].forEach((itm) => {
570
+ new_data[itm] = this.utils.encrypt(data[itm], this.encryption_key);
571
+ });
572
+ }
573
+ // generate the doc object for data
574
+ let doc_obj = this._get_blank_doc(schema);
575
+ doc_obj["data"] = new_data;
576
+ return doc_obj;
577
+ }
578
+ }
579
+
580
+ export default BeanBagDB;