@archerjessop/utilities 4.6.23 → 4.6.25

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.
@@ -1,2 +1,2 @@
1
- const E={ASSIGNMENT_FEE_PERCENTAGE:.05,NET_TO_BUYER_PERCENTAGE:.1,CLOSING_COSTS_PERCENTAGE:.0125,HARD_MONEY_RATE:.03,REHAB_RATE:0,SELLER_AGENT_COMMISSION:.025,BUYER_AGENT_COMMISSION:.025,MINIMUM_COCR15_PRICE:1e4,MAX_COCR15_PRICE_MULTIPLIER:50,CONSERVATIVE_COCR15_PRICE_MULTIPLIER:20,MAX_ITERATIONS:50,CALCULATION_TOLERANCE:.001,ADJUSTMENT_FACTOR:.5,EXPORT_URL_BASE:"https://app.archerjessop.com/property-dashboard/import",EXCLUDED_EXPORT_VALUES:["Loading...","Not found","",null,void 0],DASHBOARD_URL_BASE:"https://app.archerjessop.com/property-dashboard/"},{ASSIGNMENT_FEE_PERCENTAGE:_,NET_TO_BUYER_PERCENTAGE:R,CLOSING_COSTS_PERCENTAGE:C,HARD_MONEY_RATE:T,SELLER_AGENT_COMMISSION:A,BUYER_AGENT_COMMISSION:I,MINIMUM_COCR15_PRICE:N,MAX_COCR15_PRICE_MULTIPLIER:O,CONSERVATIVE_COCR15_PRICE_MULTIPLIER:S,REHAB_RATE:M}=E;export{_ as ASSIGNMENT_FEE_PERCENTAGE,E as BUSINESS_CONSTANTS,I as BUYER_AGENT_COMMISSION,C as CLOSING_COSTS_PERCENTAGE,S as CONSERVATIVE_COCR15_PRICE_MULTIPLIER,T as HARD_MONEY_RATE,O as MAX_COCR15_PRICE_MULTIPLIER,N as MINIMUM_COCR15_PRICE,R as NET_TO_BUYER_PERCENTAGE,M as REHAB_RATE,A as SELLER_AGENT_COMMISSION};
1
+ import{getEnvVar as E}from"../environment/utilities.js";const _={ASSIGNMENT_FEE_PERCENTAGE:.05,NET_TO_BUYER_PERCENTAGE:.1,CLOSING_COSTS_PERCENTAGE:.0125,HARD_MONEY_RATE:.03,REHAB_RATE:0,SELLER_AGENT_COMMISSION:.025,BUYER_AGENT_COMMISSION:.025,MINIMUM_COCR15_PRICE:1e4,MAX_COCR15_PRICE_MULTIPLIER:50,CONSERVATIVE_COCR15_PRICE_MULTIPLIER:20,MAX_ITERATIONS:50,CALCULATION_TOLERANCE:.001,ADJUSTMENT_FACTOR:.5,EXPORT_URL_BASE:E("EXPORT_URL_BASE","https://app.archerjessop.com/property-dashboard/import"),EXCLUDED_EXPORT_VALUES:["Loading...","Not found","",null,void 0],DASHBOARD_URL_BASE:E("DASHBOARD_URL_BASE","https://app.archerjessop.com/property-dashboard/")},{ASSIGNMENT_FEE_PERCENTAGE:R,NET_TO_BUYER_PERCENTAGE:A,CLOSING_COSTS_PERCENTAGE:C,HARD_MONEY_RATE:T,SELLER_AGENT_COMMISSION:I,BUYER_AGENT_COMMISSION:N,MINIMUM_COCR15_PRICE:O,MAX_COCR15_PRICE_MULTIPLIER:S,CONSERVATIVE_COCR15_PRICE_MULTIPLIER:M,REHAB_RATE:L}=_;export{R as ASSIGNMENT_FEE_PERCENTAGE,_ as BUSINESS_CONSTANTS,N as BUYER_AGENT_COMMISSION,C as CLOSING_COSTS_PERCENTAGE,M as CONSERVATIVE_COCR15_PRICE_MULTIPLIER,T as HARD_MONEY_RATE,S as MAX_COCR15_PRICE_MULTIPLIER,O as MINIMUM_COCR15_PRICE,A as NET_TO_BUYER_PERCENTAGE,L as REHAB_RATE,I as SELLER_AGENT_COMMISSION};
2
2
  //# sourceMappingURL=business.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"business.js","sources":["../../src/config/business.js"],"sourcesContent":["/**\r\n * Business logic and fee constants\r\n */\r\n\r\nexport const BUSINESS_CONSTANTS = {\r\n // Assignment and transaction fees\r\n ASSIGNMENT_FEE_PERCENTAGE: 0.05, // 5% assignment fee\r\n NET_TO_BUYER_PERCENTAGE: 0.10, // 10% net to buyer\r\n CLOSING_COSTS_PERCENTAGE: 0.0125, // 1.25% closing costs\r\n HARD_MONEY_RATE: 0.03, // 3% financing fee\r\n REHAB_RATE: 0.0, // 0% rehab fee\r\n \r\n // Agent commissions\r\n SELLER_AGENT_COMMISSION: 0.025, // 3% seller agent commission\r\n BUYER_AGENT_COMMISSION: 0.025, // 3% buyer agent commission\r\n \r\n // COCR15 price calculation limits\r\n MINIMUM_COCR15_PRICE: 10000, // $10,000 minimum\r\n MAX_COCR15_PRICE_MULTIPLIER: 50, // Maximum price as multiple of NOI\r\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER: 20, // Conservative price limit\r\n \r\n // Algorithm parameters (duplicated for business logic context)\r\n\r\n \r\n // Calculation parameters\r\n MAX_ITERATIONS: 50, // Maximum iterations for iterative calculations\r\n CALCULATION_TOLERANCE: 0.001, // Tolerance for convergence\r\n ADJUSTMENT_FACTOR: 0.5, // Adjustment factor for iterations\r\n\r\n\r\n \r\n // URL constants\r\n EXPORT_URL_BASE: \"https://app.archerjessop.com/property-dashboard/import\",\r\n EXCLUDED_EXPORT_VALUES: [\"Loading...\", \"Not found\", \"\", null, undefined],\r\n\r\n DASHBOARD_URL_BASE: \"https://app.archerjessop.com/property-dashboard/\",\r\n \r\n};\r\n\r\n// Convenience exports for commonly used values\r\nexport const {\r\n ASSIGNMENT_FEE_PERCENTAGE,\r\n NET_TO_BUYER_PERCENTAGE,\r\n CLOSING_COSTS_PERCENTAGE,\r\n HARD_MONEY_RATE,\r\n SELLER_AGENT_COMMISSION,\r\n BUYER_AGENT_COMMISSION,\r\n MINIMUM_COCR15_PRICE,\r\n MAX_COCR15_PRICE_MULTIPLIER,\r\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER,\r\n REHAB_RATE,\r\n} = BUSINESS_CONSTANTS;\r\n"],"names":["BUSINESS_CONSTANTS","ASSIGNMENT_FEE_PERCENTAGE","NET_TO_BUYER_PERCENTAGE","CLOSING_COSTS_PERCENTAGE","HARD_MONEY_RATE","REHAB_RATE","SELLER_AGENT_COMMISSION","BUYER_AGENT_COMMISSION","MINIMUM_COCR15_PRICE","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MAX_ITERATIONS","CALCULATION_TOLERANCE","ADJUSTMENT_FACTOR","EXPORT_URL_BASE","EXCLUDED_EXPORT_VALUES","undefined","DASHBOARD_URL_BASE"],"mappings":"AAIY,MAACA,EAAqB,CAEhCC,0BAA2B,IAC3BC,wBAAyB,GACzBC,yBAA0B,MAC1BC,gBAAiB,IACjBC,WAAY,EAGZC,wBAAyB,KACzBC,uBAAwB,KAGxBC,qBAAsB,IACtBC,4BAA6B,GAC7BC,qCAAsC,GAMtCC,eAAgB,GAChBC,sBAAuB,KACvBC,kBAAmB,GAKnBC,gBAAiB,yDACjBC,uBAAwB,CAAC,aAAc,YAAa,GAAI,UAAMC,GAE9DC,mBAAoB,qDAKThB,0BACXA,EAAyBC,wBACzBA,EAAuBC,yBACvBA,EAAwBC,gBACxBA,EAAeE,wBACfA,EAAuBC,uBACvBA,EAAsBC,qBACtBA,EAAoBC,4BACpBA,EAA2BC,qCAC3BA,EAAoCL,WACpCA,GACEL"}
