piper-utils 1.1.68 → 1.1.70

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/bin/main.js CHANGED
@@ -516,46 +516,56 @@ let parsed;try{parsed=JSON.parse(query[qKey])}catch(e){return}if(parsed&&"object
516
516
  * Scope to the union of partnerBusinessId + businessIds.
517
517
  * Use for tickets — partner sees own tickets + portfolio merchants' tickets.
518
518
  *
519
- * global → deletes where.businessId
520
- * partner → sets where.businessId = [partnerBusinessId, ...businessIds]
521
- * standard sets where.businessId = access.businessIds
519
+ * global → requested ? where.businessId = requested : deletes where.businessId
520
+ * partner → allowed = uniq([partnerBusinessId, ...businessIds]);
521
+ * requested ? allowed ∩ requested : where.businessId = allowed
522
+ * standard → allowed = businessIds;
523
+ * requested ? allowed ∩ requested : where.businessId = allowed
522
524
  * (explicitly handled here because ticket routes may not call
523
525
  * createFilters before scoping, e.g. delete/resolve handlers)
524
526
  *
525
- * @param {object} where - Sequelize WHERE clause to mutate.
526
- * @param {object} access - Access object from resolveAccess.
527
+ * @param {object} where - Sequelize WHERE clause to mutate.
528
+ * @param {object} access - Access object from resolveAccess.
529
+ * @param {string[]} [requested] - Explicit businessIds from the query string.
527
530
  * @param {{ getPartnerById: Function }} deps
528
531
  */
529
- async function scopeToBookUnionOwn(where,access,{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return void delete where.businessId;if("partner"===access.level){await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById});const ids=_lodash.default.uniq([access.partnerBusinessId,...access.businessIds||[]].filter(Boolean));return void(where.businessId=ids)}if("standard"===access.level)return void(where.businessId=access.businessIds)}
532
+ async function scopeToBookUnionOwn(where,access,requested=[],{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return void(requested.length?where.businessId=requested:delete where.businessId);if("partner"===access.level){await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById});const allowed=_lodash.default.uniq([access.partnerBusinessId,...access.businessIds||[]].filter(Boolean));return void(where.businessId=requested.length?_lodash.default.intersection(allowed,requested):allowed)}if("standard"===access.level){const allowed=access.businessIds||[];return void(where.businessId=requested.length?_lodash.default.intersection(allowed,requested):allowed)}}
530
533
  /***/,exports.scopeToOwnBusiness=
534
+ // `requested` is the caller-supplied businessIds array (what the route extracts via
535
+ // getRequestedBusinessIds(event)). Empty array = no explicit filter (back-compat path).
536
+ // When non-empty it is intersected against the role's allowed set so a user can never
537
+ // widen their scope past what their role permits by passing a query-string businessId.
531
538
  /**
532
539
  * Scope a WHERE clause to the partner's own business (partnerBusinessId).
533
540
  * Use for "mine" resources: deal, activity, application, template.
534
541
  *
535
- * global → deletes where.businessId (super sees everything)
536
- * partner → sets where.businessId = partnerBusinessId
542
+ * global → requested ? where.businessId = requested : deletes where.businessId
543
+ * partner → requested ? [partnerBusinessId] ∩ requested : where.businessId = partnerBusinessId
537
544
  * throws partnerNotConfigured if partnerBusinessId is missing
538
545
  * standard → no-op (createFilters already handled)
539
546
  *
540
- * @param {object} where - Sequelize WHERE clause to mutate.
541
- * @param {object} access - Access object from resolveAccess.
547
+ * @param {object} where - Sequelize WHERE clause to mutate.
548
+ * @param {object} access - Access object from resolveAccess.
549
+ * @param {string[]} [requested] - Explicit businessIds from the query string.
542
550
  * @param {{ getPartnerById: Function }} deps
543
551
  */
