@autofleet/sheilta 2.7.4 → 2.8.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/lib/index.cjs +85 -99
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +21 -3
- package/lib/index.d.ts +21 -3
- package/lib/index.js +42 -49
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -1,143 +1,129 @@
|
|
|
1
1
|
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`sequelize`);c=s(c);let l=require(`@autofleet/logger`);l=s(l);let u=require(`@autofleet/errors`);u=s(u);let d=require(`joi`);d=s(d);let f=require(`@autofleet/common-types`);f=s(f);let p=require(`node:crypto`);p=s(p);let m=require(`redis`);m=s(m);let h=require(`express`);h=s(h);let g=require(`zod`);g=s(g);let _=require(`node:events`);_=s(_);const v=[`eq`,`ne`,`gte`,`gt`,`lte`,`lt`,`not`,`in`,`notIn`,`is`,`like`,`iLike`,`notLike`,`between`,`and`,`or`,`overlap`,`contains`],y=`$`,b={$eq:`=`,$ne:`!=`,$gte:`>=`,$gt:`>`,$lte:`<=`,$lt:`<`,$not:`NOT`,$in:`IN`,$notIn:`NOT IN`,$is:`IS`,$like:`LIKE`,$iLike:`ILIKE`,$notLike:`NOT LIKE`,$and:`AND`,$or:`OR`},x=(e={Op:c.Op})=>{let{Op:t}=e;return Object.fromEntries(v.map(e=>[`${`$`+e}`,t[e]]))},S=`-`,ee=`.`,C=`$`,w=20,te=1,ne=100,re=1,ie=1,ae=e=>`\$${e}\$`,T=(e,t)=>e.includes(`.`)&&t.includes(e.split(`.`,1)[0]),E=(e,t)=>{let n=e;return e.includes(`-`)&&([,n]=n.split(`-`,2)),T(n,t)&&([n]=n.split(`.`,1)),n},D=e=>e.includes(`-`),O=e=>{throw new u.BadRequest([Error(e)])},oe=e=>e.split(`.`,2)[1],k=(e=5)=>Array.from({length:e},()=>`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.charAt((0,p.randomInt)(52))).join(``),A=(e,t)=>Object.fromEntries(t.map(t=>[t,e[t]])),j={length:`length`};function M(e){return e.replace(/(?!^)[A-Z]/g,e=>`_${e.toLowerCase()}`)}function se(e){switch(e.action){case j.length:return[(0,c.literal)(`jsonb_array_length(${M(e.columnName)})`),e.alias];default:return e.action,[]}}function ce(e){return e.map(e=>se(e))}function le(e){return e.map(e=>{let t=M(e.columnName),n=`json_build_object(${e.keys.map(e=>`'${e}', ${t} -> '${e}'`).join(`, `)})`,r=e.alias||e.columnName;return[(0,c.literal)(n),r]})}function ue({select:e=[],computed:t=[]}={}){let n=le(e),r=ce(t);return[...n,...r]}const de=`id`,N=`DESC`,fe=`ASC`,P=`customFields.`,{CUSTOM_FIELDS_FILTER_SCOPE:F,CUSTOM_FIELDS_SORT_SCOPE:I}=f.customFields,pe=e=>[`string`,`number`].includes(typeof e)||Array.isArray(e)?e:Object.entries(e).map(([e,t])=>({operator:b[e],value:t})),me=(e,t={})=>{let{literalAttributes:n=[],DBFormatter:r=void 0}=t,[i,a]=e.reduce((e,t)=>{let[i,a=`ASC`]=Array.isArray(t)?t:[t],o=n?.find(e=>e.attribute===i);if(o){let t=r?r(`"${o.attribute}" ${a}`):`${o.attribute} ${a}`;e[1].push(o.literal),e[0].push([t])}else e[0].push(t);return e},[[],[]]);return[i,a]},L=e=>{let t={};return Object.entries(e).forEach(([e,n])=>{let r=k();if(t[r]=e.split(P,2)[1],Array.isArray(n))n.forEach(e=>{let n=k();t[n]=typeof e==`string`?e:e.value});else if(typeof n==`string`||typeof n==`number`){let e=k();t[e]=n}else if(n?.operator){let e=k();t[e]=n.value}}),t},he=e=>{let t={};return e.forEach(e=>{if(e.startsWith(P)){let n=k();t[n]=e.split(P,2)[1]}else if(e.substring(1).startsWith(P)){let n=k();t[n]=e.substring(1).split(P,2)[1]}}),t},ge=(e,t)=>({...he(e),...L(t)}),_e=({order:e,associationModels:t=[],replacementsMap:n={}})=>{let r=[],i=new Map;return e.forEach(e=>{if([e,e.substring(1)].some(e=>e.startsWith(P))){i.has(I)||i.set(I,{});let t=e.split(P,2)[1];i.get(I)[t]=D(e)?N:`ASC`;return}let n=[E(e,t)],a=D(e);T(a?e.split(`-`,2)[1]:e,t)&&n.push(oe(e)),a&&n.push(N),r.push(n)}),{formattedOrders:r,replacementsMap:n,orderScopes:Array.from(i.entries()).map(([e,t])=>t?{method:[e,{replacementsMap:n,scopeValue:t}]}:e)}},ve=e=>e||1,ye=e=>e||20,R=(e,t={})=>{let n=e.map(e=>{let n=t[typeof e==`string`?e:e.association||e.model];return{...typeof e!=`string`&&e,association:n,required:typeof e==`string`||e.required!==!1,...typeof e!=`string`&&e.include&&{include:R(e.include,n?.target?.associations)}}});return n=n.map(({model:e,...t})=>t),n},be=(e,t,n,r=[])=>{let i={},a={},o=new Map;return Object.entries(e).forEach(([e,n])=>{if(e.startsWith(P)){o.has(F)||o.set(F,{});let t=e.split(P,2)[1];o.get(F)[t]=pe(n);return}if(r.includes(e)){a[e]=n;return}let s=T(e,t)?ae(e):e;i[s]=n}),{formattedQuery:i,externalQueryValues:a,formattedScopes:Array.from(o.entries()).map(([e,t])=>t?{method:[e,{replacementsMap:n,scopeValue:t}]}:e)}},xe=(e,t,n)=>({$and:e.split(` `).map(e=>({$or:t.filter(e=>n[e].type.key===`STRING`).map(t=>({[t]:{$iLike:`%${e}%`}}))}))}),Se=({order:e=[],page:t=1,perPage:n=20,include:r=[],query:i={},attributes:a=null,searchTerm:o=null,jsonAttributes:s={}},c,l)=>{let u=ge(e,i),d=Object.keys(c?.associations||{}),{formattedOrders:f,orderScopes:p}=_e({order:[...e,`id`],associationModels:d,replacementsMap:u}),[m,h]=me(f,l),g=ue(s),_=[...h,...a??[],...g],v=a?.length?_:{include:_},y=R(r,c?.associations),b=ve(t),x=ye(n),S=be(i,d,u,l?.additionalAllowedAttributes),{formattedScopes:ee,externalQueryValues:C}=S,{formattedQuery:w}=S;if(o&&!l?.skipSearchTermFormat){let e=xe(o,a?.length?a:Object.keys(c.rawAttributes||{}),c.rawAttributes);w=!w||Object.keys(w).length===0?e:{$and:[w,e]}}return{query:w,order:m,page:b,perPage:x,include:y,scopes:[...ee,...p],...v&&{attributes:v},...Object.keys(C).length>0&&{externalQueryValues:C}}};var Ce=Se;const we=e=>v.includes(e.split(`$`,2)[1]),z=(e,t=[],n=[],r=[])=>{let i=e.startsWith(`$`)&&e.endsWith(`$`)?e.slice(1,-1):e;return[...t,...n].includes(i.includes(`.`)?i.split(`.`,1)[0]:i)||r.includes(i)},Te=(e,t,n,r={})=>{let i=D(e);i&&!e.startsWith(`-`)&&O(`- must be only at the beginning of the word`);let a=i?e.split(`-`,2)[1]:e,o=T(a,n),s=E(e,n),c=r?.literalAttributes?.map(e=>e.attribute)?.includes(a);!o&&s.includes(`.`)&&([s]=s.split(`.`,1)),t.includes(s)||o||c||O(`${e} is invalid. isLiteralAttribute: ${c}`)},Ee=(e,t)=>{t.includes(e)||O(`${e} is invalid`)},B=(e,t,n=[],r={})=>{e.forEach(e=>Te(e,t,n,r))},V=(e,t)=>{e.forEach(e=>Ee(e,t))},De=(e,t)=>{V([...e.select?.map(e=>e.columnName)??[],...e.computed?.map(e=>e.columnName)??[]],t)},Oe=(e,t)=>{let n=Array.isArray(e)?e:Object.keys(e);if(!n?.length)return;let r=n.find(e=>!t?.enrichmentAttributes?.includes(e));r&&O(`enrichment attribute ${r} is invalid`)},H=(e,t,n=[],r=[])=>{Object.entries(e).forEach(([e,i])=>{Array.isArray(i)?i[0]&&typeof i[0]==`object`&&i.map(e=>H(e,t,n,r)):we(e)||z(e,t,n,r)?i&&typeof i==`object`&&H(i,t,[],r):O(`invalid key: ${e}`)})},U=({page:e,perPage:t})=>{e<1&&O(`Page must be greater than 0`),(t>100||t<1)&&O(`PerPage must be between 1 to 100`)},ke=(e,t)=>{let n=Object.keys(t);e.forEach(e=>{z(e.model,n);let r=t[e.model]?.target;r||O(`model not found in associations`);let{rawAttributes:i}=r,a=Object.keys(i);e.where&&H(e.where,a),e.order&&B(e.order,a),e.attributes&&V(e.attributes,a),[null,void 0,!0,!1].includes(e.required)||O(`include.required must be a boolean`)})},W=({query:e={},order:t=[],attributes:n=[],include:r=[],page:i=1,perPage:a=20,enrichments:o=[],group:s=[],jsonAttributes:c={}},l,u={})=>{let d=Object.keys(l.rawAttributes),f=Object.keys(l?.associations||{});return!n||n.length===0?n=d:V(n,d),B(t,d,f,u),H(e,d,f,u.additionalAllowedAttributes),Oe(o,u),De(c,d),Array.isArray(s)||O(`group must be an array`),r.length&&typeof r==`object`?ke(r,l?.associations):r&&typeof r!=`object`&&O(`include must be an array`),U({page:i,perPage:a}),!0},{object:G,string:K,number:q,any:Ae,array:J,alternatives:je}=d.default.types(),Me=(0,l.default)(),Ne=G.keys({query:G,attributes:J.items(K),order:J.items(K),page:q,perPage:q,include:J.items(Ae),searchTerm:K,group:J.items(K),enrichments:je.try(J.items(K),G.pattern(K,{exclude:J.items(K)})),jsonAttributes:d.default.object({select:d.default.array().items(d.default.object({columnName:d.default.string().required(),keys:d.default.array().items(d.default.string().required()).required(),alias:d.default.string().optional()})).default([]),computed:d.default.array().items(d.default.object({columnName:d.default.string().required(),action:d.default.string().valid(...Object.values(j)).required(),alias:d.default.string().required()})).default([])}).default({})}),Y=(e,t,n={})=>{let{query:r,attributes:i,order:a,page:o,perPage:s,include:c,group:l,enrichments:d,jsonAttributes:f}=t,p=Ne.validate(t);if(p.error)throw new u.BadRequest([p.error]);W({query:r,attributes:i,order:a,page:o,perPage:s,include:c,enrichments:d,group:l,jsonAttributes:f},e,n)},Pe=(e,t={},n=`body`)=>(r,i,a)=>{try{Y(e,r[n],t),a()}catch(e){let{query:a,attributes:o,order:s}=r[n];(0,u.handleError)(e,i,{logger:t.logger??Me,message:`error in query middleware`,payload:{error:e,query:a,attributes:o,order:s}})}},X=(e,t,n={})=>{let{order:r,page:i,perPage:a,include:o,query:s,attributes:c,searchTerm:l,jsonAttributes:u}=t,{query:d,externalQueryValues:f,order:p,page:m,perPage:h,include:g,scopes:_,attributes:v}=Ce({query:s,order:r,page:i,perPage:a,include:o,attributes:c,searchTerm:l,jsonAttributes:u},e,n);t.query=d,t.externalQueryValues=f,t.order=p,t.attributes=v,t.page=m,t.perPage=h,t.include=g,t.scopes=_,n.includeRawPayload&&(t.rawPayload={order:r,page:i,perPage:a,include:o,query:s,attributes:c,searchTerm:l})},Fe=(e,t={},n=`body`)=>(r,i,a)=>{X(e,r[n],t),a()},Ie=({model:e,logger:t,validationOptions:n,formatOptions:r,modelName:i=e.constructor?.name,additionalScopes:a=[],modifyQueryValues:o,onRowsRetrieved:s})=>async(c,l)=>{try{Y(e,c.body,{...n,logger:t})}catch(e){(0,u.handleError)(e,l,{logger:t,message:`error in query endpoint`,payload:A(c.body,[`query`,`order`,`attributes`])});return}try{X(e,c.body,r);let n=Object.assign(A(c.body,[`query`,`externalQueryValues`,`order`,`attributes`,`page`,`perPage`,`include`,`scopes`,`enrichments`]),{distinct:!0});t.info(`querying ${i}`,{queryValues:n});let u=o?.(n)??n,{scopes:d=[],query:f,perPage:p,page:m,enrichments:h,externalQueryValues:g,..._}=u,v=await e.scope([...a,...d]).findAndCountAll({where:f,limit:p,offset:(m-1)*p,..._});if(!v.rows.length||!s){l.json(v);return}let y=await s(v,u);l.json(y)}catch(e){(0,u.handleError)(new u.UnexpectedError(e),l,{logger:t,message:`Error while querying ${i}`,payload:{query:c.body}})}};var Z=class e extends Error{constructor(e,t=!1){super(e),this.name=`BulkerError`,this.retryable=t,Object.setPrototypeOf(this,new.target.prototype)}static isBulkerError(t){return t instanceof e}static wrap(t,n,r=!1){return t instanceof e?new e(n??t.message,r||t.retryable):new e(n?`${n}: ${t instanceof Error?t.message:String(t)}`:String(t),r)}static retryable(t){return new e(t,!0)}static nonRetryable(t){return new e(t,!1)}};const Le=e=>e.length===0?`No keys provided`:e.length===1?`Key "${e[0]}" is required`:`Exactly one of [${e.join(`, `)}] must be provided`;function Re(e,t){return g.z.object(e).refine(e=>t.filter(t=>e[t]!==void 0&&e[t]!==null).length===1,{message:Le(t)})}const Q=g.z.string().uuid();function ze(e){return Re(Object.fromEntries(e.map(e=>[e,g.z.union([Q,g.z.array(Q)]).optional()])),e)}const Be=[`businessModelId`,`fleetId`,`demandSourceId`,`contextId`,`userId`,`businessAccountId`,`activeBusinessModelId`];var Ve=class{constructor(e,t){if(this.bulker=e,this.opts=t,this.rabbitQueueName=null,this.rabbit=null,this.bulkHandler=async(e,t,n)=>{try{let{query:n,payload:r,preview:i}=e.body??{},a=r;if(r&&this.payloadSchema)try{a=this.payloadSchema.parse(r)}catch(e){if(e instanceof g.z.ZodError)return t.status(400).json({error:`invalid_payload`,details:e.issues.map(e=>`${e.path.join(`.`)}: ${e.message}`).join(`, `)});throw e}try{Y(this.model,n,{logger:this.bulker.logger})}catch(e){return t.status(400).json({error:`invalid_query`,details:e.message||e})}let o=this.identityScopeSchema.safeParse(n?.query||{});if(!o.success)return t.status(400).json({error:`invalid_identity_scope`,details:o.error.issues.map(e=>`${e.path.join(`.`)}: ${e.message}`).join(`, `)});X(this.model,n,{includeRawPayload:!0});let{query:s,...c}=Object.assign(A(n,[`query`,`externalQueryValues`,`order`,`include`,`scopes`,`enrichments`]),{distinct:!0}),l=[];if(this.opts.additionalIdsHook){let{rawPayload:e}=n;try{l=await this.opts.additionalIdsHook({rawPayload:e,payload:a}),this.bulker.logger.info(`additionalIdsHook returned ${l.length} IDs for action ${this.action}`)}catch(e){return this.bulker.logger.error(`Error in additionalIdsHook for action ${this.action}: ${e.message||e}`,{err:e}),t.status(500).json({error:`additional_ids_hook_error`,details:e.message||e})}}let u=[];s&&typeof s==`object`&&Object.keys(s).length>0&&u.push(s),Array.isArray(l)&&l.length>0&&u.push({id:{$in:l}});let d;if(u.length===0)return t.status(400).json({error:`no_query`,details:`No valid query provided to select records`});if(u.length===1){let[e]=u;d=e}else d={$or:u};this.bulker.logger.info(`Constructed final where clause for action ${this.action}`,{where:d});let f={where:d,...c},m=await this.model.scope(this.modelScopes).count({...f,col:this.idField});if(i)return t.json({estimatedCount:m});let h=(0,p.randomUUID)();return await this.bulker.jobManager.initJob(h,{status:`queued`,total:m,action:this.action}),this.bulker.emitEvent(`job:created`,{jobId:h,action:this.action,total:m}),setImmediate(()=>{this.rabbitScanAndEnqueue(h,f,a).catch(e=>this.bulker.logger.error(e))}),t.status(202).json({jobId:h,estimatedCount:m})}catch(e){return this.bulker.logger.error(`Error in bulkHandler for action ${this.action}: ${e.message||e}`,{err:e}),n(e)}},this.action=t.action,this.model=t.model,this.modelScopes=t.modelScopes??[],this.consumer=t.consumer,this.rabbit=e.rabbit,this.rabbitQueueName=t.rabbitQueueName??`bulk-${this.action}-queue`,this.consumerOptions={enableRabbitTrace:!0,...this.opts.consumerOptions},this.payloadSchema=t.payloadSchema,!/^[a-zA-Z0-9-_]+$/.test(this.action))throw Error(`BulkRoute action must be alphanumeric`);if(!this.model||typeof this.model.findAll!=`function`||typeof this.model.count!=`function`)throw Error(`BulkRoute model must be a valid Sequelize model`);if(typeof this.consumer!=`function`)throw Error(`BulkRoute consumer must be a function`);if(this.payloadSchema&&!(this.payloadSchema instanceof g.z.ZodType))throw Error(`BulkRoute payloadSchema must be a Zod schema`);if(this.modelScopes&&!Array.isArray(this.modelScopes))throw Error(`BulkRoute scopes must be an array of strings`);if(this.opts.additionalIdsHook&&typeof this.opts.additionalIdsHook!=`function`)throw Error(`BulkRoute additionalIdsHook must be a function`);this.idField=t.idField??e.defaults.idField,this.pageSize=t.pageSize??e.defaults.pageSize,this.consumerBatchSize=t.consumerBatchSize??e.defaults.consumerBatchSize;let n=this.model.rawAttributes||{},r=(t.identityScopes&&t.identityScopes.length>0?t.identityScopes:Be).filter(e=>!!n[e]);r.length===0&&this.bulker.logger.warn(`BulkRoute for action ${this.action} has no valid identityScopes configured - all records will be accessible`),this.bulker.logger.info(`BulkRoute for action ${this.action} using idField ${this.idField}, pageSize ${this.pageSize},
|
|
2
|
-
consumerBatchSize ${this.consumerBatchSize}, identityScopes: ${r.join(`, `)}`),this.identityScopeSchema=ze(r),this.startRabbitWorker().catch(e=>{this.bulker.logger.error(`Failed to start RabbitMQ worker for queue ${this.rabbitQueueName}: ${e.message||e}`)})}async getUserJobs(e,t=20){return this.bulker.jobManager.getUserJobs(e,t)}batchIds(e){let t=[];for(let n=0;n<e.length;n+=this.consumerBatchSize)t.push(e.slice(n,n+this.consumerBatchSize));return t}async rabbitScanAndEnqueue(e,t,n){if(!this.rabbit)throw Error(`RabbitMQ not configured in Bulker`);let r=Date.now().toString();if(this.bulker.getUserId){let t=this.bulker.getUserId();t&&await this.bulker.jobManager.addUserJob(t,e)}await this.bulker.jobManager.setJobFields(e,{status:`running`,startTime:r}),this.bulker.emitEvent(`job:started`,{jobId:e,action:this.action});let i=null,a=0,o=0;for(;;){let r=await this.bulker.jobManager.getJobField(e,`status`);if(!r||r===`canceled`){this.bulker.logger.info(`Job ${e} was canceled, stopping scan and enqueue`);break}let s=i?{[c.Op.and]:[t.where,{[this.idField]:{[c.Op.gt]:i}}]}:t.where,l=await this.model.scope(this.modelScopes).findAll({where:s,...A(t,[`include`,`order`,`scopes`]),attributes:[this.idField],order:[[this.idField,`ASC`]],limit:this.pageSize,raw:!0,subQuery:!1});if(l.length===0)break;let u,d=l.map(e=>e[this.idField]);this.consumerBatchSize===1&&d.length===1&&(u=d[0]);let f=this.batchIds(d).map(t=>({jobId:e,ids:t,payload:n,id:u}));this.bulker.logger.info(`Enqueuing ${f.length} batched messages (${d.length} total IDs) to RabbitMQ queue ${this.rabbitQueueName}`);let p=(await Promise.allSettled(f.map(e=>this.rabbit.sendToQueue(this.rabbitQueueName,e)))).filter(e=>e.status===`rejected`);if(p.length>0)throw this.bulker.logger.error(`Failed to enqueue ${p.length} messages to RabbitMQ`,{rejected:p}),Error(`Failed to enqueue ${p.length} messages to RabbitMQ`);a+=l.length,await this.bulker.jobManager.incrJobField(e,`queued`,l.length),i=l[l.length-1][this.idField],o+=1,this.bulker.emitEvent(`scan:page`,{jobId:e,action:this.action,pageNumber:o,itemsInPage:l.length});let m=await this.bulker.jobManager.getJob(e);m&&this.bulker.emitEvent(`job:queued`,{jobId:e,action:this.action,queued:a,total:m.total})}a===0&&await this.bulker.jobManager.completeEmptyJob(e)}async startRabbitWorker(){return this.bulker.logger.info(`Starting RabbitMQ consumer for queue ${this.rabbitQueueName}`),this.bulker.emitEvent(`worker:started`,{action:this.action,queueName:this.rabbitQueueName}),this.rabbit?.consume(this.rabbitQueueName,async(e,t,n)=>{if(!e)return;let{jobId:r,ids:i,payload:a}=e.content,o=!1,s=async()=>{if(!o){o=!0,await Promise.all([this.bulker.jobManager.ack(r,i.length),t()]);for(let e of i)this.bulker.emitEvent(`item:processed`,{jobId:r,action:this.action,id:e})}},c=async(e,t)=>{if(o)return;o=!0;let
|
|
2
|
+
consumerBatchSize ${this.consumerBatchSize}, identityScopes: ${r.join(`, `)}`),this.identityScopeSchema=ze(r),this.startRabbitWorker().catch(e=>{this.bulker.logger.error(`Failed to start RabbitMQ worker for queue ${this.rabbitQueueName}: ${e.message||e}`)})}async getUserJobs(e,t=20){return this.bulker.jobManager.getUserJobs(e,t)}batchIds(e){let t=[];for(let n=0;n<e.length;n+=this.consumerBatchSize)t.push(e.slice(n,n+this.consumerBatchSize));return t}async rabbitScanAndEnqueue(e,t,n){if(!this.rabbit)throw Error(`RabbitMQ not configured in Bulker`);let r=Date.now().toString();if(this.bulker.getUserId){let t=this.bulker.getUserId();t&&await this.bulker.jobManager.addUserJob(t,e)}await this.bulker.jobManager.setJobFields(e,{status:`running`,startTime:r}),this.bulker.emitEvent(`job:started`,{jobId:e,action:this.action});let i=null,a=0,o=0;for(;;){let r=await this.bulker.jobManager.getJobField(e,`status`);if(!r||r===`canceled`){this.bulker.logger.info(`Job ${e} was canceled, stopping scan and enqueue`);break}let s=i?{[c.Op.and]:[t.where,{[this.idField]:{[c.Op.gt]:i}}]}:t.where,l=await this.model.scope(this.modelScopes).findAll({where:s,...A(t,[`include`,`order`,`scopes`]),attributes:[this.idField],order:[[this.idField,`ASC`]],limit:this.pageSize,raw:!0,subQuery:!1});if(l.length===0)break;let u,d=l.map(e=>e[this.idField]);this.consumerBatchSize===1&&d.length===1&&(u=d[0]);let f=this.batchIds(d).map(t=>({jobId:e,ids:t,payload:n,id:u}));this.bulker.logger.info(`Enqueuing ${f.length} batched messages (${d.length} total IDs) to RabbitMQ queue ${this.rabbitQueueName}`);let p=(await Promise.allSettled(f.map(e=>this.rabbit.sendToQueue(this.rabbitQueueName,e)))).filter(e=>e.status===`rejected`);if(p.length>0)throw this.bulker.logger.error(`Failed to enqueue ${p.length} messages to RabbitMQ`,{rejected:p}),Error(`Failed to enqueue ${p.length} messages to RabbitMQ`);a+=l.length,await this.bulker.jobManager.incrJobField(e,`queued`,l.length),i=l[l.length-1][this.idField],o+=1,this.bulker.emitEvent(`scan:page`,{jobId:e,action:this.action,pageNumber:o,itemsInPage:l.length});let m=await this.bulker.jobManager.getJob(e);m&&this.bulker.emitEvent(`job:queued`,{jobId:e,action:this.action,queued:a,total:m.total})}a===0&&await this.bulker.jobManager.completeEmptyJob(e)}async startRabbitWorker(){return this.bulker.logger.info(`Starting RabbitMQ consumer for queue ${this.rabbitQueueName}`),this.bulker.emitEvent(`worker:started`,{action:this.action,queueName:this.rabbitQueueName}),this.rabbit?.consume(this.rabbitQueueName,async(e,t,n)=>{if(!e)return;let{jobId:r,ids:i,payload:a}=e.content,o=!1,s=async()=>{if(!o){o=!0,await Promise.all([this.bulker.jobManager.ack(r,i.length),t()]);for(let e of i)this.bulker.emitEvent(`item:processed`,{jobId:r,action:this.action,id:e})}},c=async(e,t)=>{if(o)return;o=!0;let a=t;a===void 0&&(a=Z.isBulkerError(e)?e.retryable:!1),await Promise.all([this.bulker.jobManager.nack(r,e?e.message||String(e):`nacked`,{ids:i},i.length),n(void 0,{skipRetry:!a})]);for(let t of i)a?this.bulker.emitEvent(`item:retrying`,{jobId:r,action:this.action,id:t}):this.bulker.emitEvent(`item:failed`,{jobId:r,action:this.action,id:t,error:e?e.message||String(e):`nacked`})},l=async e=>{if(o)return;o=!0;let n=e.succeeded??[],i=e.failed??[];await Promise.all([this.bulker.jobManager.partialAck(r,n.length,i),t()]);for(let e of n)this.bulker.emitEvent(`item:processed`,{jobId:r,action:this.action,id:e});for(let e of i)this.bulker.emitEvent(`item:failed`,{jobId:r,action:this.action,id:e.id,error:e.error||`Item failed`})},u=await this.bulker.jobManager.getJobField(r,`status`);if(!u||u===`canceled`){await this.bulker.jobManager.incrJobField(r,`processed`,i.length);return}try{this.bulker.logger.info(`Started processing job ${r} action ${this.action} ids amount: ${i.length}`);for(let e of i)this.bulker.emitEvent(`item:processing`,{jobId:r,action:this.action,id:e});await this.consumer({jobId:r,ids:i,id:this.consumerBatchSize===1?i[0]:void 0,payload:a},s,c,l),await s()}catch(e){this.bulker.logger.error(`Error processing job ${r} action ${this.action}: ${e.message||e}`,{err:e}),this.bulker.emitEvent(`worker:error`,{action:this.action,error:e.message||String(e)}),await c(e,!1)}},this.consumerOptions)}},He=class{constructor(e,t,n=`/bulk-actions`){this.routes=[],this.actionsToRouteMap=new Map,this.bulkHandler=async(e,t,n)=>{try{let{action:r}=e.body??{};if(!r)return t.status(400).json({error:`missing_action`,message:`Request body must include an "action" field`});let i=this.actionsToRouteMap.get(r);return i?i.bulkHandler(e,t,n):t.status(404).json({error:`unknown_action`,message:`Action "${r}" is not registered`,availableActions:Array.from(this.actionsToRouteMap.keys())})}catch(e){return this.bulker.logger.error(`Error in BulkRouter bulkHandler: ${e.message||e}`,{err:e}),n(e),null}},this.getJobHandler=async(e,t,n)=>{try{if(!e.params.id)return t.status(400).json({error:`missing_id`});let n=await this.bulker.jobManager.getJob(e.params.id);return n?t.json(n):t.status(404).json({error:`not_found`})}catch(e){return n(e)}},this.cancelJobHandler=async(e,t,n)=>{try{return e.params.id?await this.bulker.jobManager.cancelJob(e.params.id)?(this.bulker.logger.info(`Job ${e.params.id} cancel requested`),t.json({ok:!0})):t.status(404).json({error:`not_found`}):t.status(400).json({error:`missing_id`})}catch(e){return n(e)}},this.getMyJobsHandler=async(e,t,n)=>{try{if(!this.bulker.getUserId)return t.status(400).json({error:`user_id_function_not_configured`,message:`Bulker instance does not have a getUserId function configured`});let e=this.bulker.getUserId();if(!e)return t.json([]);let n=(await this.bulker.jobManager.getUserJobs(e)).filter(e=>e!==null);return n.sort((e,t)=>{let n=e.createdAt?new Date(e.createdAt).getTime():0;return(t.createdAt?new Date(t.createdAt).getTime():0)-n}),t.json(n)}catch(e){return n(e)}},this.bulker=e,this.router=t??(0,h.Router)(),this.staticRoute=n,this.registerStaticRoutes()}registerStaticRoutes(){this.router.post(this.staticRoute,this.bulkHandler),this.router.get(`/jobs/:id`,this.getJobHandler),this.router.get(`/jobs`,this.getMyJobsHandler),this.router.post(`/jobs/:id/cancel`,this.cancelJobHandler)}addAction(e,t){let n=new Ve(this.bulker,{action:e,...t});if(this.bulker.logger.info(`Registering action handler: ${e}`),this.actionsToRouteMap.has(e))throw Error(`Action "${e}" is already registered`);return this.actionsToRouteMap.set(e,n),this.routes.push(n),n}getRouter(){return this.router}};const Ue=`
|
|
3
3
|
local jobKey = KEYS[1]
|
|
4
|
-
local
|
|
5
|
-
local
|
|
4
|
+
local succeededCount = tonumber(ARGV[1]) or 0
|
|
5
|
+
local failedCount = tonumber(ARGV[2]) or 0
|
|
6
|
+
local failedResults = ARGV[3]
|
|
7
|
+
local errorLogLimit = tonumber(ARGV[4])
|
|
8
|
+
local updatedAt = ARGV[5]
|
|
9
|
+
|
|
6
10
|
local total = tonumber(redis.call('HGET', jobKey, 'total'))
|
|
7
11
|
if not total then
|
|
8
12
|
return 0
|
|
9
13
|
end
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
-- Update counters
|
|
16
|
+
local totalCount = succeededCount + failedCount
|
|
17
|
+
redis.call('HINCRBY', jobKey, 'succeeded', succeededCount)
|
|
18
|
+
redis.call('HINCRBY', jobKey, 'failed', failedCount)
|
|
19
|
+
local processed = redis.call('HINCRBY', jobKey, 'processed', totalCount)
|
|
13
20
|
redis.call('HSET', jobKey, 'updatedAt', updatedAt)
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
redis.call('
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
local
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if not total then
|
|
31
|
-
return 0
|
|
32
|
-
end
|
|
22
|
+
-- Process failed results and add errors
|
|
23
|
+
if failedCount > 0 then
|
|
24
|
+
local errorsJson = redis.call('HGET', jobKey, 'errors') or '[]'
|
|
25
|
+
local errors = cjson.decode(errorsJson)
|
|
26
|
+
local failedData = cjson.decode(failedResults)
|
|
27
|
+
|
|
28
|
+
for i = 1, #failedData do
|
|
29
|
+
if #errors >= errorLogLimit then
|
|
30
|
+
-- Keep only the last (errorLogLimit - 1) entries
|
|
31
|
+
local newErrors = {}
|
|
32
|
+
for j = #errors - errorLogLimit + 2, #errors do
|
|
33
|
+
table.insert(newErrors, errors[j])
|
|
34
|
+
end
|
|
35
|
+
errors = newErrors
|
|
36
|
+
end
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
local
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-- Keep only the last (errorLogLimit - 1) entries
|
|
43
|
-
local newErrors = {}
|
|
44
|
-
for j = #errors - errorLogLimit + 2, #errors do
|
|
45
|
-
table.insert(newErrors, errors[j])
|
|
38
|
+
local failedItem = failedData[i]
|
|
39
|
+
local errorData
|
|
40
|
+
-- If id is a table (object), store it as-is (for backwards compat with nack)
|
|
41
|
+
-- Otherwise, wrap simple values in { id: value } structure (for partialAck)
|
|
42
|
+
if type(failedItem.id) == "table" then
|
|
43
|
+
errorData = failedItem.id
|
|
44
|
+
else
|
|
45
|
+
errorData = { id = failedItem.id }
|
|
46
46
|
end
|
|
47
|
-
errors = newErrors
|
|
48
|
-
end
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
local errorEntry = {
|
|
49
|
+
message = failedItem.error or 'Item failed',
|
|
50
|
+
data = errorData
|
|
51
|
+
}
|
|
52
|
+
table.insert(errors, errorEntry)
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
redis.call('HSET', jobKey, 'errors', cjson.encode(errors))
|
|
57
56
|
end
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-- Update counters
|
|
62
|
-
redis.call('HINCRBY', jobKey, 'failed', count)
|
|
63
|
-
local processed = redis.call('HINCRBY', jobKey, 'processed', count)
|
|
64
|
-
redis.call('HSET', jobKey, 'updatedAt', updatedAt)
|
|
65
|
-
|
|
58
|
+
-- Check if job is complete
|
|
66
59
|
if processed >= total then
|
|
67
60
|
redis.call('HSET', jobKey, 'status', 'completed')
|
|
68
61
|
redis.call('HSET', jobKey, 'endTime', updatedAt)
|
|
69
62
|
end
|
|
70
63
|
|
|
71
64
|
return processed
|
|
72
|
-
`;var $=class e{constructor(e,t){this.redis=e,this.defaults={maxJobsPerUser:t?.maxJobsPerUser??-1,jobTtlSeconds:t?.jobTtlSeconds??168*3600,errorLogLimit:t?.errorLogLimit??10}}setEventEmitter(e){this.eventEmitter=e}static jobKey(e){return`bulker:job:${e}`}static userJobsKey(e){return`bulker:user:${e}:jobs`}async initJob(t,n){let r=Date.now().toString(),i=e.jobKey(t),a=this.redis.multi();a.hSet(i,{status:n.status,total:String(n.total),queued:`0`,processed:`0`,succeeded:`0`,failed:`0`,action:n.action,errors:JSON.stringify([]),createdAt:r,updatedAt:r,startTime:r,endTime:``}),a.expire(i,this.defaults.jobTtlSeconds),await a.exec()}async getJob(t){let n=e.jobKey(t),r=await this.redis.hGetAll(n);if(!r||Object.keys(r).length===0)return null;let i=r.startTime?Number(r.startTime):null,a=r.endTime&&r.endTime!==``?Number(r.endTime):null,o=Date.now(),s={startTime:i?new Date(i).toISOString():null,endTime:a?new Date(a).toISOString():null,durationMs:i?(a||o)-i:null};return{jobId:t,status:r.status,action:r.action,total:Number(r.total??0),queued:Number(r.queued??0),processed:Number(r.processed??0),succeeded:Number(r.succeeded??0),failed:Number(r.failed??0),errors:r.errors?JSON.parse(r.errors):[],createdAt:r.createdAt?new Date(Number(r.createdAt)).toISOString():void 0,updatedAt:r.updatedAt?new Date(Number(r.updatedAt)).toISOString():void 0,duration:s}}async setJobField(t,n,r){let i=e.jobKey(t);if(!await this.redis.exists(i))return!1;let a=this.redis.multi();return a.hSet(i,n,r),a.hSet(i,`updatedAt`,Date.now().toString()),await a.exec(),!0}async setJobFields(t,n){let r=e.jobKey(t);if(!await this.redis.exists(r))return!1;let i=this.redis.multi();return i.hSet(r,n),i.hSet(r,`updatedAt`,Date.now().toString()),await i.exec(),!0}async incrJobField(t,n,r=1){let i=e.jobKey(t),a=this.redis.multi();a.hIncrBy(i,n,r),a.hSet(i,`updatedAt`,Date.now().toString());let o=await a.exec();return o?.[0]&&o[0][0]===null?o[0][1]:0}async getJobField(t,n){let r=e.jobKey(t);return this.redis.hGet(r,n)}async cancelJob(e){return this.setJobField(e,`status`,`canceled`)}async completeEmptyJob(e){let t=Date.now().toString();await this.setJobFields(e,{status:`completed`,endTime:t})}async ack(t,n=1){let r=e.jobKey(t);await this.redis.eval(`
|
|
65
|
+
`;var $=class e{constructor(e,t){this.redis=e,this.defaults={maxJobsPerUser:t?.maxJobsPerUser??-1,jobTtlSeconds:t?.jobTtlSeconds??168*3600,errorLogLimit:t?.errorLogLimit??10}}setEventEmitter(e){this.eventEmitter=e}static jobKey(e){return`bulker:job:${e}`}static userJobsKey(e){return`bulker:user:${e}:jobs`}async initJob(t,n){let r=Date.now().toString(),i=e.jobKey(t),a=this.redis.multi();a.hSet(i,{status:n.status,total:String(n.total),queued:`0`,processed:`0`,succeeded:`0`,failed:`0`,action:n.action,errors:JSON.stringify([]),createdAt:r,updatedAt:r,startTime:r,endTime:``}),a.expire(i,this.defaults.jobTtlSeconds),await a.exec()}async getJob(t){let n=e.jobKey(t),r=await this.redis.hGetAll(n);if(!r||Object.keys(r).length===0)return null;let i=r.startTime?Number(r.startTime):null,a=r.endTime&&r.endTime!==``?Number(r.endTime):null,o=Date.now(),s={startTime:i?new Date(i).toISOString():null,endTime:a?new Date(a).toISOString():null,durationMs:i?(a||o)-i:null};return{jobId:t,status:r.status,action:r.action,total:Number(r.total??0),queued:Number(r.queued??0),processed:Number(r.processed??0),succeeded:Number(r.succeeded??0),failed:Number(r.failed??0),errors:r.errors?JSON.parse(r.errors):[],createdAt:r.createdAt?new Date(Number(r.createdAt)).toISOString():void 0,updatedAt:r.updatedAt?new Date(Number(r.updatedAt)).toISOString():void 0,duration:s}}async setJobField(t,n,r){let i=e.jobKey(t);if(!await this.redis.exists(i))return!1;let a=this.redis.multi();return a.hSet(i,n,r),a.hSet(i,`updatedAt`,Date.now().toString()),await a.exec(),!0}async setJobFields(t,n){let r=e.jobKey(t);if(!await this.redis.exists(r))return!1;let i=this.redis.multi();return i.hSet(r,n),i.hSet(r,`updatedAt`,Date.now().toString()),await i.exec(),!0}async incrJobField(t,n,r=1){let i=e.jobKey(t),a=this.redis.multi();a.hIncrBy(i,n,r),a.hSet(i,`updatedAt`,Date.now().toString());let o=await a.exec();return o?.[0]&&o[0][0]===null?o[0][1]:0}async getJobField(t,n){let r=e.jobKey(t);return this.redis.hGet(r,n)}async cancelJob(e){return this.setJobField(e,`status`,`canceled`)}async completeEmptyJob(e){let t=Date.now().toString();await this.setJobFields(e,{status:`completed`,endTime:t})}async ack(e,t=1){await this.partialAck(e,t,[])}async nack(e,t,n,r=1){let i=[],a=e=>n.ids&&Array.isArray(n.ids)?n.ids[e]??`unknown-${e}`:`unknown-${e}`;for(let e=0;e<r;e++)i.push({id:a(e),error:t});await this.partialAck(e,0,i)}async partialAck(t,n,r){let i=e.jobKey(t);await this.redis.eval(`
|
|
73
66
|
local jobKey = KEYS[1]
|
|
74
|
-
local
|
|
75
|
-
local
|
|
67
|
+
local succeededCount = tonumber(ARGV[1]) or 0
|
|
68
|
+
local failedCount = tonumber(ARGV[2]) or 0
|
|
69
|
+
local failedResults = ARGV[3]
|
|
70
|
+
local errorLogLimit = tonumber(ARGV[4])
|
|
71
|
+
local updatedAt = ARGV[5]
|
|
72
|
+
|
|
76
73
|
local total = tonumber(redis.call('HGET', jobKey, 'total'))
|
|
77
74
|
if not total then
|
|
78
75
|
return 0
|
|
79
76
|
end
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
-- Update counters
|
|
79
|
+
local totalCount = succeededCount + failedCount
|
|
80
|
+
redis.call('HINCRBY', jobKey, 'succeeded', succeededCount)
|
|
81
|
+
redis.call('HINCRBY', jobKey, 'failed', failedCount)
|
|
82
|
+
local processed = redis.call('HINCRBY', jobKey, 'processed', totalCount)
|
|
83
83
|
redis.call('HSET', jobKey, 'updatedAt', updatedAt)
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
redis.call('
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
local
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if not total then
|
|
101
|
-
return 0
|
|
102
|
-
end
|
|
85
|
+
-- Process failed results and add errors
|
|
86
|
+
if failedCount > 0 then
|
|
87
|
+
local errorsJson = redis.call('HGET', jobKey, 'errors') or '[]'
|
|
88
|
+
local errors = cjson.decode(errorsJson)
|
|
89
|
+
local failedData = cjson.decode(failedResults)
|
|
90
|
+
|
|
91
|
+
for i = 1, #failedData do
|
|
92
|
+
if #errors >= errorLogLimit then
|
|
93
|
+
-- Keep only the last (errorLogLimit - 1) entries
|
|
94
|
+
local newErrors = {}
|
|
95
|
+
for j = #errors - errorLogLimit + 2, #errors do
|
|
96
|
+
table.insert(newErrors, errors[j])
|
|
97
|
+
end
|
|
98
|
+
errors = newErrors
|
|
99
|
+
end
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
local
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-- Keep only the last (errorLogLimit - 1) entries
|
|
113
|
-
local newErrors = {}
|
|
114
|
-
for j = #errors - errorLogLimit + 2, #errors do
|
|
115
|
-
table.insert(newErrors, errors[j])
|
|
101
|
+
local failedItem = failedData[i]
|
|
102
|
+
local errorData
|
|
103
|
+
-- If id is a table (object), store it as-is (for backwards compat with nack)
|
|
104
|
+
-- Otherwise, wrap simple values in { id: value } structure (for partialAck)
|
|
105
|
+
if type(failedItem.id) == "table" then
|
|
106
|
+
errorData = failedItem.id
|
|
107
|
+
else
|
|
108
|
+
errorData = { id = failedItem.id }
|
|
116
109
|
end
|
|
117
|
-
errors = newErrors
|
|
118
|
-
end
|
|
119
110
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
local errorEntry = {
|
|
112
|
+
message = failedItem.error or 'Item failed',
|
|
113
|
+
data = errorData
|
|
114
|
+
}
|
|
115
|
+
table.insert(errors, errorEntry)
|
|
124
116
|
end
|
|
125
117
|
|
|
126
|
-
|
|
118
|
+
redis.call('HSET', jobKey, 'errors', cjson.encode(errors))
|
|
127
119
|
end
|
|
128
120
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-- Update counters
|
|
132
|
-
redis.call('HINCRBY', jobKey, 'failed', count)
|
|
133
|
-
local processed = redis.call('HINCRBY', jobKey, 'processed', count)
|
|
134
|
-
redis.call('HSET', jobKey, 'updatedAt', updatedAt)
|
|
135
|
-
|
|
121
|
+
-- Check if job is complete
|
|
136
122
|
if processed >= total then
|
|
137
123
|
redis.call('HSET', jobKey, 'status', 'completed')
|
|
138
124
|
redis.call('HSET', jobKey, 'endTime', updatedAt)
|
|
139
125
|
end
|
|
140
126
|
|
|
141
127
|
return processed
|
|
142
|
-
`,{keys:[
|
|
128
|
+
`,{keys:[i],arguments:[String(n),String(r.length),JSON.stringify(r),String(this.defaults.errorLogLimit),Date.now().toString()]});let a=await this.getJob(t);if(a&&a.status===`completed`){let e=a.duration.durationMs||0;this.eventEmitter?.emitEvent(`job:completed`,{jobId:t,action:a.action,processed:a.processed,failed:a.failed,duration:e})}}async addUserJob(t,n){let r=e.userJobsKey(t),i=this.redis.multi();i.lPush(r,n),this.defaults.maxJobsPerUser>0&&i.lTrim(r,0,this.defaults.maxJobsPerUser-1),i.expire(r,3600),await i.exec()}async removeUserJob(t,n){let r=e.userJobsKey(t);await this.redis.lRem(r,0,n)}async getUserJobs(t,n=20){let r=e.userJobsKey(t),i=await this.redis.lRange(r,0,n-1);return await Promise.all(i.map(e=>this.getJob(e)))}};const We={emitEvent:()=>{}};var Ge=class extends _.EventEmitter{constructor(e){if(super(),this.bulkRouters=[],this.sequelize=e.sequelize,this.logger=e.logger,this.rabbit=e.rabbit,this.eventsEnabled=e.emitEvents??!1,e.redis&&typeof e.redis.connect==`function`)this.redis=e.redis,this.redis.isOpen||this.redis.connect().catch(e=>{this.logger.error(`Error connecting to Redis:`,e)});else{let t=e.redis;this.redis=(0,m.createClient)({socket:{host:t.host,port:t.port},commandsQueueMaxLength:5e3,disableOfflineQueue:!1,password:t.password,database:t.db}),this.redis.connect().catch(e=>{this.logger.error(`Error connecting to Redis:`,e)})}this.getUserId=e.getUserId??(()=>null),this.defaults={maxJobsPerUser:e.defaults?.maxJobsPerUser??-1,pageSize:e.defaults?.pageSize??1e3,idField:e.defaults?.idField??`id`,consumerBatchSize:e.defaults?.consumerBatchSize??1,workerConcurrency:e.defaults?.workerConcurrency??16,jobTtlSeconds:e.defaults?.jobTtlSeconds??168*3600,errorLogLimit:e.defaults?.errorLogLimit??10},this.jobManager=new $(this.redis,{maxJobsPerUser:this.defaults.maxJobsPerUser,jobTtlSeconds:this.defaults.jobTtlSeconds,errorLogLimit:this.defaults.errorLogLimit}),this.jobManager.setEventEmitter(this.eventsEnabled?{emitEvent:this.emitEvent.bind(this)}:We)}pingRedis(){return this.redis.ping()}createBulkRouter(e,t){let n=new He(this,e,t);return this.bulkRouters.push(n),n}emitEvent(e,...t){this.eventsEnabled&&this.emit(e,...t)}};exports.Bulker=Ge,exports.BulkerError=Z,exports.BulkerJobManager=$,exports.formatOperators=x,exports.generateFilterReplacements=L,exports.queryFormatMiddleware=Fe,exports.queryHandler=Ie,exports.queryValidationMiddleware=Pe,exports.validatePayload=W,Object.defineProperty(exports,`z`,{enumerable:!0,get:function(){return g.z}});
|
|
143
129
|
//# sourceMappingURL=index.cjs.map
|