1
+ {"version":3,"file":"business.js","sources":["../../src/config/business.js"],"sourcesContent":["/**\r\n * Business logic and fee constants\r\n */\r\n\r\nimport { getEnvVar } from \"../environment/utilities.js\";\r\n\r\nexport const BUSINESS_CONSTANTS = {\r\n // Assignment and transaction fees\r\n ASSIGNMENT_FEE_PERCENTAGE: 0.05, // 5% assignment fee\r\n NET_TO_BUYER_PERCENTAGE: 0.10, // 10% net to buyer\r\n CLOSING_COSTS_PERCENTAGE: 0.0125, // 1.25% closing costs\r\n HARD_MONEY_RATE: 0.03, // 3% financing fee\r\n REHAB_RATE: 0.0, // 0% rehab fee\r\n \r\n // Agent commissions\r\n SELLER_AGENT_COMMISSION: 0.025, // 3% seller agent commission\r\n BUYER_AGENT_COMMISSION: 0.025, // 3% buyer agent commission\r\n \r\n // COCR15 price calculation limits\r\n MINIMUM_COCR15_PRICE: 10000, // $10,000 minimum\r\n MAX_COCR15_PRICE_MULTIPLIER: 50, // Maximum price as multiple of NOI\r\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER: 20, // Conservative price limit\r\n \r\n // Algorithm parameters (duplicated for business logic context)\r\n\r\n \r\n // Calculation parameters\r\n MAX_ITERATIONS: 50, // Maximum iterations for iterative calculations\r\n CALCULATION_TOLERANCE: 0.001, // Tolerance for convergence\r\n ADJUSTMENT_FACTOR: 0.5, // Adjustment factor for iterations\r\n\r\n\r\n \r\n // URL constants - safe for both Node.js and browser\r\n EXPORT_URL_BASE: getEnvVar(\r\n \"EXPORT_URL_BASE\",\r\n \"https://app.archerjessop.com/property-dashboard/import\"\r\n ),\r\n EXCLUDED_EXPORT_VALUES: [\"Loading...\", \"Not found\", \"\", null, undefined],\r\n DASHBOARD_URL_BASE: getEnvVar(\r\n \"DASHBOARD_URL_BASE\",\r\n \"https://app.archerjessop.com/property-dashboard/\"\r\n ),\r\n \r\n};\r\n\r\n// Convenience exports for commonly used values\r\nexport const {\r\n ASSIGNMENT_FEE_PERCENTAGE,\r\n NET_TO_BUYER_PERCENTAGE,\r\n CLOSING_COSTS_PERCENTAGE,\r\n HARD_MONEY_RATE,\r\n SELLER_AGENT_COMMISSION,\r\n BUYER_AGENT_COMMISSION,\r\n MINIMUM_COCR15_PRICE,\r\n MAX_COCR15_PRICE_MULTIPLIER,\r\n CONSERVATIVE_COCR15_PRICE_MULTIPLIER,\r\n REHAB_RATE,\r\n} = BUSINESS_CONSTANTS;\r\n"],"names":["BUSINESS_CONSTANTS","ASSIGNMENT_FEE_PERCENTAGE","NET_TO_BUYER_PERCENTAGE","CLOSING_COSTS_PERCENTAGE","HARD_MONEY_RATE","REHAB_RATE","SELLER_AGENT_COMMISSION","BUYER_AGENT_COMMISSION","MINIMUM_COCR15_PRICE","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MAX_ITERATIONS","CALCULATION_TOLERANCE","ADJUSTMENT_FACTOR","EXPORT_URL_BASE","getEnvVar","EXCLUDED_EXPORT_VALUES","undefined","DASHBOARD_URL_BASE"],"mappings":"wDAMY,MAACA,EAAqB,CAEhCC,0BAA2B,IAC3BC,wBAAyB,GACzBC,yBAA0B,MAC1BC,gBAAiB,IACjBC,WAAY,EAGZC,wBAAyB,KACzBC,uBAAwB,KAGxBC,qBAAsB,IACtBC,4BAA6B,GAC7BC,qCAAsC,GAMtCC,eAAgB,GAChBC,sBAAuB,KACvBC,kBAAmB,GAKnBC,gBAAiBC,EACf,kBACA,0DAEFC,uBAAwB,CAAC,aAAc,YAAa,GAAI,UAAMC,GAC9DC,mBAAoBH,EAClB,qBACA,sDAMSd,0BACXA,EAAyBC,wBACzBA,EAAuBC,yBACvBA,EAAwBC,gBACxBA,EAAeE,wBACfA,EAAuBC,uBACvBA,EAAsBC,qBACtBA,EAAoBC,4BACpBA,EAA2BC,qCAC3BA,EAAoCL,WACpCA,GACEL"}
@@ -1,2 +1,2 @@
1
- function calculateDOM(t){if(!t||"Not found"===t)return"Not found";try{let e;const a=t.match(/(\d{1,2})\/(\d{1,2})\/(\d{4})/);if(a){const[,t,n,r]=a;e=new Date(r,t-1,n)}else e=new Date(t);if(isNaN(e.getTime()))return"Invalid date";const n=new Date-e,r=Math.floor(n/864e5);return`${r} (${(t=>`${(t.getMonth()+1).toString().padStart(2,"0")}/${t.getDate().toString().padStart(2,"0")}/${t.getFullYear()}`)(e)})`}catch(t){return"Calculation error"}}export{calculateDOM};
1
+ function formatDate(t){return`${(t.getMonth()+1).toString().padStart(2,"0")}/${t.getDate().toString().padStart(2,"0")}/${t.getFullYear()}`}function calculateDOM(t,e=!0){if(!t||"Not found"===t)return"Not found";try{let a;const r=t.match(/(\d{1,2})\/(\d{1,2})\/(\d{4})/);if(r){const[,t,e,n]=r;a=new Date(n,t-1,e)}else a=new Date(t);if(isNaN(a.getTime()))return"Invalid date";const n=new Date-a,o=Math.floor(n/864e5);return e?`${o} (${formatDate(a)})`:o}catch(t){return"Calculation error"}}export{calculateDOM,formatDate};
2
2
  //# sourceMappingURL=utilities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.js","sources":["../../src/date/utilities.js"],"sourcesContent":["/**\r\n * DATE utilities\r\n */\r\n\r\nexport function calculateDOM(listingDateText) {\r\n if (!listingDateText || listingDateText === \"Not found\") {\r\n return \"Not found\";\r\n }\r\n\r\n try {\r\n // Parse various date formats\r\n let listingDate;\r\n \r\n // Try parsing MM/DD/YYYY format\r\n const mmddyyyyMatch = listingDateText.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\r\n if (mmddyyyyMatch) {\r\n const [, month, day, year] = mmddyyyyMatch;\r\n listingDate = new Date(year, month - 1, day);\r\n } else {\r\n // Try parsing other common date formats\r\n listingDate = new Date(listingDateText);\r\n }\r\n\r\n if (isNaN(listingDate.getTime())) {\r\n return \"Invalid date\";\r\n }\r\n\r\n // Calculate days difference\r\n const today = new Date();\r\n const diffTime = today - listingDate;\r\n const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));\r\n\r\n // Format the original date as MM/DD/YYYY\r\n const formatDate = (date) => {\r\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\r\n const day = date.getDate().toString().padStart(2, \"0\");\r\n const year = date.getFullYear();\r\n return `${month}/${day}/${year}`;\r\n };\r\n\r\n return `${diffDays} (${formatDate(listingDate)})`;\r\n } catch (error) {\r\n return \"Calculation error\";\r\n }\r\n}\r\n\r\n"],"names":["calculateDOM","listingDateText","listingDate","mmddyyyyMatch","match","month","day","year","Date","isNaN","getTime","diffTime","diffDays","Math","floor","date","getMonth","toString","padStart","getDate","getFullYear","formatDate","error"],"mappings":"AAIO,SAASA,aAAaC,GAC3B,IAAKA,GAAuC,cAApBA,EACtB,MAAO,YAGT,IAEE,IAAIC,EAGJ,MAAMC,EAAgBF,EAAgBG,MAAM,iCAC5C,GAAID,EAAe,CACjB,MAAM,CAAGE,EAAOC,EAAKC,GAAQJ,EAC7BD,EAAc,IAAIM,KAAKD,EAAMF,EAAQ,EAAGC,EAC1C,MAEEJ,EAAc,IAAIM,KAAKP,GAGzB,GAAIQ,MAAMP,EAAYQ,WACpB,MAAO,eAIT,MACMC,EADQ,IAAIH,KACON,EACnBU,EAAWC,KAAKC,MAAMH,EAAQ,OAUpC,MAAO,GAAGC,MAPS,CAACG,GAIX,IAHQA,EAAKC,WAAa,GAAGC,WAAWC,SAAS,EAAG,QAC/CH,EAAKI,UAAUF,WAAWC,SAAS,EAAG,QACrCH,EAAKK,gBAIGC,CAAWnB,KACpC,CAAE,MAAOoB,GACP,MAAO,mBACT,CACF"}