544
- async function scopeToOwnBusiness(where,access,{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return void delete where.businessId;if("partner"===access.level){if(await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),!access.partnerBusinessId)throw _errorCodes.errorList.partnerNotConfigured;return void(where.businessId=access.partnerBusinessId)}
552
+ async function scopeToOwnBusiness(where,access,requested=[],{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return void(requested.length?where.businessId=requested:delete where.businessId);if("partner"===access.level){if(await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),!access.partnerBusinessId)throw _errorCodes.errorList.partnerNotConfigured;return void(requested.length?where.businessId=_lodash.default.intersection([access.partnerBusinessId],requested):where.businessId=access.partnerBusinessId)}
545
553
  // standard → no-op (createFilters already injected from JWT)
546
554
  }
547
555
  /**
548
556
  * Scope a WHERE clause to the partner's portfolio (businessIds array).
549
557
  * Use for "book" resources (future transaction lists, merchant inbox views).
550
558
  *
551
- * global → deletes where.businessId
552
- * partner → sets where.businessId = businessIds[]
559
+ * global → requested ? where.businessId = requested : deletes where.businessId
560
+ * partner → requested ? businessIds ∩ requested : where.businessId = businessIds[]
561
+ * (own business is intentionally excluded — book is portfolio only)
553
562
  * standard → no-op
554
563
  *
555
- * @param {object} where - Sequelize WHERE clause to mutate.
556
- * @param {object} access - Access object from resolveAccess.
564
+ * @param {object} where - Sequelize WHERE clause to mutate.
565
+ * @param {object} access - Access object from resolveAccess.
566
+ * @param {string[]} [requested] - Explicit businessIds from the query string.
557
567
  * @param {{ getPartnerById: Function }} deps
558
- */,exports.scopeToPartnerBook=async function scopeToPartnerBook(where,access,{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return void delete where.businessId;if("partner"===access.level)return await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),void(where.businessId=access.businessIds||[]);
568
+ */,exports.scopeToPartnerBook=async function scopeToPartnerBook(where,access,requested=[],{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return void(requested.length?where.businessId=requested:delete where.businessId);if("partner"===access.level){await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById});const allowed=access.businessIds||[];return void(where.businessId=requested.length?_lodash.default.intersection(allowed,requested):allowed)}
559
569
  // standard → no-op
560
570
  };var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_accessContext=__webpack_require__(62),_errorCodes=__webpack_require__(953)},
561
571
  /***/515(module){module.exports=require("@aws-sdk/lib-dynamodb");
@@ -577,7 +587,7 @@ async function scopeToOwnBusiness(where,access,{getPartnerById}={}){if(!access)t
577
587
  * @param {{ getPartnerById: Function }} deps
578
588
  * @returns {object} Pre-bound helpers.
579
589
  */
580
- function createAccessHelpers({getPartnerById}){const deps={getPartnerById};return{ensurePartnerScope:access=>(0,_accessContext.ensurePartnerScope)(access,deps),scopeToOwnBusiness:(where,access)=>(0,_accessScope.scopeToOwnBusiness)(where,access,deps),scopeToPartnerBook:(where,access)=>(0,_accessScope.scopeToPartnerBook)(where,access,deps),scopeToBookUnionOwn:(where,access)=>(0,_accessScope.scopeToBookUnionOwn)(where,access,deps),assertCanWriteOwnBusiness:(access,businessId)=>(0,_accessWrites.assertCanWriteOwnBusiness)(access,businessId,deps),assertCanWriteBookBusiness:(access,businessId)=>(0,_accessWrites.assertCanWriteBookBusiness)(access,businessId,deps),assertCanWriteBookUnionOwn:(access,businessId)=>(0,_accessWrites.assertCanWriteBookUnionOwn)(access,businessId,deps),stampOwnBusinessId:(access,body)=>(0,_accessWrites.stampOwnBusinessId)(access,body,deps)}}
590
+ function createAccessHelpers({getPartnerById}){const deps={getPartnerById};return{ensurePartnerScope:access=>(0,_accessContext.ensurePartnerScope)(access,deps),scopeToOwnBusiness:(where,access,requested)=>(0,_accessScope.scopeToOwnBusiness)(where,access,requested,deps),scopeToPartnerBook:(where,access,requested)=>(0,_accessScope.scopeToPartnerBook)(where,access,requested,deps),scopeToBookUnionOwn:(where,access,requested)=>(0,_accessScope.scopeToBookUnionOwn)(where,access,requested,deps),assertCanWriteOwnBusiness:(access,businessId)=>(0,_accessWrites.assertCanWriteOwnBusiness)(access,businessId,deps),assertCanWriteBookBusiness:(access,businessId)=>(0,_accessWrites.assertCanWriteBookBusiness)(access,businessId,deps),assertCanWriteBookUnionOwn:(access,businessId)=>(0,_accessWrites.assertCanWriteBookUnionOwn)(access,businessId,deps),stampOwnBusinessId:(access,body)=>(0,_accessWrites.stampOwnBusinessId)(access,body,deps)}}
581
591
  /***/;var _accessContext=__webpack_require__(62),_accessScope=__webpack_require__(513),_accessWrites=__webpack_require__(165)},
582
592
  /***/564(module){module.exports=require("bluebird");
583
593
  /***/},
@@ -728,7 +738,9 @@ return errorBody.message=dynamooseError.trim(),errorBody}
728
738
  * @param options {Object} an options object
