piper-utils 1.1.67 → 1.1.69

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
@@ -95,51 +95,7 @@ async function assertCanWriteBookBusiness(access,businessId,{getPartnerById}={})
95
95
  */
96
96
  async function assertCanWriteOwnBusiness(access,businessId,{getPartnerById}={}){if(!access)throw _errorCodes.errorList.unauthorized;if("global"===access.level)return;if("partner"===access.level){if(await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),!access.partnerBusinessId)throw{..._errorCodes.errorList.partnerNotConfigured};if(businessId!==access.partnerBusinessId)throw _errorCodes.errorList.unauthorized;return}throw _errorCodes.errorList.unauthorized},exports.stampOwnBusinessId=async function stampOwnBusinessId(access,body,{getPartnerById}={}){if(!access||"partner"!==access.level)return;if(body.businessId)return;await(0,_accessContext.ensurePartnerScope)(access,{getPartnerById}),body.businessId=access.partnerBusinessId}
97
97
  /***/;var _lodash=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(__webpack_require__(825)),_accessContext=__webpack_require__(62),_errorCodes=__webpack_require__(953)},
98
- /***/179(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.attachAudit=
99
- /**
100
- * Define a sibling `<ModelName>Audit` table for the given Sequelize model and
101
- * wire the four lifecycle hooks (`afterCreate`, `afterUpsert`, `afterUpdate`,
102
- * `afterDestroy`) that record changes into it.
103
- *
104
- * Call this **once per model at boot time** from inside the model's
105
- * `dbSetup()` function. It is idempotent — calling it again for a model that
106
- * already has an audit table attached is a no-op.
107
- *
108
- * The audit table has no foreign key into any user table; the username of the
109
- * person who made the change is stamped onto each row from the JWT (set by
110
- * {@link bindAuditRequest}). This keeps the audit util portable across domain
111
- * services that own different user models.
112
- *
113
- * @example
114
- * // src/customer/customer.js
115
- * Customer.dbSetup = async function () {
116
- * await attachAudit(Customer);
117
- * };
118
- *
119
- * @param {import('sequelize').ModelStatic<any>} model the Sequelize model to audit
120
- * @returns {Promise<void>} resolves when the audit table's `sync()` completes
121
- * @see {@link bindAuditRequest} - per-request setup that turns audit on
122
- * @see {@link getAuditModel} - retrieve the audit model from the parent
123
- * @see {@link getAuditFilter} - build a filter for an audit history query
124
- */
125
- async function attachAudit(model){if(auditModels[model.name])return;const auditModel=model.sequelize.define(model.name+"Audit",function getAuditSchema(model){const DataTypes=model.sequelize.Sequelize;return{field:{type:DataTypes.STRING,allowNull:!1},type:DataTypes.STRING,valueOld:DataTypes.STRING,valueNew:DataTypes.STRING,changedByUser:{type:DataTypes.STRING,allowNull:!1},businessId:{type:DataTypes.STRING,allowNull:!1}}}
126
- /**
127
- * Internal hook factory. Returns the async function that Sequelize invokes on
128
- * each create/upsert/update/destroy. Reads its per-request state from
129
- * {@link modelOptions}; emits zero rows when audit is disabled for the model.
130
- *
131
- * Sensitive fields inside JSON values (token, password, secret, key,
132
- * credential, auth, securityCode, cvv, pin, ssn) are masked as
133
- * `***************` before being persisted. String values longer than 255
134
- * characters are truncated.
135
- *
136
- * Exported only so tests can drive the hook directly. Application code should
137
- * never call this — use {@link attachAudit}.
138
- *
139
- * @param {string} modelName name of the parent Sequelize model
140
- * @returns {(modelInfo: any, options: { type?: string, transaction?: import('sequelize').Transaction }) => Promise<void>}
141
- * the hook function Sequelize will call on each lifecycle event
142
- */(model),{updatedAt:!1,freezeTableName:!0});return auditModel.belongsTo(model,{foreignKey:{allowNull:!0},onDelete:"SET NULL"}),auditModels[model.name]=auditModel,model.addHook("afterCreate",auditMe(model.name)),model.addHook("afterUpsert",auditMe(model.name)),model.addHook("afterUpdate",auditMe(model.name)),model.addHook("afterDestroy",auditMe(model.name)),auditModel.sync()}
98
+ /***/179(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.attachAudit=attachAudit,exports.auditMe=auditMe,exports.auditModels=void 0,exports.bindAuditRequest=
143
99
  /**
144
100
  * Bind per-request audit context for one or more models. Call this **once at
145
101
  * the top of each route handler** that mutates an audited model.
@@ -177,7 +133,14 @@ async function attachAudit(model){if(auditModels[model.name])return;const auditM
177
133
  * @see {@link bindAuditRequestForUser} - the equivalent for non-JWT contexts
178
134
  * (webhooks, integration crons, Cognito triggers)
179
135
  * @see {@link attachAudit} - one-time setup that creates the audit table
180
- */,exports.auditMe=auditMe,exports.auditModels=void 0,exports.bindAuditRequest=function bindAuditRequest(event,businessId,models){const user=(0,_requestResponse.getCurrentUser)(event),auditEnabled=!!((0,_accessRightsUtils.getCompanySettings)(event)||{}).auditEnabled;_lodash.default.forEach(models,model=>{modelOptions[model.name]={user,businessId,auditEnabled}})}
136
+ */
137
+ function bindAuditRequest(event,businessId,models){const user=(0,_requestResponse.getCurrentUser)(event),auditEnabled=!!((0,_accessRightsUtils.getCompanySettings)(event)||{}).auditEnabled;_lodash.default.forEach(models,model=>{
138
+ // Lazily ensure the audit table model + hooks are wired in this Lambda
139
+ // process. `attachAudit` is idempotent — first call registers, subsequent
140
+ // calls are no-ops. The returned sync() promise is intentionally not
141
+ // awaited; the table is expected to already exist from the migration
142
+ // Lambda, and the synchronous registration is what the request needs.
143
+ const attachPromise=attachAudit(model);attachPromise&&"function"==typeof attachPromise.catch&&attachPromise.catch(err=>console.error("attachAudit sync failed for",model.name,err)),modelOptions[model.name]={user,businessId,auditEnabled}})}
181
144
  /**
182
145
  * Bind per-request audit context for **system-driven flows** that do not carry
183
146
  * an API Gateway JWT — for example, payment-gateway webhooks, integration
@@ -211,7 +174,9 @@ async function attachAudit(model){if(auditModels[model.name])return;const auditM
211
174
  * binds context but suppresses writes; defaults to `true`
212
175
  * @returns {void}
213
176
  * @see {@link bindAuditRequest} - the JWT-driven equivalent for API handlers
214
- */,exports.bindAuditRequestForUser=function bindAuditRequestForUser(user,businessId,models,opts={}){const auditEnabled=!1!==opts.auditEnabled;_lodash.default.forEach(models,model=>{modelOptions[model.name]={user,businessId,auditEnabled}})}
177
+ */,exports.bindAuditRequestForUser=function bindAuditRequestForUser(user,businessId,models,opts={}){const auditEnabled=!1!==opts.auditEnabled;_lodash.default.forEach(models,model=>{
178
+ // See bindAuditRequest for why this is here.
179
+ const attachPromise=attachAudit(model);attachPromise&&"function"==typeof attachPromise.catch&&attachPromise.catch(err=>console.error("attachAudit sync failed for",model.name,err)),modelOptions[model.name]={user,businessId,auditEnabled}})}
215
180
  /**
216
181
  * Get the `<ModelName>Audit` Sequelize model that {@link attachAudit} created
217
182
  * for a parent model. Returns `undefined` if audit was never attached.
@@ -346,7 +311,51 @@ function getAuditFilter(modelToFilter,id,options){const filter={where:{businessI
346
311
  * Exported for tests; do not mutate from app code.
347
312
  *
348
313
  * @type {Object<string, { user: { username: string, id?: number }, businessId: string|number, auditEnabled: boolean }>}
349
- */function auditMe(modelName){return async(modelInfo,options)=>{_lodash.default.isArray(modelInfo)&&(modelInfo=modelInfo[0]);const opts=modelOptions[modelName];if(!opts||!opts.auditEnabled)return;const auditModel=auditModels[modelName],writeAuditRow=async(field,type,valueOld,valueNew)=>{const data={field,type,valueOld:String(valueOld).substring(0,255),valueNew:String(valueNew).substring(0,255),businessId:opts.businessId,changedByUser:opts.user.username};data[modelName+"Id"]=modelInfo.dataValues.id;const newOptions={};return options.transaction&&(newOptions.transaction=options.transaction),auditModel.create(data,newOptions)},checkAudit=async(field,type="INITIAL",valueOld,valueNew)=>{if(!_lodash.default.isEqual(valueOld,valueNew)){if(_lodash.default.isString(valueOld)&&_lodash.default.isString(valueNew)&&(type="INSERT"),_lodash.default.isArray(valueOld)||_lodash.default.isArray(valueNew)){let oldString=JSON.stringify(valueOld||[]),newString=JSON.stringify(valueNew||[]);return oldString=oldString.replace(SENSITIVE_FIELDS_PATTERN,'"$1":"***************"').substring(0,255),newString=newString.replace(SENSITIVE_FIELDS_PATTERN,'"$1":"***************"').substring(0,255),writeAuditRow(field,"UPDATE",oldString,newString)}return _lodash.default.isObject(valueOld)||_lodash.default.isObject(valueNew)?(_lodash.default.forEach(valueOld,(valueSub,keySub)=>{checkAudit(field+" "+keySub,"DELETED",valueSub||"",(valueNew||{})[keySub]||"")}),void _lodash.default.forEach(valueNew,(valueSub,keySub)=>{checkAudit(field+" "+keySub,"INSERT",(valueOld||{})[keySub]||"",valueSub||"")})):writeAuditRow(field,type,valueOld,valueNew)}};return _bluebird.default.map([...modelInfo._changed],async field=>{const valueOld=modelInfo._previousDataValues[field]||"",valueNew=modelInfo.dataValues[field]||"";await checkAudit(field,options.type,valueOld,valueNew)})}}},
314
+ */
315
+ /**
316
+ * Internal hook factory. Returns the async function that Sequelize invokes on
317
+ * each create/upsert/update/destroy. Reads its per-request state from
318
+ * {@link modelOptions}; emits zero rows when audit is disabled for the model.
319
+ *
320
+ * Sensitive fields inside JSON values (token, password, secret, key,
321
+ * credential, auth, securityCode, cvv, pin, ssn) are masked as
322
+ * `***************` before being persisted. String values longer than 255
323
+ * characters are truncated.
324
+ *
325
+ * Exported only so tests can drive the hook directly. Application code should
326
+ * never call this — use {@link attachAudit}.
327
+ *
328
+ * @param {string} modelName name of the parent Sequelize model
329
+ * @returns {(modelInfo: any, options: { type?: string, transaction?: import('sequelize').Transaction }) => Promise<void>}
330
+ * the hook function Sequelize will call on each lifecycle event
331
+ */
332
+ function auditMe(modelName){return async(modelInfo,options)=>{_lodash.default.isArray(modelInfo)&&(modelInfo=modelInfo[0]);const opts=modelOptions[modelName];if(!opts||!opts.auditEnabled)return;const auditModel=auditModels[modelName],writeAuditRow=async(field,type,valueOld,valueNew)=>{const data={field,type,valueOld:String(valueOld).substring(0,255),valueNew:String(valueNew).substring(0,255),businessId:opts.businessId,changedByUser:opts.user.username};data[modelName+"Id"]=modelInfo.dataValues.id;const newOptions={};return options.transaction&&(newOptions.transaction=options.transaction),auditModel.create(data,newOptions)},checkAudit=async(field,type="INITIAL",valueOld,valueNew)=>{if(!_lodash.default.isEqual(valueOld,valueNew)){if(_lodash.default.isString(valueOld)&&_lodash.default.isString(valueNew)&&(type="INSERT"),_lodash.default.isArray(valueOld)||_lodash.default.isArray(valueNew)){let oldString=JSON.stringify(valueOld||[]),newString=JSON.stringify(valueNew||[]);return oldString=oldString.replace(SENSITIVE_FIELDS_PATTERN,'"$1":"***************"').substring(0,255),newString=newString.replace(SENSITIVE_FIELDS_PATTERN,'"$1":"***************"').substring(0,255),writeAuditRow(field,"UPDATE",oldString,newString)}return _lodash.default.isObject(valueOld)||_lodash.default.isObject(valueNew)?(_lodash.default.forEach(valueOld,(valueSub,keySub)=>{checkAudit(field+" "+keySub,"DELETED",valueSub||"",(valueNew||{})[keySub]||"")}),void _lodash.default.forEach(valueNew,(valueSub,keySub)=>{checkAudit(field+" "+keySub,"INSERT",(valueOld||{})[keySub]||"",valueSub||"")})):writeAuditRow(field,type,valueOld,valueNew)}};return _bluebird.default.map([...modelInfo._changed],async field=>{const valueOld=modelInfo._previousDataValues[field]||"",valueNew=modelInfo.dataValues[field]||"";await checkAudit(field,options.type,valueOld,valueNew)})}}
333
+ /**
334
+ * Define a sibling `<ModelName>Audit` table for the given Sequelize model and
335
+ * wire the four lifecycle hooks (`afterCreate`, `afterUpsert`, `afterUpdate`,
336
+ * `afterDestroy`) that record changes into it.
337
+ *
338
+ * Call this **once per model at boot time** from inside the model's
339
+ * `dbSetup()` function. It is idempotent — calling it again for a model that
340
+ * already has an audit table attached is a no-op.
341
+ *
342
+ * The audit table has no foreign key into any user table; the username of the
343
+ * person who made the change is stamped onto each row from the JWT (set by
344
+ * {@link bindAuditRequest}). This keeps the audit util portable across domain
345
+ * services that own different user models.
346
+ *
347
+ * @example
348
+ * // src/customer/customer.js
349
+ * Customer.dbSetup = async function () {
350
+ * await attachAudit(Customer);
351
+ * };
352
+ *
353
+ * @param {import('sequelize').ModelStatic<any>} model the Sequelize model to audit
354
+ * @returns {Promise<void>} resolves when the audit table's `sync()` completes
355
+ * @see {@link bindAuditRequest} - per-request setup that turns audit on
356
+ * @see {@link getAuditModel} - retrieve the audit model from the parent
357
+ * @see {@link getAuditFilter} - build a filter for an audit history query
358
+ */async function attachAudit(model){if(auditModels[model.name])return;const auditModel=model.sequelize.define(model.name+"Audit",function getAuditSchema(model){const DataTypes=model.sequelize.Sequelize;return{field:{type:DataTypes.STRING,allowNull:!1},type:DataTypes.STRING,valueOld:DataTypes.STRING,valueNew:DataTypes.STRING,changedByUser:{type:DataTypes.STRING,allowNull:!1},businessId:{type:DataTypes.STRING,allowNull:!1}}}(model),{updatedAt:!1,freezeTableName:!0});return auditModel.belongsTo(model,{foreignKey:{allowNull:!0},onDelete:"SET NULL"}),auditModels[model.name]=auditModel,model.addHook("afterCreate",auditMe(model.name)),model.addHook("afterUpsert",auditMe(model.name)),model.addHook("afterUpdate",auditMe(model.name)),model.addHook("afterDestroy",auditMe(model.name)),auditModel.sync()}},
350
359
  /***/183(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.watchBucket=
351
360
  /**
352
361
  * bucket watcher watches a s3 bucket and publishes events to a sns topic, this allows you to process files in s3 with a some transformer function
@@ -719,7 +728,9 @@ return errorBody.message=dynamooseError.trim(),errorBody}
719
728
  * @param options {Object} an options object
720
729
  * @returns {{statusCode:number, headers:object, body:string}} a response object
721
730
  */
722
- 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)}
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);"LOG_ALL"!==process.env.UTIL_LOG&&"test"!==process.env.BUILD_ENV&&newBody.statusCode>=500&&
732
+ // Log the raw error object so thrown Error stacks survive (JSON.stringify drops them)
733
+ console.error("-------\x3eUTIL ERROR:",newBody.statusCode,_lodash.default.get(body,"message",""),body);return buildResponse(newBody.statusCode,newBody)}
723
734
  /**
724
735
  * Create a failure response object
725
736
  *
@@ -773,7 +784,7 @@ function success(body,options){return _lodash.default.get(options,"dbClose",_lod
773
784
  /**
774
785
  * @param {string} html
775
786
  * @param {{dbClose:function|undefined}} options
776
- */,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}},
787
+ */,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}},
777
788
  /***/864(__unused_webpack_module,exports,__webpack_require__){Object.defineProperty(exports,"__esModule",{value:!0}),exports.loop=loop,exports.publishEvents=
778
789
  /**
779
790
  * 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