1
+ {"version":3,"file":"utilities.js","sources":["../../src/date/utilities.js"],"sourcesContent":["/**\r\n * DATE utilities\r\n */\r\n\r\nexport function formatDate(date) {\r\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\r\n const day = date.getDate().toString().padStart(2, \"0\");\r\n const year = date.getFullYear();\r\n return `${month}/${day}/${year}`;\r\n}\r\n\r\nexport function calculateDOM(listingDateText, returnFormattedDate = true) {\r\n if (!listingDateText || listingDateText === \"Not found\") {\r\n return \"Not found\";\r\n }\r\n\r\n try {\r\n // Parse various date formats\r\n let listingDate;\r\n \r\n // Try parsing MM/DD/YYYY format\r\n const mmddyyyyMatch = listingDateText.match(/(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\r\n if (mmddyyyyMatch) {\r\n const [, month, day, year] = mmddyyyyMatch;\r\n listingDate = new Date(year, month - 1, day);\r\n } else {\r\n // Try parsing other common date formats\r\n listingDate = new Date(listingDateText);\r\n }\r\n\r\n if (isNaN(listingDate.getTime())) {\r\n return \"Invalid date\";\r\n }\r\n\r\n // Calculate days difference\r\n const today = new Date();\r\n const diffTime = today - listingDate;\r\n const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));\r\n\r\n if(returnFormattedDate) {\r\n return `${diffDays} (${formatDate(listingDate)})`;\r\n }\r\n else {\r\n return diffDays;\r\n }\r\n } catch (error) {\r\n return \"Calculation error\";\r\n }\r\n}\r\n\r\n"],"names":["formatDate","date","getMonth","toString","padStart","getDate","getFullYear","calculateDOM","listingDateText","returnFormattedDate","listingDate","mmddyyyyMatch","match","month","day","year","Date","isNaN","getTime","diffTime","diffDays","Math","floor","error"],"mappings":"AAIO,SAASA,WAAWC,GAIzB,MAAO,IAHQA,EAAKC,WAAa,GAAGC,WAAWC,SAAS,EAAG,QAC/CH,EAAKI,UAAUF,WAAWC,SAAS,EAAG,QACrCH,EAAKK,eAEpB,CAEO,SAASC,aAAaC,EAAiBC,GAAsB,GAClE,IAAKD,GAAuC,cAApBA,EACtB,MAAO,YAGT,IAEE,IAAIE,EAGJ,MAAMC,EAAgBH,EAAgBI,MAAM,iCAC5C,GAAID,EAAe,CACjB,MAAM,CAAGE,EAAOC,EAAKC,GAAQJ,EAC7BD,EAAc,IAAIM,KAAKD,EAAMF,EAAQ,EAAGC,EAC1C,MAEEJ,EAAc,IAAIM,KAAKR,GAGzB,GAAIS,MAAMP,EAAYQ,WACpB,MAAO,eAIT,MACMC,EADQ,IAAIH,KACON,EACnBU,EAAWC,KAAKC,MAAMH,EAAQ,OAEpC,OAAGV,EACM,GAAGW,MAAapB,WAAWU,MAG3BU,CAEX,CAAE,MAAOG,GACP,MAAO,mBACT,CACF"}
@@ -0,0 +1,2 @@
1
+ const getEnvVar=(e,r)=>{try{if("undefined"!=typeof process&&void 0!==process.env&&process.env[e])return process.env[e]}catch(e){}return r},isNodeEnvironment=()=>{try{return"undefined"!=typeof process&&void 0!==process.versions&&void 0!==process.versions.node}catch(e){return!1}},isBrowserEnvironment=()=>{try{return"undefined"!=typeof window&&"undefined"!=typeof document}catch(e){return!1}};export{getEnvVar,isBrowserEnvironment,isNodeEnvironment};
2
+ //# sourceMappingURL=utilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utilities.js","sources":["../../src/environment/utilities.js"],"sourcesContent":["// src/environment/utilities.js\r\n\r\n/**\r\n * Safely get environment variable (Node.js only)\r\n * Returns defaultValue in browser environments or if variable is not set\r\n * \r\n * @param {string} key - Environment variable name\r\n * @param {*} defaultValue - Default value to return if not found\r\n * @returns {*} Environment variable value or default\r\n * \r\n * @example\r\n * const apiUrl = getEnvVar('API_URL', 'https://api.example.com');\r\n */\r\nexport const getEnvVar = (key, defaultValue) => {\r\n try {\r\n // Check if we're in Node.js environment\r\n if (typeof process !== 'undefined' && \r\n process.env !== undefined && \r\n process.env[key]) {\r\n return process.env[key];\r\n }\r\n } catch (e) {\r\n // Silently fall back to default if anything fails\r\n }\r\n return defaultValue;\r\n};\r\n\r\n/**\r\n * Check if running in Node.js environment\r\n * \r\n * @returns {boolean} True if Node.js, false if browser\r\n * \r\n * @example\r\n * if (isNodeEnvironment()) {\r\n * // Node.js specific code\r\n * }\r\n */\r\nexport const isNodeEnvironment = () => {\r\n try {\r\n return typeof process !== 'undefined' && \r\n process.versions !== undefined && \r\n process.versions.node !== undefined;\r\n } catch (e) {\r\n return false;\r\n }\r\n};\r\n\r\n/**\r\n * Check if running in browser environment\r\n * \r\n * @returns {boolean} True if browser, false if Node.js\r\n */\r\nexport const isBrowserEnvironment = () => {\r\n try {\r\n return typeof window !== 'undefined' && \r\n typeof document !== 'undefined';\r\n } catch (e) {\r\n return false;\r\n }\r\n};"],"names":["getEnvVar","key","defaultValue","process","undefined","env","e","isNodeEnvironment","versions","node","isBrowserEnvironment","window","document"],"mappings":"AAaY,MAACA,UAAY,CAACC,EAAKC,KAC7B,IAEE,GAAuB,oBAAZC,cACSC,IAAhBD,QAAQE,KACRF,QAAQE,IAAIJ,GACd,OAAOE,QAAQE,IAAIJ,EAEvB,CAAE,MAAOK,GAET,CACA,OAAOJ,GAaIK,kBAAoB,KAC/B,IACE,MAA0B,oBAAZJ,cACcC,IAArBD,QAAQK,eACkBJ,IAA1BD,QAAQK,SAASC,IAC1B,CAAE,MAAOH,GACP,OAAO,CACT,GAQWI,qBAAuB,KAClC,IACE,MAAyB,oBAAXC,QACa,oBAAbC,QAChB,CAAE,MAAON,GACP,OAAO,CACT"}
@@ -1,2 +1,2 @@
1
- import{FINANCIAL_CONSTANTS as t}from"../config/financial.js";import{BUSINESS_CONSTANTS as e}from"../config/business.js";import{PROPERTY_TYPES as c,PROPERTY_TYPE_CONSTANTS as a}from"../config/property-types.js";function calculatePMT(t,e,c){if(0===e)return t/(12*c);const a=e/12,r=12*c;return t*(a*Math.pow(1+a,r))/(Math.pow(1+a,r)-1)}function calculateCOCR30(t,e){try{const c=.3*t,a=12*calculatePMT(.7*t,.075,30);return(e-a)/c*100}catch(t){return 0}}function calculateCashFlowYield(t,e){if(!e||e<=0)return 0;return 12*t/e*100}function calculatePriceForCOCR(c,a=.15,r={}){const{downPercent:n=100*t.DEFAULT_DOWN_PAYMENT,dscrLtvPercent:l=100*t.DEFAULT_DSCR_PERCENTAGE,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:E=t.DSCR_AMORTIZATION,maxIterations:o=e.MAX_ITERATIONS,tolerance:T=e.CALCULATION_TOLERANCE}=r;try{let t=c/.08,r=0;for(;r<o;){const o=t*(n/100),R=12*calculatePMT(t*(l/100),u,E),s=(c-R)/o;if(Math.abs(s-a)<T)break;const A=s-a,_=A*e.ADJUSTMENT_FACTOR;t*=A>0?1+Math.abs(_):1-Math.abs(_),t<1e3&&(t=1e3),t>c*e.MAX_COCR15_PRICE_MULTIPLIER&&(t=c*e.CONSERVATIVE_COCR15_PRICE_MULTIPLIER),r++}return t<e.MINIMUM_COCR15_PRICE&&(t=e.MINIMUM_COCR15_PRICE),t}catch(t){return 0}}function calculateCOCRAtPercent(e,c,a,r={}){const{dscrRate:n=t.DSCR_INTEREST_RATE,dscrTerm:l=t.DSCR_AMORTIZATION}=r;try{const t=e*(a/100),r=12*calculatePMT(e-t,n,l);return(c-r)/t*100}catch(t){return 0}}function calculateNOIByType(t,e,r=c.MULTIFAMILY,n={}){const{strGrossIncomeMultiplier:l=a.STR.ESTIMATED_GROSS_RATE,strNoiPercentage:u=a.STR.NOI_PERCENTAGE,assistedIncomePerBedroom:E=a.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,bedroomCount:o=a.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT}=n;try{switch(r.toLowerCase()){case c.STR:return t*l*u;case c.ASSISTED_LIVING:return o*E*12;case c.MULTIFAMILY:default:return t*e}}catch(t){return 0}}function calculateAssignmentFee(t,c=100*e.ASSIGNMENT_FEE_PERCENTAGE){try{return t*(c/100)}catch(t){return 0}}function calculateNetToBuyer(c,a={}){const{buyerCostPercent:r=100*e.NET_TO_BUYER_PERCENTAGE,sellerCostAssignment:n=100*e.ASSIGNMENT_FEE_PERCENTAGE,sellerCostClosing:l=100*e.CLOSING_COSTS_PERCENTAGE,additionalCostRehab:u=100*e.REHAB_RATE,additionalCostFinancing:E=100*e.HARD_MONEY_RATE,dscrLtvPercent:o=100*t.DEFAULT_DSCR_PERCENTAGE}=a;try{return c*(r/100)-c*((n+l)/100)-c*(u/100)-E/100*(c-c*(o/100))}catch(t){return 0}}function calculateBalloonBalance(e,c,a,r=t.DEFAULT_BALLOON_PERIOD_YEARS){try{if(e<=0||c<0||a<=0||r<=0)return 0;if(r>=a)return 0;if(0===c){const t=12*a;return e*(t-12*r)/t}const t=c/12,n=12*a,l=12*r,u=Math.pow(1+t,n),E=e*(u-Math.pow(1+t,l))/(u-1);return Math.max(0,E)}catch(t){return 0}}function calculateAppreciatedValue(e,c=t.APPRECIATION_RATE,a=t.DEFAULT_BALLOON_PERIOD_YEARS){try{return e<=0||c<0||a<0?e:e*Math.pow(1+c,a)}catch(t){return e}}function calculateCashOutAfterRefi(e,c,a,r={}){const{appreciationRate:n=t.APPRECIATION_RATE,balloonYears:l=t.DEFAULT_BALLOON_PERIOD_YEARS,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:E=t.DSCR_AMORTIZATION,sellerFiTerm:o=t.SELLER_FI_AMORTIZATION,refiLtvPercent:T=70}=r;try{const r=calculateAppreciatedValue(e,n,l),R=calculateBalloonBalance(c,u,E,l),s=calculateBalloonBalance(a,t.SELLER_FI_INTEREST_RATE,o,l);return r*(T/100)-(R+s)}catch(t){return 0}}function calculateDscrPayment(t,e,c,a){return calculatePMT(t*(e/100),c,a)}function calculateSfPayment(t,e,c,a){return calculatePMT(t*(e/100),c,a)}function calculateCashFlow(t,e,c){return t-(e+c)}function calculateDiscountFromPrice(t,e){return!t||t<=0?0:(t-e)/t}function calculatePriceFromDiscount(t,e){return!t||t<=0?0:t*(1-e)}function safePercentage(t,e=100){return null==t||isNaN(t)?e:100*t}function calculateMonthlyCashFlow(e,c,a=t.DEFAULT_DOWN_PAYMENT,r=t.SELLER_FI_INTEREST_RATE,n=t.SELLER_FI_AMORTIZATION,l=0){try{if(!e||!c||e<=0||c<=0)return 0;const u=c/12,E=e*t.DEFAULT_DSCR_PERCENTAGE,o=e*(1-a-t.DEFAULT_DSCR_PERCENTAGE),T=calculatePMT(E,t.DSCR_INTEREST_RATE,30);let R=0;R=r>0?calculatePMT(o,r,n):o/(12*n);let s=R;if(l>0&&r>0){s=o*(r/12)}return u-(T+s)}catch(t){return console.error("Error calculating monthly cash flow:",t),0}}function calculateAssignmentFeeFromDecimal(t,e=.05){try{return!t||t<=0||!e?0:t*e}catch(t){return console.error("Error calculating assignment fee:",t),0}}function calculateDscrMonthlyPayment(e){try{if(!e||e<=0)return 0;return calculatePMT(e*t.DEFAULT_DSCR_PERCENTAGE,t.DSCR_INTEREST_RATE,30)}catch(t){return console.error("Error calculating DSCR payment:",t),0}}function calculateSellerFiMonthlyPayment(e,c=t.DEFAULT_DOWN_PAYMENT,a=t.SELLER_FI_INTEREST_RATE,r=t.SELLER_FI_AMORTIZATION){try{if(!e||e<=0)return 0;const n=e*(1-c-t.DEFAULT_DSCR_PERCENTAGE);return a>0?calculatePMT(n,a,r):n/(12*r)}catch(t){return console.error("Error calculating seller fi payment:",t),0}}export{calculateAppreciatedValue,calculateAssignmentFee,calculateAssignmentFeeFromDecimal,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateDscrMonthlyPayment,calculateDscrPayment,calculateMonthlyCashFlow,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,calculateSellerFiMonthlyPayment,calculateSfPayment,safePercentage};
1
+ import{FINANCIAL_CONSTANTS as t}from"../config/financial.js";import{BUSINESS_CONSTANTS as e}from"../config/business.js";import{PROPERTY_TYPES as c,PROPERTY_TYPE_CONSTANTS as a}from"../config/property-types.js";function calculatePMT(t,e,c){if(0===e)return t/(12*c);const a=e/12,r=12*c;return t*(a*Math.pow(1+a,r))/(Math.pow(1+a,r)-1)}function calculateCOCR30(t,e){try{const c=.3*t,a=12*calculatePMT(.7*t,.075,30);return(e-a)/c*100}catch(t){return 0}}function calculateCashFlowYield(t,e){if(!e||e<=0)return 0;return 12*t/e*100}function calculatePriceForCOCR(c,a=.15,r={}){const{downPercent:n=100*t.DEFAULT_DOWN_PAYMENT,dscrLtvPercent:l=100*t.DEFAULT_DSCR_PERCENTAGE,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:o=t.DSCR_AMORTIZATION,maxIterations:E=e.MAX_ITERATIONS,tolerance:T=e.CALCULATION_TOLERANCE}=r;try{let t=c/.08,r=0;for(;r<E;){const E=t*(n/100),R=12*calculatePMT(t*(l/100),u,o),s=(c-R)/E;if(Math.abs(s-a)<T)break;const A=s-a,i=A*e.ADJUSTMENT_FACTOR;t*=A>0?1+Math.abs(i):1-Math.abs(i),t<1e3&&(t=1e3),t>c*e.MAX_COCR15_PRICE_MULTIPLIER&&(t=c*e.CONSERVATIVE_COCR15_PRICE_MULTIPLIER),r++}return t<e.MINIMUM_COCR15_PRICE&&(t=e.MINIMUM_COCR15_PRICE),t}catch(t){return 0}}function calculateCOCRAtPercent(e,c,a,r={}){const{dscrRate:n=t.DSCR_INTEREST_RATE,dscrTerm:l=t.DSCR_AMORTIZATION}=r;try{const t=e*(a/100),r=12*calculatePMT(e-t,n,l);return(c-r)/t*100}catch(t){return 0}}function calculateNOIByType(t,e,r=c.MULTIFAMILY,n={}){const{strGrossIncomeMultiplier:l=a.STR.ESTIMATED_GROSS_RATE,strNoiPercentage:u=a.STR.NOI_PERCENTAGE,assistedIncomePerBedroom:o=a.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,bedroomCount:E=a.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT}=n;try{switch(r.toLowerCase()){case c.STR:return t*l*u;case c.ASSISTED_LIVING:return E*o*12;case c.MULTIFAMILY:default:return t*e}}catch(t){return 0}}function calculateAssignmentFee(t,c=100*e.ASSIGNMENT_FEE_PERCENTAGE){try{return t*(c/100)}catch(t){return 0}}function calculateNetToBuyer(c,a={}){const{buyerCostPercent:r=100*e.NET_TO_BUYER_PERCENTAGE,sellerCostAssignment:n=100*e.ASSIGNMENT_FEE_PERCENTAGE,sellerCostClosing:l=100*e.CLOSING_COSTS_PERCENTAGE,additionalCostRehab:u=100*e.REHAB_RATE,additionalCostFinancing:o=100*e.HARD_MONEY_RATE,dscrLtvPercent:E=100*t.DEFAULT_DSCR_PERCENTAGE}=a;try{return c*(r/100)-c*((n+l)/100)-c*(u/100)-o/100*(c-c*(E/100))}catch(t){return 0}}function calculateBalloonBalance(e,c,a,r=t.DEFAULT_BALLOON_PERIOD_YEARS){try{if(e<=0||c<0||a<=0||r<=0)return 0;if(r>=a)return 0;if(0===c){const t=12*a;return e*(t-12*r)/t}const t=c/12,n=12*a,l=12*r,u=Math.pow(1+t,n),o=e*(u-Math.pow(1+t,l))/(u-1);return Math.max(0,o)}catch(t){return 0}}function calculateAppreciatedValue(e,c=t.APPRECIATION_RATE,a=t.DEFAULT_BALLOON_PERIOD_YEARS){try{return e<=0||c<0||a<0?e:e*Math.pow(1+c,a)}catch(t){return e}}function calculateCashOutAfterRefi(e,c,a,r={}){const{appreciationRate:n=t.APPRECIATION_RATE,balloonYears:l=t.DEFAULT_BALLOON_PERIOD_YEARS,dscrRate:u=t.DSCR_INTEREST_RATE,dscrTerm:o=t.DSCR_AMORTIZATION,sellerFiTerm:E=t.SELLER_FI_AMORTIZATION,refiLtvPercent:T=70}=r;try{const r=calculateAppreciatedValue(e,n,l),R=calculateBalloonBalance(c,u,o,l),s=calculateBalloonBalance(a,t.SELLER_FI_INTEREST_RATE,E,l);return r*(T/100)-(R+s)}catch(t){return 0}}function calculateCashFlow(t,e,c){return t-(e+c)}function calculateDiscountFromPrice(t,e){return!t||t<=0?0:(t-e)/t}function calculatePriceFromDiscount(t,e){return!t||t<=0?0:t*(1-e)}function safePercentage(t,e=100){return null==t||isNaN(t)?e:100*t}export{calculateAppreciatedValue,calculateAssignmentFee,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,safePercentage};
2
2
  //# sourceMappingURL=calculations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"calculations.js","sources":["../../src/financial/calculations.js"],"sourcesContent":["// src/financial/calculations.js\r\n\r\nimport { FINANCIAL_CONSTANTS } from '../config/financial.js';\r\nimport { BUSINESS_CONSTANTS } from '../config/business.js';\r\nimport { PROPERTY_TYPE_CONSTANTS, PROPERTY_TYPES } from '../config/property-types.js';\r\n\r\n\r\n/**\r\n * PMT function for loan payment calculation\r\n * @param {number} principal - Loan principal amount \r\n * @param {number} annualRate - Annual interest rate (as decimal, e.g., 0.075 for 7.5%)\r\n * @param {number} years - Loan term in years\r\n * @returns {number} Monthly payment amount\r\n */\r\nexport function calculatePMT(principal, annualRate, years) {\r\n if (annualRate === 0) {\r\n return principal / (years * 12);\r\n }\r\n \r\n const monthlyRate = annualRate / 12;\r\n const numPayments = years * 12;\r\n const pmt = principal * (monthlyRate * Math.pow(1 + monthlyRate, numPayments)) / \r\n (Math.pow(1 + monthlyRate, numPayments) - 1);\r\n return pmt;\r\n}\r\n\r\nexport function calculateCOCR30(askingPrice, noi) {\r\n try {\r\n const cashInvested = askingPrice * 0.30; // 30% down payment\r\n const dscrLoanAmount = askingPrice * 0.70; // Fixed 70% DSCR loan\r\n const dscrPayment = calculatePMT(dscrLoanAmount, 0.075, 30) * 12; // Annual DSCR payment\r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\nexport function calculateCashFlowYield(monthlyCashFlow, purchasePrice) {\r\n if (!purchasePrice || purchasePrice <= 0) return 0;\r\n const annualCashFlow = monthlyCashFlow * 12;\r\n return (annualCashFlow / purchasePrice) * 100;\r\n}\r\n\r\n\r\n/**\r\n * Calculate the property price that yields a target COCR percentage\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} targetCOCR - Target COCR as decimal (default: 0.15 for 15%)\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated property price\r\n */\r\nexport function calculatePriceForCOCR(noi, targetCOCR = 0.15, options = {}) {\r\n const {\r\n downPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n maxIterations = BUSINESS_CONSTANTS.MAX_ITERATIONS,\r\n tolerance = BUSINESS_CONSTANTS.CALCULATION_TOLERANCE\r\n } = options;\r\n\r\n try {\r\n let targetPrice = noi / 0.08; // Initial estimate: NOI / 8% cap rate\r\n let iterations = 0;\r\n \r\n while (iterations < maxIterations) {\r\n const cashInvested = targetPrice * (downPercent / 100);\r\n const dscrLoanAmount = targetPrice * (dscrLtvPercent / 100);\r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n const annualCashFlow = noi - dscrPayment;\r\n const currentCOCR = annualCashFlow / cashInvested;\r\n \r\n if (Math.abs(currentCOCR - targetCOCR) < tolerance) {\r\n break;\r\n }\r\n \r\n const error = currentCOCR - targetCOCR;\r\n const adjustment = error * BUSINESS_CONSTANTS.ADJUSTMENT_FACTOR;\r\n \r\n if (error > 0) {\r\n targetPrice = targetPrice * (1 + Math.abs(adjustment));\r\n } else {\r\n targetPrice = targetPrice * (1 - Math.abs(adjustment));\r\n }\r\n \r\n // Reasonable bounds during iteration (prevent extreme values)\r\n if (targetPrice < 1000) targetPrice = 1000;\r\n if (targetPrice > noi * BUSINESS_CONSTANTS.MAX_COCR15_PRICE_MULTIPLIER) {\r\n targetPrice = noi * BUSINESS_CONSTANTS.CONSERVATIVE_COCR15_PRICE_MULTIPLIER;\r\n }\r\n \r\n iterations++;\r\n }\r\n \r\n // Apply final bounds check AFTER iteration\r\n if (targetPrice < BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE) {\r\n targetPrice = BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE;\r\n }\r\n \r\n return targetPrice;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate COCR at a specific down payment percentage\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} downPercent - Down payment percentage\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} COCR percentage\r\n */\r\nexport function calculateCOCRAtPercent(askingPrice, noi, downPercent, options = {}) {\r\n const {\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n } = options;\r\n\r\n try {\r\n const downDecimal = downPercent / 100;\r\n const cashInvested = askingPrice * downDecimal;\r\n \r\n // Fix financing structure: seller financing reduces available DSCR loan\r\n const dscrLoanAmount = askingPrice - cashInvested;\r\n \r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n \r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate NOI based on property type\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} capRate - Cap rate as decimal (e.g., 0.08 for 8%)\r\n * @param {string} propertyType - Property type from PROPERTY_TYPES\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated NOI\r\n */\r\nexport function calculateNOIByType(askingPrice, capRate, propertyType = PROPERTY_TYPES.MULTIFAMILY, options = {}) {\r\n const {\r\n strGrossIncomeMultiplier = PROPERTY_TYPE_CONSTANTS.STR.ESTIMATED_GROSS_RATE,\r\n strNoiPercentage = PROPERTY_TYPE_CONSTANTS.STR.NOI_PERCENTAGE,\r\n assistedIncomePerBedroom = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,\r\n bedroomCount = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT\r\n } = options;\r\n\r\n try {\r\n switch (propertyType.toLowerCase()) {\r\n case PROPERTY_TYPES.STR:\r\n const estimatedGrossIncome = askingPrice * strGrossIncomeMultiplier;\r\n return estimatedGrossIncome * strNoiPercentage;\r\n \r\n case PROPERTY_TYPES.ASSISTED_LIVING:\r\n return bedroomCount * assistedIncomePerBedroom * 12;\r\n \r\n case PROPERTY_TYPES.MULTIFAMILY:\r\n default:\r\n return askingPrice * capRate;\r\n }\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate assignment fee\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} assignmentPercent - Assignment fee percentage (uses config default)\r\n * @returns {number} Assignment fee amount\r\n */\r\nexport function calculateAssignmentFee(askingPrice, assignmentPercent = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100) {\r\n try {\r\n return askingPrice * (assignmentPercent / 100);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate net to buyer\r\n * @param {number} askingPrice - Property asking price\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Net to buyer amount\r\n */\r\nexport function calculateNetToBuyer(askingPrice, options = {}) {\r\n const {\r\n buyerCostPercent = BUSINESS_CONSTANTS.NET_TO_BUYER_PERCENTAGE * 100,\r\n sellerCostAssignment = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100,\r\n sellerCostClosing = BUSINESS_CONSTANTS.CLOSING_COSTS_PERCENTAGE * 100,\r\n additionalCostRehab = BUSINESS_CONSTANTS.REHAB_RATE * 100,\r\n additionalCostFinancing = BUSINESS_CONSTANTS.HARD_MONEY_RATE * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100\r\n } = options;\r\n\r\n try {\r\n const dscrLoanAmount = askingPrice * (dscrLtvPercent / 100);\r\n \r\n return askingPrice * (buyerCostPercent / 100) - \r\n askingPrice * ((sellerCostAssignment + sellerCostClosing) / 100) - \r\n askingPrice * (additionalCostRehab / 100) - \r\n (additionalCostFinancing / 100) * (askingPrice - dscrLoanAmount);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate remaining loan balance at end of balloon period\r\n * @param {number} loanAmount - Initial loan amount\r\n * @param {number} interestRate - Annual interest rate as decimal (e.g., 0.075 for 7.5%)\r\n * @param {number} amortizationYears - Full amortization period in years\r\n * @param {number} balloonYears - Balloon period in years\r\n * @returns {number} Remaining balance at end of balloon period\r\n */\r\nexport function calculateBalloonBalance(loanAmount, interestRate, amortizationYears, balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (loanAmount <= 0 || interestRate < 0 || amortizationYears <= 0 || balloonYears <= 0) {\r\n return 0;\r\n }\r\n\r\n // If balloon period equals or exceeds amortization, loan is fully paid\r\n if (balloonYears >= amortizationYears) {\r\n return 0;\r\n }\r\n\r\n // Special handling for zero interest rate (simple linear paydown)\r\n if (interestRate === 0) {\r\n const totalPayments = amortizationYears * 12;\r\n const paymentsMade = balloonYears * 12;\r\n return loanAmount * (totalPayments - paymentsMade) / totalPayments;\r\n }\r\n\r\n const monthlyRate = interestRate / 12;\r\n const totalPayments = amortizationYears * 12;\r\n const balloonPayments = balloonYears * 12;\r\n\r\n // Calculate remaining balance using loan balance formula\r\n // Balance = P * [(1 + r)^n - (1 + r)^p] / [(1 + r)^n - 1]\r\n // Where P = principal, r = monthly rate, n = total payments, p = payments made\r\n \r\n const factor1 = Math.pow(1 + monthlyRate, totalPayments);\r\n const factor2 = Math.pow(1 + monthlyRate, balloonPayments);\r\n \r\n const remainingBalance = loanAmount * (factor1 - factor2) / (factor1 - 1);\r\n \r\n return Math.max(0, remainingBalance); // Ensure non-negative\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate property value after appreciation period\r\n * @param {number} currentValue - Current property value\r\n * @param {number} appreciationRate - Annual appreciation rate as decimal\r\n * @param {number} years - Number of years\r\n * @returns {number} Appreciated property value\r\n */\r\nexport function calculateAppreciatedValue(currentValue, appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE, years = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (currentValue <= 0 || appreciationRate < 0 || years < 0) {\r\n return currentValue;\r\n }\r\n \r\n return currentValue * Math.pow(1 + appreciationRate, years);\r\n } catch (error) {\r\n return currentValue;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate cash out amount after appreciation refinance\r\n * @param {number} originalPrice - Original purchase price\r\n * @param {number} dscrLoanAmount - Original DSCR loan amount \r\n * @param {number} sellerFiAmount - Original seller financing amount\r\n * @param {Object} options - Configuration options\r\n * @returns {number} Cash out amount (positive = cash out, negative = cash in)\r\n */\r\nexport function calculateCashOutAfterRefi(originalPrice, dscrLoanAmount, sellerFiAmount, options = {}) {\r\n const {\r\n appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE,\r\n balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n sellerFiTerm = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION,\r\n refiLtvPercent = 70 // 70% LTV on refi\r\n } = options;\r\n\r\n try {\r\n // Calculate appreciated property value\r\n const appreciatedValue = calculateAppreciatedValue(originalPrice, appreciationRate, balloonYears);\r\n \r\n // Calculate remaining balance on DSCR loan\r\n const dscrRemainingBalance = calculateBalloonBalance(dscrLoanAmount, dscrRate, dscrTerm, balloonYears);\r\n \r\n // Calculate remaining balance on seller financing (0% interest)\r\n const sellerFiRemainingBalance = calculateBalloonBalance(sellerFiAmount, FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE, sellerFiTerm, balloonYears);\r\n \r\n // Total remaining debt\r\n const totalRemainingDebt = dscrRemainingBalance + sellerFiRemainingBalance;\r\n \r\n // Calculate new loan amount at 70% LTV of appreciated value\r\n const newLoanAmount = appreciatedValue * (refiLtvPercent / 100);\r\n \r\n // Cash out = new loan - total remaining debt\r\n const cashOut = newLoanAmount - totalRemainingDebt;\r\n \r\n return cashOut;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n// DSCR Payment calculation using existing calculatePMT\r\nexport function calculateDscrPayment(askingPrice, dscrPercent, dscrRate, dscrAmortization) {\r\n const dscrLoanAmount = askingPrice * (dscrPercent / 100);\r\n return calculatePMT(dscrLoanAmount, dscrRate, dscrAmortization);\r\n}\r\n\r\n// SF Payment calculation using existing calculatePMT \r\nexport function calculateSfPayment(askingPrice, sellerFiPercent, sellerFiRate, sellerFiAmortization) {\r\n const sellerFiAmount = askingPrice * (sellerFiPercent / 100);\r\n return calculatePMT(sellerFiAmount, sellerFiRate, sellerFiAmortization);\r\n}\r\n\r\n// Cash Flow calculation (matching loopnet-analyzer exactly)\r\nexport function calculateCashFlow(monthlyNOI, dscrPayment, sfPayment) {\r\n return monthlyNOI - (dscrPayment + sfPayment);\r\n}\r\n\r\n/**\r\n * Calculate discount percentage from asking price and offered price\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} priceOffered - Offered price\r\n * @returns {number} Discount as decimal (positive = discount, negative = premium)\r\n */\r\nexport function calculateDiscountFromPrice(askingPrice, priceOffered) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return (askingPrice - priceOffered) / askingPrice;\r\n}\r\n\r\n/**\r\n * Calculate price from asking price and discount percentage\r\n * @param {number} askingPrice - Property asking price \r\n * @param {number} discountPercent - Discount as decimal (positive = discount, negative = premium)\r\n * @returns {number} Calculated price\r\n */\r\nexport function calculatePriceFromDiscount(askingPrice, discountPercent) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return askingPrice * (1 - discountPercent);\r\n}\r\n\r\nexport function safePercentage(value, fallback = 100) {\r\n return (value != null && !isNaN(value)) ? (value * 100) : fallback;\r\n}\r\n\r\n/**\r\n * Calculate monthly cash flow from property financials\r\n * Handles all loan calculations internally\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} annualNOI - Annual Net Operating Income\r\n * @param {number} downPaymentPercent - Down payment as decimal (0.3 = 30%)\r\n * @param {number} sellerFiRate - Seller financing interest rate as decimal (0.065 = 6.5%)\r\n * @param {number} sellerAmortization - Seller financing term in years (default 30)\r\n * @param {number} deferralMonths - Interest-only deferral period in months (default 0)\r\n * @returns {number} Monthly cash flow in dollars\r\n */\r\nexport function calculateMonthlyCashFlow(\r\n askingPrice,\r\n annualNOI,\r\n downPaymentPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT,\r\n sellerFiRate = FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE,\r\n sellerAmortization = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION,\r\n deferralMonths = 0\r\n) {\r\n try {\r\n if (!askingPrice || !annualNOI || askingPrice <= 0 || annualNOI <= 0) {\r\n return 0;\r\n }\r\n\r\n // Monthly NOI\r\n const monthlyNOI = annualNOI / 12;\r\n\r\n // Calculate financing amounts\r\n // DSCR is always 70% of asking price\r\n const dscrLoanAmount = askingPrice * FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE;\r\n \r\n // Seller financing = remaining after down payment and DSCR\r\n const sellerFiAmount = askingPrice * (1 - downPaymentPercent - FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE);\r\n\r\n // Calculate monthly payments\r\n const dscrPayment = calculatePMT(dscrLoanAmount, FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE, 30);\r\n \r\n // Seller financing payment (may be 0 if rate is 0)\r\n let sellerFiPayment = 0;\r\n if (sellerFiRate > 0) {\r\n sellerFiPayment = calculatePMT(sellerFiAmount, sellerFiRate, sellerAmortization);\r\n } else {\r\n // If 0% interest, just divide principal by term\r\n sellerFiPayment = sellerFiAmount / (sellerAmortization * 12);\r\n }\r\n\r\n // Handle deferral period (interest-only)\r\n let effectiveSellerFiPayment = sellerFiPayment;\r\n if (deferralMonths > 0 && sellerFiRate > 0) {\r\n // During deferral, only pay interest\r\n const monthlyRate = sellerFiRate / 12;\r\n effectiveSellerFiPayment = sellerFiAmount * monthlyRate;\r\n }\r\n\r\n // Monthly cash flow\r\n return monthlyNOI - (dscrPayment + effectiveSellerFiPayment);\r\n } catch (error) {\r\n console.error('Error calculating monthly cash flow:', error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate assignment fee from decimal rate\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} assignmentRateDecimal - Assignment rate as decimal (0.05 = 5%)\r\n * @returns {number} Assignment fee in dollars\r\n */\r\nexport function calculateAssignmentFeeFromDecimal(askingPrice, assignmentRateDecimal = 0.05) {\r\n try {\r\n if (!askingPrice || askingPrice <= 0 || !assignmentRateDecimal) {\r\n return 0;\r\n }\r\n return askingPrice * assignmentRateDecimal;\r\n } catch (error) {\r\n console.error('Error calculating assignment fee:', error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate DSCR monthly payment\r\n * @param {number} askingPrice - Property asking price\r\n * @returns {number} Monthly DSCR payment in dollars\r\n */\r\nexport function calculateDscrMonthlyPayment(askingPrice) {\r\n try {\r\n if (!askingPrice || askingPrice <= 0) {\r\n return 0;\r\n }\r\n const dscrLoanAmount = askingPrice * FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE;\r\n return calculatePMT(dscrLoanAmount, FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE, 30);\r\n } catch (error) {\r\n console.error('Error calculating DSCR payment:', error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate seller financing monthly payment\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} downPaymentPercent - Down payment as decimal (0.3 = 30%)\r\n * @param {number} sellerFiRate - Seller financing rate as decimal (0.065 = 6.5%)\r\n * @param {number} sellerAmortization - Seller financing term in years\r\n * @returns {number} Monthly seller financing payment in dollars\r\n */\r\nexport function calculateSellerFiMonthlyPayment(\r\n askingPrice,\r\n downPaymentPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT,\r\n sellerFiRate = FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE,\r\n sellerAmortization = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION\r\n) {\r\n try {\r\n if (!askingPrice || askingPrice <= 0) {\r\n return 0;\r\n }\r\n\r\n const sellerFiAmount = askingPrice * (1 - downPaymentPercent - FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE);\r\n\r\n if (sellerFiRate > 0) {\r\n return calculatePMT(sellerFiAmount, sellerFiRate, sellerAmortization);\r\n } else {\r\n return sellerFiAmount / (sellerAmortization * 12);\r\n }\r\n } catch (error) {\r\n console.error('Error calculating seller fi payment:', error);\r\n return 0;\r\n }\r\n}"],"names":["calculatePMT","principal","annualRate","years","monthlyRate","numPayments","Math","pow","calculateCOCR30","askingPrice","noi","cashInvested","dscrPayment","error","calculateCashFlowYield","monthlyCashFlow","purchasePrice","calculatePriceForCOCR","targetCOCR","options","downPercent","FINANCIAL_CONSTANTS","DEFAULT_DOWN_PAYMENT","dscrLtvPercent","DEFAULT_DSCR_PERCENTAGE","dscrRate","DSCR_INTEREST_RATE","dscrTerm","DSCR_AMORTIZATION","maxIterations","BUSINESS_CONSTANTS","MAX_ITERATIONS","tolerance","CALCULATION_TOLERANCE","targetPrice","iterations","currentCOCR","abs","adjustment","ADJUSTMENT_FACTOR","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MINIMUM_COCR15_PRICE","calculateCOCRAtPercent","calculateNOIByType","capRate","propertyType","PROPERTY_TYPES","MULTIFAMILY","strGrossIncomeMultiplier","PROPERTY_TYPE_CONSTANTS","STR","ESTIMATED_GROSS_RATE","strNoiPercentage","NOI_PERCENTAGE","assistedIncomePerBedroom","ASSISTED_LIVING","INCOME_PER_BEDROOM_MONTHLY","bedroomCount","DEFAULT_BEDROOM_COUNT","toLowerCase","calculateAssignmentFee","assignmentPercent","ASSIGNMENT_FEE_PERCENTAGE","calculateNetToBuyer","buyerCostPercent","NET_TO_BUYER_PERCENTAGE","sellerCostAssignment","sellerCostClosing","CLOSING_COSTS_PERCENTAGE","additionalCostRehab","REHAB_RATE","additionalCostFinancing","HARD_MONEY_RATE","calculateBalloonBalance","loanAmount","interestRate","amortizationYears","balloonYears","DEFAULT_BALLOON_PERIOD_YEARS","totalPayments","balloonPayments","factor1","remainingBalance","max","calculateAppreciatedValue","currentValue","appreciationRate","APPRECIATION_RATE","calculateCashOutAfterRefi","originalPrice","dscrLoanAmount","sellerFiAmount","sellerFiTerm","SELLER_FI_AMORTIZATION","refiLtvPercent","appreciatedValue","dscrRemainingBalance","sellerFiRemainingBalance","SELLER_FI_INTEREST_RATE","calculateDscrPayment","dscrPercent","dscrAmortization","calculateSfPayment","sellerFiPercent","sellerFiRate","sellerFiAmortization","calculateCashFlow","monthlyNOI","sfPayment","calculateDiscountFromPrice","priceOffered","calculatePriceFromDiscount","discountPercent","safePercentage","value","fallback","isNaN","calculateMonthlyCashFlow","annualNOI","downPaymentPercent","sellerAmortization","deferralMonths","sellerFiPayment","effectiveSellerFiPayment","console","calculateAssignmentFeeFromDecimal","assignmentRateDecimal","calculateDscrMonthlyPayment","calculateSellerFiMonthlyPayment"],"mappings":"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"}