729
739
  * @returns {{statusCode:number, headers:object, body:string}} a response object
730
740
  */
731
- function failure(body={},options){let cleanedErrorBody;_lodash.default.get(options,"dbClose",_lodash.default.noop)(),("LOG_ALL"===process.env.UTIL_LOG||"test"===process.env.BUILD_ENV)&&_lodash.default.isObject(body)&&console.error("-------\x3eALL UTIL ERROR:",JSON.stringify(body,null,2));cleanedErrorBody=body,_lodash.default.isUndefined(body.details)?(_lodash.default.isUndefined(body.errorCode)||_lodash.default.isUndefined(body.statusCode))&&(cleanedErrorBody=detectSequelizeError(body)):cleanedErrorBody=detectJoyError(body);if(_lodash.default.get(body,"response.data.message")){const err=_lodash.default.get(body,"response.data.message");_lodash.default.isObject(body)&&("LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV||console.error("-------\x3eMSG UTIL ERROR:",JSON.stringify(err,null,2)))}const newBody=_lodash.default.merge({statusCode:500,errorCode:"5XX",message:"INTERNAL UTIL ERROR"},cleanedErrorBody);return buildResponse(newBody.statusCode,newBody)}
741
+ function failure(body={},options){let cleanedErrorBody;_lodash.default.get(options,"dbClose",_lodash.default.noop)(),("LOG_ALL"===process.env.UTIL_LOG||"test"===process.env.BUILD_ENV)&&_lodash.default.isObject(body)&&console.error("-------\x3eALL UTIL ERROR:",JSON.stringify(body,null,2));cleanedErrorBody=body,_lodash.default.isUndefined(body.details)?(_lodash.default.isUndefined(body.errorCode)||_lodash.default.isUndefined(body.statusCode))&&(cleanedErrorBody=detectSequelizeError(body)):cleanedErrorBody=detectJoyError(body);if(_lodash.default.get(body,"response.data.message")){const err=_lodash.default.get(body,"response.data.message");_lodash.default.isObject(body)&&("LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV||console.error("-------\x3eMSG UTIL ERROR:",JSON.stringify(err,null,2)))}const newBody=_lodash.default.merge({statusCode:500,errorCode:"5XX",message:"INTERNAL UTIL ERROR"},cleanedErrorBody);"LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV&&newBody.statusCode>=500&&
742
+ // Log the raw error object so thrown Error stacks survive (JSON.stringify drops them)
743
+ console.error("-------\x3eUTIL ERROR:",newBody.statusCode,_lodash.default.get(body,"message",""),body);return buildResponse(newBody.statusCode,newBody)}
732
744
  /**
733
745
  * Create a failure response object
734
746
  *
@@ -782,7 +794,7 @@ function success(body,options){return _lodash.default.get(options,"dbClose",_lod
782
794
  /**
783
795
  * @param {string} html
784
796
  * @param {{dbClose:function|undefined}} options
785
- */,exports.successHtml=function successHtml(html,options){return _lodash.default.get(options,"dbClose",_lodash.default.noop)(),{statusCode:200,headers:{"Content-Type":"text/html","Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...htmlSecurityHeaders},body:html}};var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_errorCodes=__webpack_require__(953);const securityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()","Cache-Control":"no-store","Content-Security-Policy":"default-src 'none'; frame-ancestors 'none'"},htmlSecurityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=(), payment=*","Cache-Control":"no-store","Content-Security-Policy":["default-src 'self'","script-src 'self' 'unsafe-inline' https://browser.sentry-cdn.com https://test-htp.tokenex.com https://htp.tokenex.com https://sandbox.nmi.com https://secure.nmi.com https://applepay.cdn-apple.com","style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://sandbox.nmi.com https://secure.nmi.com","font-src 'self' https://fonts.gstatic.com","frame-src https:","connect-src 'self' https://*.tokenex.com https://*.sentry.io https://*.nmi.com https://*.apple.com","img-src 'self' data:","frame-ancestors *"].join("; ")};function buildResponse(statusCode,body){return{statusCode,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...securityHeaders},body:JSON.stringify(body)}}function detectSequelizeError(body){const errorBody={},errorName=_lodash.default.get(body,"name","");if("SequelizeForeignKeyConstraintError"===errorName){const detail=_lodash.default.get(body,"parent.detail")||_lodash.default.get(body,"original.detail")||"",notPresent=detail.match(/Key \((\w+)\)=\((.+?)\) is not present in table "(\w+)"/),stillReferenced=detail.match(/Key \((\w+)\)=\((.+?)\) is still referenced from table "(\w+)"/);return errorBody.message=notPresent?"The referenced "+notPresent[3]+" does not exist ("+notPresent[1]+": "+notPresent[2]+")":stillReferenced?"This item cannot be removed because it is referenced by "+stillReferenced[3]:"A referenced item does not exist or is still in use",errorBody.statusCode=409,errorBody.errorCode="4090",errorBody}if("SequelizeUniqueConstraintError"===errorName){const fields=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"path")).filter(Boolean);return errorBody.message=fields.length?"A record with this "+fields.join(", ")+" already exists":"A record with this value already exists",errorBody.statusCode=409,errorBody.errorCode="4091",errorBody}if("SequelizeValidationError"===errorName){const messages=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"message")).filter(Boolean);return errorBody.message=messages.length?messages.join(", "):"Validation error",errorBody.statusCode=400,errorBody.errorCode="4001",errorBody}let sequelizeError=_lodash.default.get(body,"errors",[]).reduce((acc,err)=>acc=acc+" "+_lodash.default.get(err,"message"),"");const parentError=_lodash.default.get(body,"parent","");return sequelizeError=sequelizeError+_lodash.default.get(body,"original.detail","")+_lodash.default.get(body,"TypeError","")+parentError,errorBody.message=sequelizeError.trim(),errorBody}function detectJoyError(body){const errorBody={},joyError=_lodash.default.get(body,"details[0]",{}),v=(_lodash.default.get(body,"details[0].context.details")||[]).reduce((acc,contextItem)=>acc=acc+" "+contextItem?.message||"","");console.error("USER VALIDATION ERROR:",body);const msg=(joyError?.message||"")+v;return msg&&(errorBody.message=_lodash.default.trim(msg)),errorBody.statusCode=400,errorBody.errorCode="4000",errorBody}},
797
+ */,exports.successHtml=function successHtml(html,options){return _lodash.default.get(options,"dbClose",_lodash.default.noop)(),{statusCode:200,headers:{"Content-Type":"text/html","Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...htmlSecurityHeaders},body:html}};var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_errorCodes=__webpack_require__(953);const securityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=()","Cache-Control":"no-store","Content-Security-Policy":"default-src 'none'; frame-ancestors 'none'"},htmlSecurityHeaders={"Strict-Transport-Security":"max-age=31536000; includeSubDomains","X-Content-Type-Options":"nosniff","Referrer-Policy":"strict-origin-when-cross-origin","Permissions-Policy":"camera=(), microphone=(), geolocation=(), payment=*","Cache-Control":"no-store","Content-Security-Policy":["default-src 'self'","script-src 'self' 'unsafe-inline' https://browser.sentry-cdn.com https://test-htp.tokenex.com https://htp.tokenex.com https://sandbox.nmi.com https://secure.nmi.com https://applepay.cdn-apple.com","style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://sandbox.nmi.com https://secure.nmi.com","font-src 'self' https://fonts.gstatic.com","frame-src https:","connect-src 'self' https://*.tokenex.com https://*.sentry.io https://*.nmi.com https://*.apple.com","img-src 'self' data:","frame-ancestors *"].join("; ")};function buildResponse(statusCode,body){return{statusCode,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":!0,...securityHeaders},body:JSON.stringify(body)}}function detectSequelizeError(body){const errorBody={},errorName=_lodash.default.get(body,"name","");if("SequelizeForeignKeyConstraintError"===errorName){const detail=_lodash.default.get(body,"parent.detail")||_lodash.default.get(body,"original.detail")||"",notPresent=detail.match(/Key \((\w+)\)=\((.+?)\) is not present in table "(\w+)"/),stillReferenced=detail.match(/Key \((\w+)\)=\((.+?)\) is still referenced from table "(\w+)"/);return errorBody.message=notPresent?"The referenced "+notPresent[3]+" does not exist ("+notPresent[1]+": "+notPresent[2]+")":stillReferenced?"This item cannot be removed because it is referenced by "+stillReferenced[3]:"A referenced item does not exist or is still in use",errorBody.statusCode=409,errorBody.errorCode="4090",errorBody}if("SequelizeUniqueConstraintError"===errorName){const fields=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"path")).filter(Boolean);return errorBody.message=fields.length?"A record with this "+fields.join(", ")+" already exists":"A record with this value already exists",errorBody.statusCode=409,errorBody.errorCode="4091",errorBody}if("SequelizeValidationError"===errorName){const messages=_lodash.default.get(body,"errors",[]).map(err=>_lodash.default.get(err,"message")).filter(Boolean);return errorBody.message=messages.length?messages.join(", "):"Validation error",errorBody.statusCode=400,errorBody.errorCode="4001",errorBody}let sequelizeError=_lodash.default.get(body,"errors",[]).reduce((acc,err)=>acc=acc+" "+_lodash.default.get(err,"message"),"");const parentError=_lodash.default.get(body,"parent","");return sequelizeError=sequelizeError+_lodash.default.get(body,"original.detail","")+_lodash.default.get(body,"TypeError","")+parentError,errorBody.message=sequelizeError.trim(),errorBody}function detectJoyError(body){const errorBody={},joyError=_lodash.default.get(body,"details[0]",{}),v=(_lodash.default.get(body,"details[0].context.details")||[]).reduce((acc,contextItem)=>acc=acc+" "+contextItem?.message||"",""),msg=(joyError?.message||"")+v;return msg&&(errorBody.message=_lodash.default.trim(msg)),errorBody.statusCode=400,errorBody.errorCode="4000",errorBody}},
786
798
  /***/864(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.loop=loop,exports.publishEvents=
787
799
  /**
788
800
  * Published SNS events at a steady pace for data in a particular bucket. Should be executed via a cloud watch cron job as a part of a micro service