UNPKG

@decaf-ts/for-nano

Version:

decaf-ts persistence adapter for CouchDB via nano

3 lines (2 loc) 9.48 kB
var e,t;e=this,t=function(e,t,r,s,a,o,n){"use strict";const i="nano";class c extends r.CouchDBRepository{constructor(e,t,r=!1){super(e,t,r)}override(e){return super.override(e).for(e)}}class d extends o.Dispatch{constructor(e=5e3){super(),this.timeout=e,this.attemptCounter=0,this.active=!1}close(...e){return this.active=!1,this.attemptCounter=0,this.disposeChangeFeed(),super.close(...e)}async changeHandler(e,s,o,n){const i=n||this.adapter.context(t.OperationKeys.UPDATE,{},a.Model),c=i.logger.for(this.changeHandler);if(e)return c.error("Error in change request: "+e);try{s="string"==typeof s?s.split("\n").filter(e=>!!e).map(e=>JSON.parse(e)):s}catch(e){return c.error("Error parsing couchdb change feed: "+e)}const d=s.length;if(d>0){c.debug(`Received ${d} changes. processing...`);const e=s.map((e,s)=>{if(s===d-1)return void(this.observerLastUpdate===e.last_seq&&c.error(`Invalid last update check: ${this.observerLastUpdate} !== ${e.last_seq}`));const a=e,[o,n]=a.id.split(r.CouchDBKeys.SEPARATOR);return{table:o,id:n,operation:a.deleted?t.OperationKeys.DELETE:"1"===a.changes[a.changes.length-1].rev.split("-")[0]?t.OperationKeys.CREATE:t.OperationKeys.UPDATE,step:a.changes[a.changes.length-1].rev}}).reduce((e,t)=>{if(!t)return e;const{table:r,id:s,operation:a,step:o}=t;return e[r]||(e[r]={}),e[r][a]||(e[r][a]={ids:new Set,step:o}),e[r][a].ids.add(s),e[r][a].step=o,e},{});for(const t of Object.keys(e))for(const r of Object.keys(e[t]))try{await this.updateObservers(t,r,[...e[t][r].ids.values()],i),this.observerLastUpdate=e[t][r].step,c.verbose(`Observer refresh dispatched by ${r} for ${t}`),c.debug("pks: "+Array.from(e[t][r].ids.values()))}catch(e){c.error(`Failed to dispatch observer refresh for ${t}, op ${r}: ${e}`)}}}async initialize(){const e=this.log.for(this.initialize).for(r);async function r(){if(!this.adapter)throw new t.InternalError("No adapter/native observed for dispatch");if(!this.active)try{const e=this.adapter.client.changes({feed:"continuous",include_docs:!1,since:this.observerLastUpdate||"now",timeout:this.timeout},this.changeHandler.bind(this));this.changeFeed=e}catch(t){if(++this.attemptCounter>3)return e.error("Failed to subscribe to couchdb changes: "+t);if(e.info(`Failed to subscribe to couchdb changes: ${t}. Retrying in 5 seconds...`),!this.active)return;return await new Promise(e=>setTimeout(e,this.timeout)),r.call(this)}}this.active=!0,r.call(this).then(()=>{this.log.info("Subscribed to couchdb changes")}).catch(e=>{throw new t.InternalError("Failed to subscribe to couchdb changes: "+e)})}disposeChangeFeed(){this.changeFeed&&(this.changeFeed.cancel?.(),this.changeFeed.close?.(),this.changeFeed.destroy?.(),this.changeFeed.removeAllListeners?.(),this.changeFeed=void 0)}}async function h(e,t,r,s){try{const t=e.get("user");s[r]=t.name||t}catch(e){throw new o.UnsupportedError("No User found in context. Please provide a user in the context")}}class l extends r.CouchDBAdapter{constructor(e,t){super(e,i,t)}async shutdown(...e){const{ctxArgs:t}=(await this.logCtx(e,o.PersistenceKeys.SHUTDOWN,!0)).for(this.shutdown);await super.shutdown(...t),this._client&&(l.closeConnection(this._client[r.CouchDBKeys.NATIVE]),this._client=void 0)}getClient(){const{couchUser:e,couchPassword:t,host:s,dbName:a,protocol:o}=this.config,n=l.connect(e,t,s,o);return r.wrapDocumentScope(n,a,e,t)}async flags(e,t,r,...s){return super.flags(e,t,Object.assign({user:{name:r.user?.name||this.config.couchUser}},r,...s))}Dispatch(){return new d}repository(){return c}async index(...e){const s=r.generateIndexes(e);for(const e of s){const r=await this.client.createIndex(e),{result:s,id:a,name:o}=r;if("existing"===s)throw new t.ConflictError(`Index for table ${o} with id ${a}`)}const a=r.generateViews(e);for(const e of a)try{await this.client.insert(e)}catch(t){if(409!==t?.statusCode&&"conflict"!==t?.error)throw t;{const t=await this.client.get(e._id);await this.client.insert({...e,_rev:t._rev})}}}async create(e,r,s,...a){let o;try{o=await this.client.insert(s)}catch(e){throw this.parseError(e)}if(!o.ok)throw new t.InternalError(`Failed to insert doc id: ${r} in table ${e}`);return this.assignMetadata(s,o.rev)}async createAll(e,r,s,...a){let o;try{o=await this.client.bulk({docs:s})}catch(e){throw this.parseError(e)}if(!o.every(e=>!e.error)){const e=o.reduce((e,t,r)=>(t.error&&e.push(`el ${r}: ${t.error}${t.reason?" - "+t.reason:""}`),e),[]);throw new t.InternalError(e.join("\n"))}return this.assignMultipleMetadata(s,o.map(e=>e.rev))}async read(e,t,...r){const s=this.generateId(a.Model.tableName(e),t);let o;try{o=await this.client.get(s)}catch(e){throw this.parseError(e)}return this.assignMetadata(o,o._rev)}async readAll(e,s,...o){const n=a.Model.tableName(e);return(await this.client.fetch({keys:s.map(e=>this.generateId(n,e))},{})).rows.map(e=>{if(e.error)throw new t.InternalError(e.error);if(e.doc){const t=Object.assign({},e.doc);return this.assignMetadata(t,e.doc[r.CouchDBKeys.REV])}throw new t.InternalError("Should be impossible")})}async update(e,r,s,...a){let o;try{o=await this.client.insert(s)}catch(e){throw this.parseError(e)}if(!o.ok)throw new t.InternalError(`Failed to update doc id: ${r} in table ${e}`);return this.assignMetadata(s,o.rev)}async updateAll(e,r,s,...a){let o;try{o=await this.client.bulk({docs:s})}catch(e){throw this.parseError(e)}if(!o.every(e=>!e.error)){const e=o.reduce((e,t,r)=>(t.error&&e.push(`el ${r}: ${t.error}${t.reason?" - "+t.reason:""}`),e),[]);throw new t.InternalError(e.join("\n"))}return this.assignMultipleMetadata(s,o.map(e=>e.rev))}async delete(e,t,...r){const s=this.generateId(a.Model.tableName(e),t);let o;try{o=await this.client.get(s),await this.client.destroy(s,o._rev)}catch(e){throw this.parseError(e)}return this.assignMetadata(o,o._rev)}async deleteAll(e,s,...o){const{log:n}=this.logCtx(o,this.deleteAll),i=a.Model.tableName(e),c=s.map(e=>this.generateId(i,e)),d=(await this.client.fetch({keys:c},{include_docs:!0})).rows.map((e,r)=>{if(e.error)throw new t.InternalError(e.error);if(!e.doc)throw new t.InternalError("Unable to load document "+(e.id??c[r]));return Object.assign({},e.doc)});return(await this.client.bulk({docs:d.map(e=>({_id:e[r.CouchDBKeys.ID],_rev:e[r.CouchDBKeys.REV],[r.CouchDBKeys.DELETED]:!0}))})).forEach((e,t)=>{e.error&&n.error(`Failed to delete document ${d[t][r.CouchDBKeys.ID]}: ${e.error} ${e.reason?"- "+e.reason:""}`)}),d.map(e=>this.assignMetadata({...e},e[r.CouchDBKeys.REV]))}async raw(e,t=!0,...r){try{const s=await this.client.find(e);if(s.warning){const{log:t}=this.logCtx(r,this.raw),a={warning:s.warning,selector:e?.selector,sort:e?.sort,use_index:e?.use_index,fields:e?.fields,limit:e?.limit,skip:e?.skip};t.warn(`${s.warning} query=${JSON.stringify(a,null,2)}`)}return t?s.docs:s}catch(e){throw this.parseError(e)}}async view(e,t,r,...s){try{return await this.client.view(e,t,r)}catch(e){throw this.parseError(e)}}static connect(e,t,r="localhost:5984",a="http",o){const n=o?{agent:o}:void 0,i=s({url:`${a}://${e}:${t}@${r}`,...n?{requestDefaults:n}:{}});return i&&o&&(i._decafAgent=o),i}static async createDatabase(e,t){let s;try{s=await e.db.create(t)}catch(e){throw r.CouchDBAdapter.parseError(e)}const{ok:a,error:o,reason:n}=s;if(!a)throw r.CouchDBAdapter.parseError(o,n)}static async deleteDatabase(e,s){let a;try{a=await e.db.destroy(s)}catch(e){throw r.CouchDBAdapter.parseError(e)}const{ok:o}=a;if(!o)throw new t.InternalError("Failed to delete database with name "+s)}static closeConnection(e){if(!e)return;const t=e?._decafAgent;t&&"function"==typeof t.destroy&&t.destroy()}static async createUser(e,s,a,o,n=["reader","writer"]){const i=e.db.use("_users"),c={_id:"org.couchdb.user:"+a,name:a,password:o,roles:n,type:"user"};try{const r=await i.insert(c),{ok:o}=r;if(!o)throw new t.InternalError("Failed to create user "+a);if(!(await e.request({db:s,method:"put",path:"_security",body:{admins:{names:[a],roles:[]},members:{names:[a],roles:n}}})).ok)throw new t.InternalError(`Failed to authorize user ${a} to db ${s}`)}catch(e){throw r.CouchDBAdapter.parseError(e)}}static async deleteUser(e,t,s){const a=e.db.use("_users"),o="org.couchdb.user:"+s;try{const e=await a.get(o);await a.destroy(o,e._rev)}catch(e){throw r.CouchDBAdapter.parseError(e)}}static decoration(){super.decoration(),n.Decoration.flavouredAs(i).for(o.PersistenceKeys.CREATED_BY).define(t.onCreate(h),n.propMetadata(o.PersistenceKeys.CREATED_BY,{})).apply(),n.Decoration.flavouredAs(i).for(o.PersistenceKeys.UPDATED_BY).define(t.onCreateUpdate(h),n.propMetadata(o.PersistenceKeys.UPDATED_BY,{})).apply()}}o.Adapter.setCurrent(i),l.decoration();const u="##VERSION##",p="##PACKAGE##";n.Metadata.registerLibrary(p,u),e.COMMIT="8ff7ff9",e.FULL_VERSION="0.12.3-8ff7ff9",e.NanoAdapter=l,e.NanoFlavour=i,e.NanoRepository=c,e.PACKAGE_NAME=p,e.VERSION=u,e.createdByOnNanoCreateUpdate=h},"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@decaf-ts/db-decorators"),require("@decaf-ts/for-couchdb"),require("nano"),require("@decaf-ts/decorator-validation"),require("@decaf-ts/core"),require("@decaf-ts/decoration")):"function"==typeof define&&define.amd?define(["exports","@decaf-ts/db-decorators","@decaf-ts/for-couchdb","nano","@decaf-ts/decorator-validation","@decaf-ts/core","@decaf-ts/decoration"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["for-nano"]={},e.decafTsDbDecorators,e.decafTsForCouchdb,e.nano,e.decafTsDecoratorValidation,e.decafTsCore,e.decafTsDecoration); //# sourceMappingURL=for-nano.cjs.map