1
+ {"version":3,"file":"calculations.js","sources":["../../src/financial/calculations.js"],"sourcesContent":["// src/financial/calculations.js\r\n\r\nimport { FINANCIAL_CONSTANTS } from '../config/financial.js';\r\nimport { BUSINESS_CONSTANTS } from '../config/business.js';\r\nimport { PROPERTY_TYPE_CONSTANTS, PROPERTY_TYPES } from '../config/property-types.js';\r\n\r\n\r\n/**\r\n * PMT function for loan payment calculation\r\n * @param {number} principal - Loan principal amount \r\n * @param {number} annualRate - Annual interest rate (as decimal, e.g., 0.075 for 7.5%)\r\n * @param {number} years - Loan term in years\r\n * @returns {number} Monthly payment amount\r\n */\r\nexport function calculatePMT(principal, annualRate, years) {\r\n if (annualRate === 0) {\r\n return principal / (years * 12);\r\n }\r\n \r\n const monthlyRate = annualRate / 12;\r\n const numPayments = years * 12;\r\n const pmt = principal * (monthlyRate * Math.pow(1 + monthlyRate, numPayments)) / \r\n (Math.pow(1 + monthlyRate, numPayments) - 1);\r\n return pmt;\r\n}\r\n\r\nexport function calculateCOCR30(askingPrice, noi) {\r\n try {\r\n const cashInvested = askingPrice * 0.30; // 30% down payment\r\n const dscrLoanAmount = askingPrice * 0.70; // Fixed 70% DSCR loan\r\n const dscrPayment = calculatePMT(dscrLoanAmount, 0.075, 30) * 12; // Annual DSCR payment\r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\nexport function calculateCashFlowYield(monthlyCashFlow, purchasePrice) {\r\n if (!purchasePrice || purchasePrice <= 0) return 0;\r\n const annualCashFlow = monthlyCashFlow * 12;\r\n return (annualCashFlow / purchasePrice) * 100;\r\n}\r\n\r\n\r\n/**\r\n * Calculate the property price that yields a target COCR percentage\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} targetCOCR - Target COCR as decimal (default: 0.15 for 15%)\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated property price\r\n */\r\nexport function calculatePriceForCOCR(noi, targetCOCR = 0.15, options = {}) {\r\n const {\r\n downPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n maxIterations = BUSINESS_CONSTANTS.MAX_ITERATIONS,\r\n tolerance = BUSINESS_CONSTANTS.CALCULATION_TOLERANCE\r\n } = options;\r\n\r\n try {\r\n let targetPrice = noi / 0.08; // Initial estimate: NOI / 8% cap rate\r\n let iterations = 0;\r\n \r\n while (iterations < maxIterations) {\r\n const cashInvested = targetPrice * (downPercent / 100);\r\n const dscrLoanAmount = targetPrice * (dscrLtvPercent / 100);\r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n const annualCashFlow = noi - dscrPayment;\r\n const currentCOCR = annualCashFlow / cashInvested;\r\n \r\n if (Math.abs(currentCOCR - targetCOCR) < tolerance) {\r\n break;\r\n }\r\n \r\n const error = currentCOCR - targetCOCR;\r\n const adjustment = error * BUSINESS_CONSTANTS.ADJUSTMENT_FACTOR;\r\n \r\n if (error > 0) {\r\n targetPrice = targetPrice * (1 + Math.abs(adjustment));\r\n } else {\r\n targetPrice = targetPrice * (1 - Math.abs(adjustment));\r\n }\r\n \r\n // Reasonable bounds during iteration (prevent extreme values)\r\n if (targetPrice < 1000) targetPrice = 1000;\r\n if (targetPrice > noi * BUSINESS_CONSTANTS.MAX_COCR15_PRICE_MULTIPLIER) {\r\n targetPrice = noi * BUSINESS_CONSTANTS.CONSERVATIVE_COCR15_PRICE_MULTIPLIER;\r\n }\r\n \r\n iterations++;\r\n }\r\n \r\n // Apply final bounds check AFTER iteration\r\n if (targetPrice < BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE) {\r\n targetPrice = BUSINESS_CONSTANTS.MINIMUM_COCR15_PRICE;\r\n }\r\n \r\n return targetPrice;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate COCR at a specific down payment percentage\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} noi - Net Operating Income (annual)\r\n * @param {number} downPercent - Down payment percentage\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} COCR percentage\r\n */\r\nexport function calculateCOCRAtPercent(askingPrice, noi, downPercent, options = {}) {\r\n const {\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n } = options;\r\n\r\n try {\r\n const downDecimal = downPercent / 100;\r\n const cashInvested = askingPrice * downDecimal;\r\n \r\n // Fix financing structure: seller financing reduces available DSCR loan\r\n const dscrLoanAmount = askingPrice - cashInvested;\r\n \r\n const dscrPayment = calculatePMT(dscrLoanAmount, dscrRate, dscrTerm) * 12;\r\n \r\n const annualCashFlow = noi - dscrPayment;\r\n const cocr = (annualCashFlow / cashInvested) * 100;\r\n \r\n return cocr;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate NOI based on property type\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} capRate - Cap rate as decimal (e.g., 0.08 for 8%)\r\n * @param {string} propertyType - Property type from PROPERTY_TYPES\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Calculated NOI\r\n */\r\nexport function calculateNOIByType(askingPrice, capRate, propertyType = PROPERTY_TYPES.MULTIFAMILY, options = {}) {\r\n const {\r\n strGrossIncomeMultiplier = PROPERTY_TYPE_CONSTANTS.STR.ESTIMATED_GROSS_RATE,\r\n strNoiPercentage = PROPERTY_TYPE_CONSTANTS.STR.NOI_PERCENTAGE,\r\n assistedIncomePerBedroom = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.INCOME_PER_BEDROOM_MONTHLY,\r\n bedroomCount = PROPERTY_TYPE_CONSTANTS.ASSISTED_LIVING.DEFAULT_BEDROOM_COUNT\r\n } = options;\r\n\r\n try {\r\n switch (propertyType.toLowerCase()) {\r\n case PROPERTY_TYPES.STR:\r\n const estimatedGrossIncome = askingPrice * strGrossIncomeMultiplier;\r\n return estimatedGrossIncome * strNoiPercentage;\r\n \r\n case PROPERTY_TYPES.ASSISTED_LIVING:\r\n return bedroomCount * assistedIncomePerBedroom * 12;\r\n \r\n case PROPERTY_TYPES.MULTIFAMILY:\r\n default:\r\n return askingPrice * capRate;\r\n }\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate assignment fee\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} assignmentPercent - Assignment fee percentage (uses config default)\r\n * @returns {number} Assignment fee amount\r\n */\r\nexport function calculateAssignmentFee(askingPrice, assignmentPercent = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100) {\r\n try {\r\n return askingPrice * (assignmentPercent / 100);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate net to buyer\r\n * @param {number} askingPrice - Property asking price\r\n * @param {Object} options - Configuration options (uses config constants as defaults)\r\n * @returns {number} Net to buyer amount\r\n */\r\nexport function calculateNetToBuyer(askingPrice, options = {}) {\r\n const {\r\n buyerCostPercent = BUSINESS_CONSTANTS.NET_TO_BUYER_PERCENTAGE * 100,\r\n sellerCostAssignment = BUSINESS_CONSTANTS.ASSIGNMENT_FEE_PERCENTAGE * 100,\r\n sellerCostClosing = BUSINESS_CONSTANTS.CLOSING_COSTS_PERCENTAGE * 100,\r\n additionalCostRehab = BUSINESS_CONSTANTS.REHAB_RATE * 100,\r\n additionalCostFinancing = BUSINESS_CONSTANTS.HARD_MONEY_RATE * 100,\r\n dscrLtvPercent = FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE * 100\r\n } = options;\r\n\r\n try {\r\n const dscrLoanAmount = askingPrice * (dscrLtvPercent / 100);\r\n \r\n return askingPrice * (buyerCostPercent / 100) - \r\n askingPrice * ((sellerCostAssignment + sellerCostClosing) / 100) - \r\n askingPrice * (additionalCostRehab / 100) - \r\n (additionalCostFinancing / 100) * (askingPrice - dscrLoanAmount);\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate remaining loan balance at end of balloon period\r\n * @param {number} loanAmount - Initial loan amount\r\n * @param {number} interestRate - Annual interest rate as decimal (e.g., 0.075 for 7.5%)\r\n * @param {number} amortizationYears - Full amortization period in years\r\n * @param {number} balloonYears - Balloon period in years\r\n * @returns {number} Remaining balance at end of balloon period\r\n */\r\nexport function calculateBalloonBalance(loanAmount, interestRate, amortizationYears, balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (loanAmount <= 0 || interestRate < 0 || amortizationYears <= 0 || balloonYears <= 0) {\r\n return 0;\r\n }\r\n\r\n // If balloon period equals or exceeds amortization, loan is fully paid\r\n if (balloonYears >= amortizationYears) {\r\n return 0;\r\n }\r\n\r\n // Special handling for zero interest rate (simple linear paydown)\r\n if (interestRate === 0) {\r\n const totalPayments = amortizationYears * 12;\r\n const paymentsMade = balloonYears * 12;\r\n return loanAmount * (totalPayments - paymentsMade) / totalPayments;\r\n }\r\n\r\n const monthlyRate = interestRate / 12;\r\n const totalPayments = amortizationYears * 12;\r\n const balloonPayments = balloonYears * 12;\r\n\r\n // Calculate remaining balance using loan balance formula\r\n // Balance = P * [(1 + r)^n - (1 + r)^p] / [(1 + r)^n - 1]\r\n // Where P = principal, r = monthly rate, n = total payments, p = payments made\r\n \r\n const factor1 = Math.pow(1 + monthlyRate, totalPayments);\r\n const factor2 = Math.pow(1 + monthlyRate, balloonPayments);\r\n \r\n const remainingBalance = loanAmount * (factor1 - factor2) / (factor1 - 1);\r\n \r\n return Math.max(0, remainingBalance); // Ensure non-negative\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate property value after appreciation period\r\n * @param {number} currentValue - Current property value\r\n * @param {number} appreciationRate - Annual appreciation rate as decimal\r\n * @param {number} years - Number of years\r\n * @returns {number} Appreciated property value\r\n */\r\nexport function calculateAppreciatedValue(currentValue, appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE, years = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS) {\r\n try {\r\n if (currentValue <= 0 || appreciationRate < 0 || years < 0) {\r\n return currentValue;\r\n }\r\n \r\n return currentValue * Math.pow(1 + appreciationRate, years);\r\n } catch (error) {\r\n return currentValue;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate cash out amount after appreciation refinance\r\n * @param {number} originalPrice - Original purchase price\r\n * @param {number} dscrLoanAmount - Original DSCR loan amount \r\n * @param {number} sellerFiAmount - Original seller financing amount\r\n * @param {Object} options - Configuration options\r\n * @returns {number} Cash out amount (positive = cash out, negative = cash in)\r\n */\r\nexport function calculateCashOutAfterRefi(originalPrice, dscrLoanAmount, sellerFiAmount, options = {}) {\r\n const {\r\n appreciationRate = FINANCIAL_CONSTANTS.APPRECIATION_RATE,\r\n balloonYears = FINANCIAL_CONSTANTS.DEFAULT_BALLOON_PERIOD_YEARS,\r\n dscrRate = FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE,\r\n dscrTerm = FINANCIAL_CONSTANTS.DSCR_AMORTIZATION,\r\n sellerFiTerm = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION,\r\n refiLtvPercent = 70 // 70% LTV on refi\r\n } = options;\r\n\r\n try {\r\n // Calculate appreciated property value\r\n const appreciatedValue = calculateAppreciatedValue(originalPrice, appreciationRate, balloonYears);\r\n \r\n // Calculate remaining balance on DSCR loan\r\n const dscrRemainingBalance = calculateBalloonBalance(dscrLoanAmount, dscrRate, dscrTerm, balloonYears);\r\n \r\n // Calculate remaining balance on seller financing (0% interest)\r\n const sellerFiRemainingBalance = calculateBalloonBalance(sellerFiAmount, FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE, sellerFiTerm, balloonYears);\r\n \r\n // Total remaining debt\r\n const totalRemainingDebt = dscrRemainingBalance + sellerFiRemainingBalance;\r\n \r\n // Calculate new loan amount at 70% LTV of appreciated value\r\n const newLoanAmount = appreciatedValue * (refiLtvPercent / 100);\r\n \r\n // Cash out = new loan - total remaining debt\r\n const cashOut = newLoanAmount - totalRemainingDebt;\r\n \r\n return cashOut;\r\n } catch (error) {\r\n return 0;\r\n }\r\n}\r\n\r\n// Cash Flow calculation (matching loopnet-analyzer exactly)\r\nexport function calculateCashFlow(monthlyNOI, dscrPayment, sfPayment) {\r\n return monthlyNOI - (dscrPayment + sfPayment);\r\n}\r\n\r\n/**\r\n * Calculate discount percentage from asking price and offered price\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} priceOffered - Offered price\r\n * @returns {number} Discount as decimal (positive = discount, negative = premium)\r\n */\r\nexport function calculateDiscountFromPrice(askingPrice, priceOffered) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return (askingPrice - priceOffered) / askingPrice;\r\n}\r\n\r\n/**\r\n * Calculate price from asking price and discount percentage\r\n * @param {number} askingPrice - Property asking price \r\n * @param {number} discountPercent - Discount as decimal (positive = discount, negative = premium)\r\n * @returns {number} Calculated price\r\n */\r\nexport function calculatePriceFromDiscount(askingPrice, discountPercent) {\r\n if (!askingPrice || askingPrice <= 0) return 0;\r\n \r\n return askingPrice * (1 - discountPercent);\r\n}\r\n\r\nexport function safePercentage(value, fallback = 100) {\r\n return (value != null && !isNaN(value)) ? (value * 100) : fallback;\r\n}\r\n\r\n/**\r\n * Calculate monthly cash flow from property financials\r\n * Handles all loan calculations internally\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} annualNOI - Annual Net Operating Income\r\n * @param {number} downPaymentPercent - Down payment as decimal (0.3 = 30%)\r\n * @param {number} sellerFiRate - Seller financing interest rate as decimal (0.065 = 6.5%)\r\n * @param {number} sellerAmortization - Seller financing term in years (default 30)\r\n * @param {number} deferralMonths - Interest-only deferral period in months (default 0)\r\n * @returns {number} Monthly cash flow in dollars\r\n */\r\nexport function calculateMonthlyCashFlow(\r\n askingPrice,\r\n annualNOI,\r\n downPaymentPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT,\r\n sellerFiRate = FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE,\r\n sellerAmortization = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION,\r\n deferralMonths = 0\r\n) {\r\n try {\r\n if (!askingPrice || !annualNOI || askingPrice <= 0 || annualNOI <= 0) {\r\n return 0;\r\n }\r\n\r\n // Monthly NOI\r\n const monthlyNOI = annualNOI / 12;\r\n\r\n // Calculate financing amounts\r\n // DSCR is always 70% of asking price\r\n const dscrLoanAmount = askingPrice * FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE;\r\n \r\n // Seller financing = remaining after down payment and DSCR\r\n const sellerFiAmount = askingPrice * (1 - downPaymentPercent - FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE);\r\n\r\n // Calculate monthly payments\r\n const dscrPayment = calculatePMT(dscrLoanAmount, FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE, 30);\r\n \r\n // Seller financing payment (may be 0 if rate is 0)\r\n let sellerFiPayment = 0;\r\n if (sellerFiRate > 0) {\r\n sellerFiPayment = calculatePMT(sellerFiAmount, sellerFiRate, sellerAmortization);\r\n } else {\r\n // If 0% interest, just divide principal by term\r\n sellerFiPayment = sellerFiAmount / (sellerAmortization * 12);\r\n }\r\n\r\n // Handle deferral period (interest-only)\r\n let effectiveSellerFiPayment = sellerFiPayment;\r\n if (deferralMonths > 0 && sellerFiRate > 0) {\r\n // During deferral, only pay interest\r\n const monthlyRate = sellerFiRate / 12;\r\n effectiveSellerFiPayment = sellerFiAmount * monthlyRate;\r\n }\r\n\r\n // Monthly cash flow\r\n return monthlyNOI - (dscrPayment + effectiveSellerFiPayment);\r\n } catch (error) {\r\n console.error('Error calculating monthly cash flow:', error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate assignment fee from decimal rate\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} assignmentRateDecimal - Assignment rate as decimal (0.05 = 5%)\r\n * @returns {number} Assignment fee in dollars\r\n */\r\nexport function calculateAssignmentFeeFromDecimal(askingPrice, assignmentRateDecimal = 0.05) {\r\n try {\r\n if (!askingPrice || askingPrice <= 0 || !assignmentRateDecimal) {\r\n return 0;\r\n }\r\n return askingPrice * assignmentRateDecimal;\r\n } catch (error) {\r\n console.error('Error calculating assignment fee:', error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate DSCR monthly payment\r\n * @param {number} askingPrice - Property asking price\r\n * @returns {number} Monthly DSCR payment in dollars\r\n */\r\nexport function calculateDscrMonthlyPayment(askingPrice) {\r\n try {\r\n if (!askingPrice || askingPrice <= 0) {\r\n return 0;\r\n }\r\n const dscrLoanAmount = askingPrice * FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE;\r\n return calculatePMT(dscrLoanAmount, FINANCIAL_CONSTANTS.DSCR_INTEREST_RATE, 30);\r\n } catch (error) {\r\n console.error('Error calculating DSCR payment:', error);\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Calculate seller financing monthly payment\r\n * @param {number} askingPrice - Property asking price\r\n * @param {number} downPaymentPercent - Down payment as decimal (0.3 = 30%)\r\n * @param {number} sellerFiRate - Seller financing rate as decimal (0.065 = 6.5%)\r\n * @param {number} sellerAmortization - Seller financing term in years\r\n * @returns {number} Monthly seller financing payment in dollars\r\n */\r\nexport function calculateSellerFiMonthlyPayment(\r\n askingPrice,\r\n downPaymentPercent = FINANCIAL_CONSTANTS.DEFAULT_DOWN_PAYMENT,\r\n sellerFiRate = FINANCIAL_CONSTANTS.SELLER_FI_INTEREST_RATE,\r\n sellerAmortization = FINANCIAL_CONSTANTS.SELLER_FI_AMORTIZATION\r\n) {\r\n try {\r\n if (!askingPrice || askingPrice <= 0) {\r\n return 0;\r\n }\r\n\r\n const sellerFiAmount = askingPrice * (1 - downPaymentPercent - FINANCIAL_CONSTANTS.DEFAULT_DSCR_PERCENTAGE);\r\n\r\n if (sellerFiRate > 0) {\r\n return calculatePMT(sellerFiAmount, sellerFiRate, sellerAmortization);\r\n } else {\r\n return sellerFiAmount / (sellerAmortization * 12);\r\n }\r\n } catch (error) {\r\n console.error('Error calculating seller fi payment:', error);\r\n return 0;\r\n }\r\n}"],"names":["calculatePMT","principal","annualRate","years","monthlyRate","numPayments","Math","pow","calculateCOCR30","askingPrice","noi","cashInvested","dscrPayment","error","calculateCashFlowYield","monthlyCashFlow","purchasePrice","calculatePriceForCOCR","targetCOCR","options","downPercent","FINANCIAL_CONSTANTS","DEFAULT_DOWN_PAYMENT","dscrLtvPercent","DEFAULT_DSCR_PERCENTAGE","dscrRate","DSCR_INTEREST_RATE","dscrTerm","DSCR_AMORTIZATION","maxIterations","BUSINESS_CONSTANTS","MAX_ITERATIONS","tolerance","CALCULATION_TOLERANCE","targetPrice","iterations","currentCOCR","abs","adjustment","ADJUSTMENT_FACTOR","MAX_COCR15_PRICE_MULTIPLIER","CONSERVATIVE_COCR15_PRICE_MULTIPLIER","MINIMUM_COCR15_PRICE","calculateCOCRAtPercent","calculateNOIByType","capRate","propertyType","PROPERTY_TYPES","MULTIFAMILY","strGrossIncomeMultiplier","PROPERTY_TYPE_CONSTANTS","STR","ESTIMATED_GROSS_RATE","strNoiPercentage","NOI_PERCENTAGE","assistedIncomePerBedroom","ASSISTED_LIVING","INCOME_PER_BEDROOM_MONTHLY","bedroomCount","DEFAULT_BEDROOM_COUNT","toLowerCase","calculateAssignmentFee","assignmentPercent","ASSIGNMENT_FEE_PERCENTAGE","calculateNetToBuyer","buyerCostPercent","NET_TO_BUYER_PERCENTAGE","sellerCostAssignment","sellerCostClosing","CLOSING_COSTS_PERCENTAGE","additionalCostRehab","REHAB_RATE","additionalCostFinancing","HARD_MONEY_RATE","calculateBalloonBalance","loanAmount","interestRate","amortizationYears","balloonYears","DEFAULT_BALLOON_PERIOD_YEARS","totalPayments","balloonPayments","factor1","remainingBalance","max","calculateAppreciatedValue","currentValue","appreciationRate","APPRECIATION_RATE","calculateCashOutAfterRefi","originalPrice","dscrLoanAmount","sellerFiAmount","sellerFiTerm","SELLER_FI_AMORTIZATION","refiLtvPercent","appreciatedValue","dscrRemainingBalance","sellerFiRemainingBalance","SELLER_FI_INTEREST_RATE","calculateCashFlow","monthlyNOI","sfPayment","calculateDiscountFromPrice","priceOffered","calculatePriceFromDiscount","discountPercent","safePercentage","value","fallback","isNaN"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export{calculateAppreciatedValue,calculateAssignmentFee,calculateAssignmentFeeFromDecimal,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateDscrMonthlyPayment,calculateDscrPayment,calculateMonthlyCashFlow,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,calculateSellerFiMonthlyPayment,calculateSfPayment,safePercentage}from"./financial/calculations.js";export{formatCurrency,formatPercentage,formatPriceValue}from"./financial/formatters.js";export{extractBedrooms,extractPhoneNumber}from"./data/extractors.js";export{calculateDOM}from"./date/utilities.js";export{calculateCursorPosition,extractNumericValue,filterNumericInput,formatInputDisplay,formatLiveInput,formatLiveNumber,parseNumericInput}from"./formatting/financial-formatting.js";export{normalizeWhitespace}from"./formatting/text.js";export{CALCULATION_TOLERANCE,DEFAULT_CAP_RATE,DEFAULT_DOWN_PAYMENT,DEFAULT_DSCR_PERCENTAGE,DEFAULT_EQUITY_ESTIMATE,DSCR_AMORTIZATION,DSCR_INTEREST_RATE,FINANCIAL_CONSTANTS,MAX_ITERATIONS,SELLER_FI_AMORTIZATION,SELLER_FI_CARRY,SELLER_FI_DOWN_PAYMENT,SELLER_FI_INTEREST_RATE}from"./config/financial.js";export{ASSISTED_LIVING,MULTIFAMILY,PROPERTY_TYPES,PROPERTY_TYPE_CONSTANTS,STR}from"./config/property-types.js";export{ASSIGNMENT_FEE_PERCENTAGE,BUSINESS_CONSTANTS,BUYER_AGENT_COMMISSION,CLOSING_COSTS_PERCENTAGE,CONSERVATIVE_COCR15_PRICE_MULTIPLIER,HARD_MONEY_RATE,MAX_COCR15_PRICE_MULTIPLIER,MINIMUM_COCR15_PRICE,NET_TO_BUYER_PERCENTAGE,REHAB_RATE,SELLER_AGENT_COMMISSION}from"./config/business.js";export{lookupLOI}from"./services/loi-lookup.js";export{LOI_LOOKUP_CONFIG,LOI_SENT_STATUS,MATCH_TYPES}from"./config/loi-lookup.js";const e="./dist/styles/base.css";export{e as STYLES_PATH};
1
+ export{calculateAppreciatedValue,calculateAssignmentFee,calculateBalloonBalance,calculateCOCR30,calculateCOCRAtPercent,calculateCashFlow,calculateCashFlowYield,calculateCashOutAfterRefi,calculateDiscountFromPrice,calculateNOIByType,calculateNetToBuyer,calculatePMT,calculatePriceForCOCR,calculatePriceFromDiscount,safePercentage}from"./financial/calculations.js";export{formatCurrency,formatPercentage,formatPriceValue}from"./financial/formatters.js";export{extractBedrooms,extractPhoneNumber}from"./data/extractors.js";export{calculateDOM,formatDate}from"./date/utilities.js";export{calculateCursorPosition,extractNumericValue,filterNumericInput,formatInputDisplay,formatLiveInput,formatLiveNumber,parseNumericInput}from"./formatting/financial-formatting.js";export{normalizeWhitespace}from"./formatting/text.js";export{CALCULATION_TOLERANCE,DEFAULT_CAP_RATE,DEFAULT_DOWN_PAYMENT,DEFAULT_DSCR_PERCENTAGE,DEFAULT_EQUITY_ESTIMATE,DSCR_AMORTIZATION,DSCR_INTEREST_RATE,FINANCIAL_CONSTANTS,MAX_ITERATIONS,SELLER_FI_AMORTIZATION,SELLER_FI_CARRY,SELLER_FI_DOWN_PAYMENT,SELLER_FI_INTEREST_RATE}from"./config/financial.js";export{ASSISTED_LIVING,MULTIFAMILY,PROPERTY_TYPES,PROPERTY_TYPE_CONSTANTS,STR}from"./config/property-types.js";export{ASSIGNMENT_FEE_PERCENTAGE,BUSINESS_CONSTANTS,BUYER_AGENT_COMMISSION,CLOSING_COSTS_PERCENTAGE,CONSERVATIVE_COCR15_PRICE_MULTIPLIER,HARD_MONEY_RATE,MAX_COCR15_PRICE_MULTIPLIER,MINIMUM_COCR15_PRICE,NET_TO_BUYER_PERCENTAGE,REHAB_RATE,SELLER_AGENT_COMMISSION}from"./config/business.js";export{lookupLOI}from"./services/loi-lookup.js";export{LOI_LOOKUP_CONFIG,LOI_SENT_STATUS,MATCH_TYPES}from"./config/loi-lookup.js";export{getEnvVar,isBrowserEnvironment,isNodeEnvironment}from"./environment/utilities.js";const e="./dist/styles/base.css";export{e as STYLES_PATH};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["/**\r\n * @archerjessop/utilities\r\n * Shared utilities for ArcherJessop property analysis tools\r\n */\r\n\r\n// Financial calculations\r\nexport { \r\n calculateAppreciatedValue,\r\n calculateAssignmentFee,\r\n calculateAssignmentFeeFromDecimal,\r\n calculateBalloonBalance,\r\n calculateCashFlow,\r\n calculateCashFlowYield,\r\n calculateCashOutAfterRefi,\r\n calculateCOCR30, \r\n calculateCOCRAtPercent,\r\n calculateDiscountFromPrice,\r\n calculateDscrMonthlyPayment,\r\n calculateDscrPayment,\r\n calculateMonthlyCashFlow,\r\n calculateNetToBuyer,\r\n calculateNOIByType,\r\n calculatePMT, \r\n calculatePriceForCOCR,\r\n calculatePriceFromDiscount,\r\n calculateSellerFiMonthlyPayment,\r\n calculateSfPayment,\r\n safePercentage,\r\n} from \"./financial/calculations.js\";\r\n\r\n// Financial formatters\r\nexport { formatCurrency, formatPriceValue, formatPercentage } from \"./financial/formatters.js\";\r\n\r\n// Data extractors\r\nexport { extractPhoneNumber, extractBedrooms } from \"./data/extractors.js\";\r\n\r\n// Date utilities\r\nexport { calculateDOM } from \"./date/utilities.js\";\r\n\r\n// Formatting utilities\r\nexport { \r\n calculateCursorPosition,\r\n extractNumericValue,\r\n filterNumericInput,\r\n formatInputDisplay,\r\n formatLiveInput,\r\n formatLiveNumber,\r\n parseNumericInput\r\n} from \"./formatting/financial-formatting.js\";\r\n\r\n// Text formatting utilities\r\nexport { normalizeWhitespace } from \"./formatting/text.js\";\r\n\r\n// Configuration constants\r\nexport * from './config/financial.js';\r\nexport * from './config/property-types.js';\r\nexport * from './config/business.js';\r\n\r\nexport const STYLES_PATH = './dist/styles/base.css';\r\n\r\n// LOI Lookup service and config\r\nexport { lookupLOI } from \"./services/loi-lookup.js\";\r\nexport { LOI_LOOKUP_CONFIG, MATCH_TYPES, LOI_SENT_STATUS } from \"./config/loi-lookup.js\";"],"names":["STYLES_PATH"],"mappings":"gwDA0DY,MAACA,EAAc"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.js"],"sourcesContent":["/**\r\n * @archerjessop/utilities\r\n * Shared utilities for ArcherJessop property analysis tools\r\n */\r\n\r\n// Financial calculations\r\nexport { \r\n calculateAppreciatedValue,\r\n calculateAssignmentFee,\r\n calculateBalloonBalance,\r\n calculateCashFlow,\r\n calculateCashFlowYield,\r\n calculateCashOutAfterRefi,\r\n calculateCOCR30, \r\n calculateCOCRAtPercent,\r\n calculateDiscountFromPrice,\r\n calculateNetToBuyer,\r\n calculateNOIByType,\r\n calculatePMT, \r\n calculatePriceForCOCR,\r\n calculatePriceFromDiscount,\r\n safePercentage,\r\n} from \"./financial/calculations.js\";\r\n\r\n// Financial formatters\r\nexport { formatCurrency, formatPriceValue, formatPercentage } from \"./financial/formatters.js\";\r\n\r\n// Data extractors\r\nexport { extractPhoneNumber, extractBedrooms } from \"./data/extractors.js\";\r\n\r\n// Date utilities\r\nexport { calculateDOM, formatDate } from \"./date/utilities.js\";\r\n\r\n// Formatting utilities\r\nexport { \r\n calculateCursorPosition,\r\n extractNumericValue,\r\n filterNumericInput,\r\n formatInputDisplay,\r\n formatLiveInput,\r\n formatLiveNumber,\r\n parseNumericInput\r\n} from \"./formatting/financial-formatting.js\";\r\n\r\n// Text formatting utilities\r\nexport { normalizeWhitespace } from \"./formatting/text.js\";\r\n\r\n// Configuration constants\r\nexport * from './config/financial.js';\r\nexport * from './config/property-types.js';\r\nexport * from './config/business.js';\r\n\r\nexport const STYLES_PATH = './dist/styles/base.css';\r\n\r\n// LOI Lookup service and config\r\nexport { lookupLOI } from \"./services/loi-lookup.js\";\r\nexport { LOI_LOOKUP_CONFIG, MATCH_TYPES, LOI_SENT_STATUS } from \"./config/loi-lookup.js\";\r\n\r\n// Environment utilities\r\nexport { \r\n getEnvVar, \r\n isNodeEnvironment, \r\n isBrowserEnvironment \r\n} from \"./environment/utilities.js\";"],"names":["STYLES_PATH"],"mappings":"qsDAoDY,MAACA,EAAc"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archerjessop/utilities",
3
- "version": "4.6.23",
3
+ "version": "4.6.25",
4
4
  "description": "Shared utilities for ArcherJessop property analysis tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",