@thezelijah/majik-runway
v1.0.1
Published
Majik Runway is a comprehensive financial runway, cashflow, and burn-rate modeling engine in the Majik system.
Downloads
204
Maintainers
Readme
Majik Runway
It helps founders, operators, and finance teams understand how long a business can operate, where money is going, and how decisions affect survival and growth.
Majik Runway models your business as a living financial system, not a static spreadsheet.
It composes other Majik financial primitives such as MajikProduct, MajikService, MajikSubscription, and MajikMoney to produce accurate, period-based projections.
Live Demo
Click the image to try Majik Runway live.
Table of Contents
- Majik Runway
- Live Demo
- Table of Contents
- Overview
- Key Concepts
- Installation
- Quick Start
- Usage
- Mental Model
- Example Usage
- Dashboard Integration & Real-Time Financial Modeling
- Utilities
- Use Cases
- Best Practices
- Conclusion
- Contributing
- License
- Author
- About the Developer
- Contact
- API Reference
Overview
Majik Runway manages:
- Cash Balance: Starting cash, funding events, and cumulative cash over time
- Revenue Streams: Products, services, and subscriptions
- Expenses: Fixed, variable, and one-time costs
- Burn Rate: Monthly and trend-based burn
- Runway: Estimated months before cash depletion
- Period Modeling: YYYYMM-based projections with proration
- Scenario Planning: Live recomputation on assumption changes
- Serialization: Safe export/import using MajikMoney
Majik Runway is not accounting software — it is a forecasting and decision engine.
Key Concepts
Majik Runway is a financial simulation and planning engine, not an accounting system. It models cash behavior over time using deterministic rules so founders, operators, and finance teams can reason about runway, burn, growth, and survivability with clarity.
Modeling Period
All computations occur inside a defined modeling window:
- Start:
YYYYMM - End:
YYYYMM
This period defines the timeline of the simulation, not the lifetime of the business.
Key rules:
- All revenues, expenses, funding events, and taxes are normalized to monthly granularity
- Partial months are prorated
- Items outside the modeling period are ignored
- Results are computed month-by-month, then aggregated
Majik Runway assumes time moves forward in discrete monthly steps. There is no backdating, retroactive mutation, or implicit carry-over beyond defined rules.
Revenue Streams
Majik Runway does not invent revenue logic — it reuses Majik domain models:
MajikProductMajikServiceMajikSubscription
Each revenue stream:
- Has an activation window (start / end)
- Produces gross monthly revenue
- May include:
- Pricing rules
- Quantity / capacity limits
- Growth or churn assumptions
- Can scale, pause, or terminate independently
Important clarifications:
- Revenue is modeled as earned revenue, not necessarily collected cash
- Cash collection timing (e.g. delayed payments) must be modeled explicitly if needed
- Refunds, chargebacks, or revenue leakage must be modeled as negative revenue or expenses
Expenses
Expenses represent cash outflows that reduce available cash and contribute to burn. Majik Runway models expenses explicitly and intentionally avoids accounting abstractions such as depreciation or accruals.
Supported Expense Types:
Operating Expenses
Recurring costs required to keep the business running:
- Salaries and contractors
- Rent and utilities
- Software subscriptions
- Base infrastructure costs
These expenses are typically predictable and stable over time.
Variable Expenses
Costs that scale with activity, usage, or growth:
- Marketing spend
- Transaction fees
- Usage-based infrastructure
- Performance-based commissions
Variable expenses may fluctuate month-to-month and often correlate with revenue or volume.
Capital Expenses
Large, usually strategic cash outlays intended to provide long-term value:
- Equipment and hardware
- Initial platform build costs
- One-time tooling or setup investments
Capital expenses are treated as immediate cash outflows, not depreciated assets.
Expense Timing
Independently of type, an expense may follow one of these timing behaviors:
One-time
Occurs once in a specific month
Recurring
Occurs every month within an active period
Capital
Large one-time expenditures, typically paired with ExpenseType.Capital
Majik Runway treats all expenses as cash-paid in the month they occur. If amortization, deferred payments, or financing effects are required, they must be modeled explicitly.
Rules:
- Expenses reduce cash balance
- Expenses contribute directly to burn
- Expenses are assumed to be paid in the month they occur
- Depreciation, amortization, and accruals are out of scope
Majik Runway intentionally favors cash realism over accounting abstraction.
Funding
Funding represents external capital injections that increase available cash without generating revenue.
Supported Funding Types:
Equity
Capital raised in exchange for ownership (e.g. angel or VC funding).
- Increases cash balance
- Does not count as revenue
- Does not affect profit or margins
- Equity dilution is out of scope
Debt
- Borrowed capital that must be repaid.
- Increases cash balance at issuance
- Principal repayment and interest are not implicit
- Interest and repayments must be modeled explicitly as expenses
Grant
Non-dilutive capital typically provided by institutions or programs.
- Increases cash balance
- Does not count as revenue
- Assumed to have no repayment obligation
- Funding Modeling Rules
- Funding occurs at a specific YYYYMM
- Funding increases cash only
- Funding does not reduce burn directly
- Funding does not imply future obligations unless explicitly modeled
Majik Runway intentionally separates capital flow from operational performance.
Relationship to Burn & Runway
- Expenses and taxes increase burn
- Revenue and funding reduce burn
- Funding extends runway without improving profitability
- Capital-heavy strategies may show strong cash early but poor burn efficiency
Burn Rate
Burn rate represents net cash loss per month.
Formula (simplified):
Burn = Cash Outflows − Cash InflowsWhere:
- Outflows include expenses and taxes
- Inflows include revenue collections and funding
Key distinctions:
- Burn is a cash metric, not a profit metric
- A company can be profitable but still burn cash
- A company can have revenue and still burn aggressively
Cash Flow
Cash flow is tracked explicitly per month:
Formula (simplified):
Closing Cash = [Opening Cash] + [Cash Inflows] − [Cash Outflows]There is no hidden balancing or correction layer.
If cash goes negative:
- The model reflects insolvency
- Runway is considered exhausted
Runway
Runway answers a single question:
How many months can the company survive before cash reaches zero?
Runway is derived from:
- Current cash balance
- Forward-looking burn trajectory
- Planned funding events
Runway is dynamic, not static — it changes as assumptions change.
Profit vs Cash (Important)
Majik Runway makes a deliberate distinction:
| Concept | Meaning | | ------- | ------------------ | | Revenue | Value earned | | Profit | Revenue − Expenses | | Cash | Money available | | Burn | Net cash loss |
Majik Runway optimizes for cash truth, not accounting compliance.
What Majik Runway Is (and Is Not)
It is:
- A financial planning engine
- A runway and burn simulator
- A decision-support tool
It is not:
- An accounting system
- A tax filing tool
- A replacement for a CFO
Installation
npm i @thezelijah/majik-runway @thezelijah/majik-money @thezelijah/majik-product @thezelijah/majik-service @thezelijah/majik-subscriptionQuick Start
import { MajikRunway } from "@thezelijah/majik-runway";
import { MajikMoney } from "@thezelijah/majik-money";
import { BusinessModelType, VATMode } from "@thezelijah/majik-runway/enums";
import { RevenueStream } from "@thezelijah/majik-runway/revenue";
import { ExpenseBreakdown } from "@thezelijah/majik-runway/expenses";
import {
dateToYYYYMM,
offsetMonthsToYYYYMM,
} from "@thezelijah/majik-runway/utils";
const revenueStream = new RevenueStream("PHP");
const expenseBreakdown = new ExpenseBreakdown("PHP");
const fundingManager = new FundingManager("PHP");
const runway = MajikRunway.initialize({
type: BusinessModelType.Hybrid,
money: MajikMoney.fromMajor(1000000, "PHP"), // initial cash
taxConfig: {
vatMode: VATMode.VAT,
vatRate: 0.12,
percentageTaxRate: 0.03,
incomeTaxRate: 0.08,
},
revenues: revenueStream,
expenses: expenseBreakdown,
funding: fundingManager,
period: {
startMonth: dateToYYYYMM(new Date()),
endMonth: offsetMonthsToYYYYMM(dateToYYYYMM(new Date()), 23),
},
});Usage
Initialize a MajikRunway Instance
Majik Runway is initialized using a configuration object that defines your business model, starting cash, taxes, revenue streams, and expenses.
import { MajikRunway } from "@thezelijah/majik-runway";
import { MajikMoney } from "@thezelijah/majik-money";
import { BusinessModelType, VATMode } from "@thezelijah/majik-runway/enums";
import { RevenueStream } from "@thezelijah/majik-runway/revenue";
import { ExpenseBreakdown } from "@thezelijah/majik-runway/expenses";
import {
dateToYYYYMM,
offsetMonthsToYYYYMM,
} from "@thezelijah/majik-runway/utils";
const revenueStream = new RevenueStream("PHP");
const expenseBreakdown = new ExpenseBreakdown("PHP");
const fundingManager = new FundingManager("PHP");
const runway = MajikRunway.initialize({
type: BusinessModelType.Hybrid,
money: MajikMoney.fromMajor(1000000, "PHP"), // initial cash
taxConfig: {
vatMode: VATMode.VAT,
vatRate: 0.12,
percentageTaxRate: 0.03,
incomeTaxRate: 0.08,
},
revenues: revenueStream,
expenses: expenseBreakdown,
funding: fundingManager,
period: {
startMonth: dateToYYYYMM(new Date()),
endMonth: offsetMonthsToYYYYMM(dateToYYYYMM(new Date()), 23),
},
});Business Models
The business model determines how revenue streams behave and how financial assumptions are applied.
- BusinessModelType.Product
- BusinessModelType.Service
- BusinessModelType.Subscription
- BusinessModelType.Hybrid
Hybrid models allow products, services, and subscriptions to coexist.
Period Configuration
Majik Runway operates on a monthly YYYYMM timeline.
import {
dateToYYYYMM,
offsetMonthsToYYYYMM,
} from "@thezelijah/majik-runway/utils";
const period = {
startMonth: dateToYYYYMM(new Date()),
endMonth: offsetMonthsToYYYYMM(dateToYYYYMM(new Date()), 23),
};
runway.setPeriod(period);This automatically updates the period across the revenue stream, funding, and expenses.
Changing the period automatically:
- Reprojects revenue
- Reallocates expenses
- Updates burn & runway
Revenue Streams
Revenue streams are managed independently and injected into Majik Runway.
They may internally contain:
- MajikProduct
- MajikService
- MajikSubscription
Revenue streams define:
- Price
- Volume
- Growth
- Start & end dates
Majik Runway does not dictate how revenue is computed — it only aggregates results.
Add/Manage Products to Revenue Stream
import { MajikProduct } from "@thezelijah/majik-product";
import { MajikRunway } from "@thezelijah/majik-runway";
import { MajikMoney } from "@thezelijah/majik-money";
import { BusinessModelType, VATMode } from "@thezelijah/majik-runway/enums";
import { RevenueStream } from "@thezelijah/majik-runway/revenue";
import { ExpenseBreakdown } from "@thezelijah/majik-runway/expenses";
import {
monthsInPeriod,
offsetMonthsToYYYYMM,
} from "@thezelijah/majik-runway/utils";
// Create a new Product Instance
const newProduct = MajikProduct.initialize(
"Brand Z Phone",
ProductType.PHYSICAL,
MajikMoney.fromMajor(24500, "PHP")
);
// Prepare and create COGS manually - make sure unitCost and subtotal are MajikMoney class instances (Use MajikMoney.fromMajor())
const productCOGS = [
{
id: "mjkpcost-vyR7lY7r",
item: "Plastic",
quantity: 50,
unitCost: {
__type: "MajikMoney",
amount: "100",
currency: "PHP",
},
unit: "PELLETS",
subtotal: {
__type: "MajikMoney",
amount: "5000",
currency: "PHP",
},
},
{
id: "mjkpcost-wKxE3TT6",
item: "Metal Parts",
quantity: 5,
unitCost: {
__type: "MajikMoney",
amount: "2000",
currency: "PHP",
},
unit: "G",
subtotal: {
__type: "MajikMoney",
amount: "10000",
currency: "PHP",
},
},
{
id: "mjkpcost-SMWmI7LU",
item: "Glass Parts",
quantity: 1,
unitCost: {
__type: "MajikMoney",
amount: "10000",
currency: "PHP",
},
unit: "PIECE",
subtotal: {
__type: "MajikMoney",
amount: "10000",
currency: "PHP",
},
},
];
// Set Cost of Goods and replace the entire list/array
newProduct.setCOGS([[...productCOGS]]);
// OR push one COG at a time (automatic generation of ID and auto computation of subtotal)
newProduct.addCOGS(
"Metal Parts",
MajikMoney.fromMajor(parseFloat(2000), "PHP"),
5,
"G"
);
// Generate Capacity Plan
newProduct.generateCapacityPlan(
!!period ? monthsInPeriod(period.startMonth, period.endMonth) : 24, // Number of months to generate from the start date.
capacityPerMonth, // Base units for the first month.
capacityGrowthRate // Optional growth rate per month (e.g. 0.03 = +3%).
);
// OR Manually Set Capacity Plan
const productCapacityPlan = [
{
month: "2025-12",
capacity: 8,
},
{
month: "2026-01",
capacity: 9,
},
{
month: "2026-02",
capacity: 9,
},
{
month: "2026-03",
capacity: 10,
},
{
month: "2026-04",
capacity: 10,
},
{
month: "2026-05",
capacity: 11,
},
{
month: "2026-06",
capacity: 12,
},
{
month: "2026-07",
capacity: 13,
},
{
month: "2026-08",
capacity: 14,
},
{
month: "2026-09",
capacity: 15,
},
{
month: "2026-10",
capacity: 16,
},
{
month: "2026-11",
capacity: 17,
},
{
month: "2026-12",
capacity: 18,
},
{
month: "2027-01",
capacity: 19,
},
{
month: "2027-02",
capacity: 21,
},
{
month: "2027-03",
capacity: 22,
},
{
month: "2027-04",
capacity: 24,
},
{
month: "2027-05",
capacity: 25,
},
{
month: "2027-06",
capacity: 27,
},
{
month: "2027-07",
capacity: 29,
},
{
month: "2027-08",
capacity: 31,
},
{
month: "2027-09",
capacity: 33,
},
{
month: "2027-10",
capacity: 35,
},
{
month: "2027-11",
capacity: 38,
},
];
newProduct.setCapacity([...productCapacityPlan]);
// Dynamically change the price if needed
newProduct.setSRP(MajikMoney.fromMajor(12500, "PHP"));
// Dynamically change the name if needed
newProduct.setName("Brand XYZ Phone");
//Dynamically set a description either both in text/html or either (TipTap compatible)
newProduct.setDescription(
`<p>This is a new product</p>`, // HTML Content
"This is a new product" // Plain Text
);
newProduct.setDescriptionText(
"This is a new product" // Plain Text
);
newProduct.setDescriptionHTML(
`<p>This is a new product</p>` // HTML Content
);
// When done, validate before finalizing and passing to revenue stream
newProduct.validateSelf(true); // Set to true to throw an error, Defaults to false - returns a plain boolean for custom conditions
// Use addRevenue from the MajikRunway class to add this new product
runway.addRevenue(newProduct);
The new
MajikProductwill now be added as a new item inRevenueStreamand return an updatedMajikRunwayinstance.
Add/Manage Services to Revenue Stream
import { MajikService } from "@thezelijah/majik-service";
import { MajikRunway } from "@thezelijah/majik-runway";
import { MajikMoney } from "@thezelijah/majik-money";
import { BusinessModelType, VATMode } from "@thezelijah/majik-runway/enums";
import { RevenueStream } from "@thezelijah/majik-runway/revenue";
import { ExpenseBreakdown } from "@thezelijah/majik-runway/expenses";
import {
monthsInPeriod,
offsetMonthsToYYYYMM,
} from "@thezelijah/majik-runway/utils";
// Create a new Service Instance
const newService = MajikService.initialize(
"Home Cleaning",
ServiceType.TIME_BASED,
{
amount: MajikMoney.fromMajor(250, "PHP"),
unit: RateUnit.PER_HOUR
}
);
// Prepare and create COS manually - make sure unitCost and subtotal are MajikMoney class instances (Use MajikMoney.fromMajor())
const serviceCOS = [
{
"id": "mjkscost-7SgAoOq2",
"item": "Labor",
"quantity": 1,
"unitCost": {
"__type": "MajikMoney",
"amount": "8000",
"currency": "PHP"
},
"unit": "PERSON",
"subtotal": {
"__type": "MajikMoney",
"amount": "8000",
"currency": "PHP"
}
},
{
"id": "mjkscost-RZPXh7pP",
"item": "Cleaning Solutions",
"quantity": 1,
"unitCost": {
"__type": "MajikMoney",
"amount": "3500",
"currency": "PHP"
},
"unit": "PACKAGE",
"subtotal": {
"__type": "MajikMoney",
"amount": "3500",
"currency": "PHP"
}
}
];
// Set Cost of Service and replace the entire list/array
newService.setCOS([...serviceCOS]);
// OR push one COS at a time (automatic generation of ID and auto computation of subtotal)
newService.addCOS(
"Labor",
MajikMoney.fromMajor(parseFloat(8000), "PHP"),
1,
"PERSON"
);
// Generate Capacity Plan
newService.generateCapacityPlan(
!!period ? monthsInPeriod(period.startMonth, period.endMonth) : 24, // Number of months to generate from the start date.
capacityPerMonth, // Base units for the first month.
capacityGrowthRate // Optional growth rate per month (e.g. 0.03 = +3%).
);
// OR Manually Set Capacity Plan
const serviceCapacityPlan = [
{
"month": "2025-12",
"capacity": 15
},
{
"month": "2026-01",
"capacity": 16
},
{
"month": "2026-02",
"capacity": 18
},
{
"month": "2026-03",
"capacity": 19
},
{
"month": "2026-04",
"capacity": 21
},
{
"month": "2026-05",
"capacity": 23
},
{
"month": "2026-06",
"capacity": 25
},
{
"month": "2026-07",
"capacity": 27
},
{
"month": "2026-08",
"capacity": 30
},
{
"month": "2026-09",
"capacity": 33
},
{
"month": "2026-10",
"capacity": 36
},
{
"month": "2026-11",
"capacity": 39
},
{
"month": "2026-12",
"capacity": 42
},
{
"month": "2027-01",
"capacity": 46
},
{
"month": "2027-02",
"capacity": 50
},
{
"month": "2027-03",
"capacity": 55
},
{
"month": "2027-04",
"capacity": 60
},
{
"month": "2027-05",
"capacity": 65
},
{
"month": "2027-06",
"capacity": 71
},
{
"month": "2027-07",
"capacity": 77
},
{
"month": "2027-08",
"capacity": 84
},
{
"month": "2027-09",
"capacity": 92
},
{
"month": "2027-10",
"capacity": 100
},
{
"month": "2027-11",
"capacity": 109
}
];
newService.setCapacity([...serviceCapacityPlan]);
// Dynamically change the rate price if needed
newService.setRateAmount(400); // Raw number will automatically be parsed into MajikMoney
// Dynamically change the rate unit if needed
/*
PER_HOUR = "Per Hour",
PER_DAY = "Per Day",
PER_SESSION = "Per Session",
FIXED = "Per Fixed",
PER_UNIT = "Per Unit", // e.g., usage-based
*/
newService.setRateUnit(RateUnit.PER_HOUR);
// Dynamically change the name if needed
newService.setName("General Cleaning");
//Dynamically set a description either both in text/html or either (TipTap compatible)
newService.setDescription(
`<p>This is a new service</p>`, // HTML Content
"This is a new service" // Plain Text
);
newService.setDescriptionText(
"This is a new service" // Plain Text
);
newService.setDescriptionHTML(
`<p>This is a new service</p>` // HTML Content
);
// When done, validate before finalizing and passing to revenue stream
newService.validateSelf(true); // Set to true to throw an error, Defaults to false - returns a plain boolean for custom conditions
// Use addRevenue from the MajikRunway class to add this new service
runway.addRevenue(newService);
The new
MajikServicewill now be added as a new item inRevenueStreamand return an updatedMajikRunwayinstance.
Add/Manage Subscriptions to Revenue Stream
import { MajikSubscription } from "@thezelijah/majik-subscription";
import { MajikRunway } from "@thezelijah/majik-runway";
import { MajikMoney } from "@thezelijah/majik-money";
import { BusinessModelType, VATMode } from "@thezelijah/majik-runway/enums";
import { RevenueStream } from "@thezelijah/majik-runway/revenue";
import { ExpenseBreakdown } from "@thezelijah/majik-runway/expenses";
import {
monthsInPeriod,
offsetMonthsToYYYYMM,
} from "@thezelijah/majik-runway/utils";
// Create a new Subscription Instance
const newSubscription = MajikSubscription.initialize(
"Majik Distro Pro",
SubscriptionType.RECURRING,
{
amount: MajikMoney.fromMajor(999,"PHP"),
unit: RateUnit.PER_USER,
billingCycle: BillingCycle.MONTHLY,
}
);
// Prepare and create COS manually - make sure unitCost and subtotal are MajikMoney class instances (Use MajikMoney.fromMajor())
const subscriptionCOS = [
{
"id": "mjksubcost-Co7mq4hj",
"item": "Cloud Services",
"quantity": 1,
"unitCost": {
"__type": "MajikMoney",
"amount": "2500",
"currency": "PHP"
},
"unit": "MONTH",
"subtotal": {
"__type": "MajikMoney",
"amount": "2500",
"currency": "PHP"
}
},
{
"id": "mjksubcost-fm1bQNGp",
"item": "Customer Support",
"quantity": 1,
"unitCost": {
"__type": "MajikMoney",
"amount": "2000",
"currency": "PHP"
},
"unit": "MONTH",
"subtotal": {
"__type": "MajikMoney",
"amount": "2000",
"currency": "PHP"
}
}
];
// Set Cost of Service and replace the entire list/array
newSubscription.setCOS([...subscriptionCOS]);
// OR push one COS at a time (automatic generation of ID and auto computation of subtotal)
newSubscription.addCOS(
"Cloud Services",
MajikMoney.fromMajor(parseFloat(25), "PHP"),
1,
"MONTH"
);
// Generate Capacity Plan
newSubscription.generateCapacityPlan(
!!period ? monthsInPeriod(period.startMonth, period.endMonth) : 24, // Number of months to generate from the start date.
capacityPerMonth, // Base units for the first month.
capacityGrowthRate // Optional growth rate per month (e.g. 0.03 = +3%).
);
// OR Manually Set Capacity Plan
const subscriptionCapacityPlan = [
{
"month": "2025-12",
"capacity": 16
},
{
"month": "2026-01",
"capacity": 16
},
{
"month": "2026-02",
"capacity": 17
},
{
"month": "2026-03",
"capacity": 17
},
{
"month": "2026-04",
"capacity": 18
},
{
"month": "2026-05",
"capacity": 19
},
{
"month": "2026-06",
"capacity": 19
},
{
"month": "2026-07",
"capacity": 20
},
{
"month": "2026-08",
"capacity": 20
},
{
"month": "2026-09",
"capacity": 21
},
{
"month": "2026-10",
"capacity": 22
},
{
"month": "2026-11",
"capacity": 22
},
{
"month": "2026-12",
"capacity": 23
},
{
"month": "2027-01",
"capacity": 23
},
{
"month": "2027-02",
"capacity": 24
},
{
"month": "2027-03",
"capacity": 25
},
{
"month": "2027-04",
"capacity": 26
},
{
"month": "2027-05",
"capacity": 26
},
{
"month": "2027-06",
"capacity": 27
},
{
"month": "2027-07",
"capacity": 28
},
{
"month": "2027-08",
"capacity": 29
},
{
"month": "2027-09",
"capacity": 30
},
{
"month": "2027-10",
"capacity": 31
},
{
"month": "2027-11",
"capacity": 32
}
];
newSubscription.setCapacity([...subscriptionCapacityPlan]);
// Dynamically change the rate price if needed
newSubscription.setRateAmount(400); // Raw number will automatically be parsed into MajikMoney
// Dynamically change the rate unit if needed
/*
PER_SUBSCRIBER = "Per Subscriber",
PER_ACCOUNT = "Per Account",
PER_USER = "Per User",
PER_MONTH = "Per Month"
*/
newSubscription.setRateUnit(RateUnit.PER_MONTH);
// Dynamically change the billing cycle if needed
/*
DAILY = "Daily",
WEEKLY = "Weekly",
MONTHLY = "Monthly",
QUARTERLY = "Quarterly",
YEARLY = "Yearly"
*/
newSubscription.setBillingCycle(BillingCycle.MONTHLY);
// Dynamically change the name if needed
newSubscription.setName("Majik Distro Advanced Plus");
//Dynamically set a description either both in text/html or either (TipTap compatible)
newSubscription.setDescription(
`<p>This is a new subscription</p>`, // HTML Content
"This is a new subscription" // Plain Text
);
newSubscription.setDescriptionText(
"This is a new subscription" // Plain Text
);
newSubscription.setDescriptionHTML(
`<p>This is a new subscription</p>` // HTML Content
);
// When done, validate before finalizing and passing to revenue stream
newSubscription.validateSelf(true); // Set to true to throw an error, Defaults to false - returns a plain boolean for custom conditions
// Use addRevenue from the MajikRunway class to add this new subscription
runway.addRevenue(newSubscription);
The new MajikSubscription will now be added as a new item in
RevenueStreamand return an updatedMajikRunwayinstance.
Update Existing Revenue Item
// To edit/update an item
const updatedRevisedProduct = newProduct.setName("Hello Phone");
// This method accepts MajikProduct, MajikService or MajikSubscription
runway.updateRevenueItem(
updatedRevisedProduct.id, // The ID of the product you want to update
updatedRevisedProduct // The updated product
);
The updated
MajikProduct/MajikService/MaikSubscriptionwill now be reflected in the currentRevenueStreamand return an updatedMajikRunwayinstance.
Remove an Existing Revenue Item
// To remove/delete an item
runway.removeRevenueByID(
updatedItem.id // The ID of the product you want to remove
);
The removed
MajikProduct/MajikService/MaikSubscriptionwill now be reflected in the currentRevenueStreamand return an updatedMajikRunwayinstance.
Expenses and Operating Costs
Expenses are grouped under an ExpenseBreakdown.
Expense instances are immutable and return a new instance when updated.
Expenses directly affect:
- Monthly burn
- Cash balance
- Runway length
Expense types may include:
- Fixed
- Variable
- One-time
Add/Manage Expenses
There are 3 main types of expenses you can create:
- One Time
- Recurring
- Capital
Create Recurring Expense
Recurring expenses represent predictable, repeating cash outflows that occur every month within an active period.
Examples include:
- Salaries and contractor payments
- Rent and utilities
- Software subscriptions
- Baseline infrastructure costs
Characteristics:
- Applied monthly between a defined start and end period
- Reduce cash balance every month they are active
- Contribute directly to monthly burn
- Commonly classified as
OperatingorVariable
Recurring expenses form the structural baseline of a company’s burn rate.
import { Expense } from "@thezelijah/majik-runway/expenses/expense";
import { Recurrence } from "./enums";
const newExpense = Expense.recurring(
undefined, // Optional ID. If undefined, automatically generates one
"Office Rent",
15000, // Automatically parses into MajikMoney
"PHP",
Recurrence.Monthly,
period, // use the same Period you have from your MajikRunway instance
true, // Set to false if not Tax Deductible (defaults to false)
);
// Use addExpense from the MajikRunway class to add this new expense item
runway.addExpense(newExpense)
// To quickly create and add directly use this method (No need to input period, uses the runway's period)
runway.addRecurringExpense(
"Office Rent",
15000, // Automatically parses into MajikMoney
"PHP",
Recurrence.Monthly,
true, // Set to false if not Tax Deductible (defaults to false)
undefined // Optional ID. If undefined, automatically generates one
)
The new
Expensewill now be added as a new item inExpenseBreakdownand return an updatedMajikRunwayinstance.
Create One Time Expense
One-time expenses represent single, non-recurring cash outflows that occur in a specific month.
Examples include:
- Legal and incorporation fees
- Marketing launches or campaigns
- One-off consulting or audit work
- Setup or migration costs
Characteristics:
- Occur once at a specific
YYYYMM - Reduce cash in the month they occur
- Do not persist or repeat
- May be classified as
OperatingorVariable
One-time expenses often cause short-term spikes in burn but do not affect long-term baseline costs.
import { Expense } from "@thezelijah/majik-runway/expenses/expense";
const newExpense = Expense.oneTime(
undefined, // Optional ID. If undefined, automatically generates one
"Event PubMats",
35000, // Automatically parses into MajikMoney
"PHP",
"2026-10", // Date in YYYY-MM string format
true, // Set to false if not Tax Deductible (defaults to false)
);
// Use addExpense from the MajikRunway class to add this new expense item
runway.addExpense(newExpense)
// To quickly create and add directly use this method
runway.addOneTimeExpense(
"Event PubMats",
35000, // Automatically parses into MajikMoney
"PHP",
"2026-10", // Date in YYYY-MM string format
true, // Set to false if not Tax Deductible (defaults to false)
undefined // Optional ID. If undefined, automatically generates one
)
The new
Expensewill now be added as a new item inExpenseBreakdownand return an updatedMajikRunwayinstance.
Create Capital Expense
Capital expenses represent large, strategic cash outflows intended to support long-term operations or growth.
Examples include:
- Equipment and hardware purchases
- Initial product or platform build costs
- Long-term tooling or infrastructure investments
Characteristics:
- Typically one-time and high-value
- Classified explicitly as
ExpenseType.Capital - Reduce cash immediately in the month they occur
- Are not depreciated or amortized
In Majik Runway, capital expenses are treated as direct cash outflows to preserve cash-flow realism.
import { Expense } from "@thezelijah/majik-runway/expenses/expense";
const newExpense = Expense.capital(
undefined, // Optional ID. If undefined, automatically generates one
"Photocopy Machine",
35000, // Automatically parses into MajikMoney
"PHP",
"2026-10", // Date in YYYY-MM string format
36, // Number of months to depreciate
undefined, // Optional residual value after depreciation
true, // Set to false if not Tax Deductible (defaults to false)
);
// Use addExpense from the MajikRunway class to add this new expense item
runway.addExpense(newExpense)
// To quickly create and add directly use this method
runway.addCapitalExpense(
"Photocopy Machine",
35000, // Automatically parses into MajikMoney
36, // Number of months to depreciate
"PHP",
"2026-10", // Date in YYYY-MM string format
undefined, // Optional residual value after depreciation
true, // Set to false if not Tax Deductible (defaults to false)
undefined // Optional ID. If undefined, automatically generates one
)
The new expense will now be added as a new item in expense breakdown and return an updated MajikRunway instance.
Update an Existing Expense Item
// To update an existing item
const updatedRevisedExpense = newExpense
.rename("Hello Phone") // Renames the expense
.withAmount(MajikMoney.fromMajor(500),"PHP"); // Updates the amount
// Important: Since expense instances are immutable, all update methods return a new instance
// This method accepts Expense
runway.updateExpenseItem(
updatedRevisedExpense.id, // The ID of the expense you want to update
updatedRevisedExpense // The updated expense instance
);
The updated expense will now be reflected in the current expense breakdown and return an updated MajikRunway instance.
Remove an Existing Expense Item
// To remove/delete an item
runway.removeExpenseByID(
updatedItem.id // The ID of the expense you want to remove
);
The removed expense will now be reflected in the current expense breakdown and return an updated MajikRunway instance.
Funding
Funding events are managed under the FundingManager.
Funding instances are immutable and return a new instance when updated.
Funding directly affects:
- Cash balance
- Runway length
- Liquidity over time
Funding does not directly affect:
- Revenue
- Profit or margins
- Operating burn
Each funding event:
- Occurs at a specific
YYYYMM - Injects cash into the model
- Is treated as a non-operational cash inflow
- Has no implicit dilution, repayment, or interest unless explicitly modeled
Majik Runway treats funding as capital flow, fully decoupled from operational performance.
Add/Manage Funding Events
There are 3 main types of funding events you can create:
- Equity
- Debt
- Grant
Create Equity Funding Event
Equity funding represents capital raised in exchange for ownership in the company.
Examples include:
- Angel investments
- Venture capital rounds
- Founder capital contributions
Characteristics:
- Increases cash balance in the month received
- Does not count as revenue
- Does not affect profit or margins
- Has no implicit dilution modeling
In Majik Runway, equity funding is treated purely as a cash injection, separate from ownership or valuation mechanics.
import { FundingEvent } from "@thezelijah/majik-runway/funding/funding";
const newFundingEvent = FundingEvent.equity(
"Round 1 Funding",
300000, // Automatically parses into MajikMoney
"2026-10", // Date in YYYY-MM string format
"PHP",
undefined // Optional ID. If undefined, automatically generates one
);
// Use addFundingEvent from the MajikRunway class to add this new funding event
runway.addFundingEvent(newFundingEvent)
// To quickly create and add directly use this method
runway.addEquity(
"Round 1 Funding",
300000, // Automatically parses into MajikMoney
"2026-10", // Date in YYYY-MM string format
undefined // Optional ID. If undefined, automatically generates one
)
The new
FundingEventwill now be added as a new item insideFundingManagerand return an updatedMajikRunwayinstance.
Create Debt Funding Event
import { FundingEvent } from "@thezelijah/majik-runway/funding/funding";
const newFundingEvent = FundingEvent.debt(
"BPI Loan",
300000, // Automatically parses into MajikMoney
"2026-10", // Date in YYYY-MM string format
new Date().toISOString(), // ISO date string representing when the debt must be fully repaid
"PHP",
0.05, // Annual interest rate as a decimal ratio (default: 0, e.g., 0.05 for 5%)
0, // Upfront payment at the start of the loan (default: 0)
undefined // Optional ID. If undefined, automatically generates one
);
// Use addFundingEvent from the MajikRunway class to add this new funding event
runway.addFundingEvent(newFundingEvent)
// To quickly create and add directly use this method
runway.addDebt(
"BPI Loan",
300000, // Automatically parses into MajikMoney
"2026-10", // Date in YYYY-MM string format
new Date().toISOString(), // ISO date string representing when the debt must be fully repaid
"PHP",
0.05, // Annual interest rate as a decimal ratio (default: 0, e.g., 0.05 for 5%)
0, // Upfront payment at the start of the loan (default: 0)
undefined // Optional ID. If undefined, automatically generates one
)
The new
FundingEventwill now be added as a new item insideFundingManagerand return an updatedMajikRunwayinstance.
Create Grant Funding Event
import { FundingEvent } from "@thezelijah/majik-runway/funding/funding";
const newFundingEvent = FundingEvent.grant(
"Round 1 Funding",
300000, // Automatically parses into MajikMoney
"2026-10", // Date in YYYY-MM string format
"PHP",
undefined // Optional ID. If undefined, automatically generates one
);
// Use addFundingEvent from the MajikRunway class to add this new funding event
runway.addFundingEvent(newFundingEvent)
// To quickly create and add directly use this method
runway.addGrant(
"Round 1 Funding",
300000, // Automatically parses into MajikMoney
"2026-10", // Date in YYYY-MM string format
undefined // Optional ID. If undefined, automatically generates one
)
The new
FundingEventwill now be added as a new item insideFundingManagerand return an updatedMajikRunwayinstance.
Update an Existing Funding Event
// To update an existing item
const updatedRevisedFunding= newFundingEvent
.rename("HSBC Loan") // Renames the funding event
.reschedule("2028-03") // Reschedules the funding to another date
.withAmount(MajikMoney.fromMajor(250000),"PHP"); // Updates the amount
// Important: Since fudning event instances are immutable, all update methods return a new instance
// This method accepts FundingEvent
runway.updateFundingEvent(
updatedRevisedFunding.id, // The ID of the expense you want to update
updatedRevisedFunding // The updated expense instance
);
The updated
FundingEventwill now be reflected in the currentFundingManagerand return an updatedMajikRunwayinstance.
Remove an Existing Funding Event
// To remove/delete an item
runway.removeFundingByID(
updatedItem.id // The ID of the funding event you want to remove
);
The removed
FundingEventwill now be reflected in the currentFundingManagerand return an updatedMajikRunwayinstance.
Tax Configuration
Majik Runway supports VAT and Non-VAT systems with income tax modeling.
import { TaxConfig } from "@thezelijah/majik-runway/types/tax";
// To set/update your MajikRunway instance's tax config pass the new taxconfig object to the updateModel
const newTaxConfig: TaxConfig = {
vatMode: VATMode.VAT,
vatRate: 0.12, // VAT businesses
percentageTaxRate: 0.03, // Non-VAT businesses
incomeTaxRate: 0.08,
}
runway.updateTaxConfig(newTaxConfig);
Rules:
- VAT & Percentage Tax are mutually exclusive
- Taxes are applied automatically to projections
- Net cashflow reflects tax impact
Initial Cash & Currency
Initial cash defines your starting runway position (opening balance).
// Construct the initial cash using MajikMoney
const initialCash = MajikMoney.fromMajor(15000,"PHP");
// Include the initial cash upon setup/initialization of your MajikRunway instance
const runwayInstance = MajikRunway.initialize({
// .. insert other parameters
money: initialCash,
});
// To set/update your MajikRunway instance's initial cash pass the new MajikMoney to the updateModel() method of MajikRunway
runway.updateModel({
money: MajikMoney.fromMajor(25000,"PHP"),
});
// Or to quickly update it directly
runway.setInitialCash(MajikMoney.fromMajor(25000,"PHP"));
Rules:
- All internal computations use MajikMoney
- Currency consistency is enforced
- Avoid raw numbers
Updating the Model
Majik Runway is designed to be mutable.
You can update:
- Business model
- Period
- Tax assumptions
- Revenue streams (Products, Services, and Subscription Plans)
- Expenses
- Initial Cash (Opening Balance)
All changes recompute projections instantly.
interface BusinessModel {
money: MajikMoney;
expenses: ExpenseBreakdown;
revenues: RevenueStream;
taxConfig: TaxConfig;
funding: FundingManager;
type?: BusinessModelType;
period: PeriodYYYYMM;
id?: string;
}
runway.updateModel({
// pass in partial properties
});
Mental Model
[ Assumptions ]
↓
[ Revenue Streams ] + [ Expenses ] + [ Funding ] + [ Taxes ]
↓
[ Monthly Cashflow Engine ]
↓
[ Burn Rate & Runway Projection ]
MajikRunwayis the central financial engine.- Revenue, Expense, Funding, Tax are pluggable sub-managers.
- Dashboard Snapshot is your single source of truth for rendering.
- Updating any sub-manager triggers a refresh / recompute.
- Charts consume methods from each sub-manager for time-series or trend plotting.
- Dynamic coloring thresholds provide visual alerts for financial health.
Why This Design Works
- Finance-correct but intuitive
- No spreadsheet drift
- Reactive & composable
- UI-agnostic
- Investor-ready projections
Example Usage
import {
SubscriptionType,
RateUnit,
BillingCycle,
CapacityPeriodResizeMode,
} from "@thezelijah/majik-subscription/enums";
import { MajikMoney } from "@thezelijah/majik-money";
const proPlan = MajikSubscription.initialize(
"Pro Plan",
SubscriptionType.RECURRING,
{
amount: MajikMoney.fromMajor(499, "PHP"),
unit: RateUnit.PER_USER,
billingCycle: BillingCycle.MONTHLY,
},
"Advanced SaaS plan",
"PRO-PLAN-001"
)
.setDescriptionHTML("<p>Best plan for growing teams.</p>")
.setDescriptionSEO("Pro SaaS subscription plan")
.addCOS("Cloud Hosting", MajikMoney.fromMajor(300, "PHP"), 1, "per user")
.addCOS("Customer Support", MajikMoney.fromMajor(100, "PHP"), 1, "per user")
.generateCapacityPlan(12, 500) // 12 months, 500 subscribers
.recomputeCapacityPeriod(
"2025-01",
"2025-12",
CapacityPeriodResizeMode.DISTRIBUTE
);
// Capacity insights
console.log("Total Capacity:", proPlan.totalCapacity);
// Monthly finance
const month = "2025-06";
console.log(`${month} Revenue:`, proPlan.getRevenue(month).value.toFormat());
console.log(`${month} COS:`, proPlan.getCOS(month).value.toFormat());
console.log(`${month} Profit:`, proPlan.getProfit(month).value.toFormat());
console.log(`${month} Margin:`, proPlan.getMargin(month).toFixed(2) + "%");
// Serialization
const json = proPlan.toJSON();
const restored = MajikSubscription.parseFromJSON(json);
console.log("Restored Subscription:", restored.metadata.description.text);Dashboard Integration & Real-Time Financial Modeling
This section demonstrates how Majik Runway is designed to be used inside a production-grade dashboard. Instead of treating financial logic as isolated calculations, Majik Runway acts as a single source of truth that powers KPIs, charts, health indicators, and editable financial modules in real time.
The example below shows how to wire MajikRunway into a React / Next.js dashboard, enabling:
- Live runway and burn calculations
- Revenue, expense, funding, and tax modeling
- Health indicators and financial risk signals
- Interactive charts and managers
- Immutable updates with snapshot-based rendering
"use client";
import React, { useMemo, useState } from "react";
import styled from "styled-components";
import {
ChartLineIcon,
CurrencyDollarSimpleIcon,
GaugeIcon,
CoinsIcon,
GearIcon,
BankIcon,
InfoIcon,
TrendUpIcon,
FireIcon,
ScalesIcon,
CalendarXIcon,
} from "@phosphor-icons/react";
import DynamicPagedTab, {
TabContent,
} from "@/components/functional/DynamicPagedTab";
import { MajikRunway } from "@/SDK/tools/business/majik-runway/majik-runway";
import ChartRevenueTrend from "./Charts/ChartRevenueTrend";
import ChartExpensePie from "./Charts/ChartExpensePie";
import ChartCashflowBar from "./Charts/ChartCashflowBar";
import RevenueStreamManager from "./RevenueStreams/RevenueStreamManager";
import ExpenseManager from "./Expenses/ExpenseManager";
import { RevenueStream } from "@/SDK/tools/business/majik-runway/revenue";
import { ExpenseBreakdown } from "@/SDK/tools/business/majik-runway/expenses/expense-breakdown";
import { toast } from "sonner";
import RunwayTaxConfig from "./RunwayTaxConfig";
import { TaxConfig } from "@/SDK/tools/business/majik-runway/types/tax";
import { formatPercentage } from "@/utils/helper";
import moment from "moment";
import { yyyyMMToDate } from "@/SDK/tools/business/majik-runway/utils";
import MajikRunwayHealthIndicator from "./MajikRunwayHealthIndicator";
import { DynamicColoredValue } from "@/components/foundations/DynamicColoredValue";
import theme from "@/globals/theme";
import FundingEventsManager from "./Funding/FundingEventsManager";
import { FundingManager } from "@/SDK/tools/business/majik-runway/funding/funding-manager";
import ChartFundingTimeSeries from "./Charts/ChartFundingTimeSeries";
import ChartExpenseTrend from "./Charts/ChartExpenseTrend";
import { TargetIcon } from "lucide-react";
import ChartGrossMarginTrend from "./Charts/ChartGrossMarginTrend";
// ======== Styled Components ========
const RootContainer = styled.div`
// css
`;
const HeaderCards = styled.div`
// css
`;
const TopHeaderRow = styled.div`
// css
`;
const Card = styled.div`
// css
`;
const CardTitle = styled.div`
// css
`;
const CardValue = styled.div`
// css
`;
const CardSubtext = styled.div`
// css
`;
const MainGrid = styled.div`
// css
`;
const ChartPlaceholder = styled.div`
// css
`;
interface DashboardMajikRunwayProps {
runway: MajikRunway;
onUpdate?: (newRunway: MajikRunway) => void;
}
// ======== Main Component ========
const DashboardMajikRunway: React.FC<DashboardMajikRunwayProps> = ({
runway,
onUpdate,
}) => {
const [refreshKey, setRefreshKey] = useState<number>(0);
const dashboardSnapshot = useMemo(
() => runway.getDashboardSnapshot(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[runway, refreshKey]
);
const revenueStream = useMemo(
() => runway.revenueStream(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[runway, refreshKey]
);
const expense = useMemo(
() => runway.expenseBreakdown(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[runway, refreshKey]
);
const fundingManager = useMemo(
() => runway.funding(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[runway, refreshKey]
);
const cashflow = useMemo(
() => runway.getCashflowPlotlyBar(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[runway, refreshKey]
);
const taxConfig = useMemo(
() => runway.taxConfig(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[runway, refreshKey]
);
const handleUpdateRevenueStream = (input: RevenueStream) => {
try {
console.log("Revenue Stream: ", input);
const updatedRunway = runway.updateModel({
revenues: input,
});
setRefreshKey((prev) => prev + 1);
onUpdate?.(updatedRunway);
} catch (error) {
console.error("Problem while updating Revenue Stream: ", error);
toast.error("Error", {
description: `Oops! There seems to be a problem while updating: ${error}`,
id: "error-input-majik-runway-dashboard-revenue",
});
}
};
const handleUpdateExpenseBreakdown = (input: ExpenseBreakdown) => {
try {
console.log("Expense Breakdown: ", input);
const updatedRunway = runway.updateModel({
expenses: input,
});
setRefreshKey((prev) => prev + 1);
onUpdate?.(updatedRunway);
} catch (error) {
console.error("Problem while updating Expense Breakdown: ", error);
toast.error("Error", {
description: `Oops! There seems to be a problem while updating: ${error}`,
id: "error-input-majik-runway-dashboard-expense",
});
}
};
const handleUpdateFundingManager = (input: FundingManager) => {
try {
console.log("Funding Manager: ", input);
const updatedRunway = runway.updateModel({
funding: input,
});
setRefreshKey((prev) => prev + 1);
onUpdate?.(updatedRunway);
} catch (error) {
console.error("Problem while updating Funding Manager: ", error);
toast.error("Error", {
description: `Oops! There seems to be a problem while updating: ${error}`,
id: "error-input-majik-runway-dashboard-funding",
});
}
};
const handleUpdateTaxConfig = (input: TaxConfig) => {
try {
console.log("Tax Config: ", input);
const updatedRunway = runway.updateModel({
taxConfig: input,
});
setRefreshKey((prev) => prev + 1);
onUpdate?.(updatedRunway);
} catch (error) {
console.error("Problem while updating Tax Config: ", error);
toast.error("Error", {
description: `Oops! There seems to be a problem while updating: ${error}`,
id: "error-input-majik-runway-dashboard-tax",
});
}
};
const InformationTabs: TabContent[] = [
{
id: "info-overview",
name: "Overview",
icon: InfoIcon,
content: (
<>
<TopHeaderRow>
<MajikRunwayHealthIndicator
health={dashboardSnapshot.runwayHealth}
/>
</TopHeaderRow>
{/* ===== Header / KPI Cards ===== */}
<HeaderCards>
<Card>
<CardTitle>
<CoinsIcon size={20} /> Runway Remaining
</CardTitle>
<CardValue>
<DynamicColoredValue
value={dashboardSnapshot.runwayMonths} // numeric months remaining
colorsMap={[
{ color: theme.colors.error, max: 2.99 }, // red if less than 3 months
{ color: theme.colors.brand.white, min: 3, max: 6 }, // yellow if 3–6 months
{ color: theme.colors.brand.green, min: 6.01 }, // green if above 6 months
]}
size={28}
weight={700}
>
{dashboardSnapshot.runwayMonths} months
</DynamicColoredValue>
</CardValue>
</Card>
<Card>
<CardTitle>
<CurrencyDollarSimpleIcon size={20} /> Opening Balance
</CardTitle>
<CardValue>{dashboardSnapshot.cashOnHand.format()}</CardValue>
</Card>
<Card>
<CardTitle>
<BankIcon size={20} /> Total Funding
</CardTitle>
<CardValue>
{dashboardSnapshot.funding.totalFunding.format()}
</CardValue>
</Card>
<Card>
<CardTitle>
<GaugeIcon size={20} /> Net Monthly Burn
</CardTitle>
<CardValue>
<DynamicColoredValue
value={dashboardSnapshot.avgNetBurn.toMajor()}
colorsMap={[
{ color: theme.colors.brand.green, max: -1 }, // green if negative
{ color: theme.colors.brand.white, min: 0, max: 0 }, // yellow if zero
{ color: theme.colors.error, min: 1 }, // red if positive
]}
size={28}
weight={700}
>
{dashboardSnapshot.avgNetBurn.format()}
</DynamicColoredValue>
</CardValue>
</Card>
<Card>
<CardTitle>
<ChartLineIcon size={20} /> Projected Revenue Next Month
</CardTitle>
<CardValue>
{dashboardSnapshot.nextMonthRevenue.format()}
</CardValue>
</Card>
</HeaderCards>
{/* ===== SubHeader / Secondary KPI Cards ===== */}
<HeaderCards>
<Card>
<CardTitle>
<TargetIcon size={20} />
Break-Even Month
</CardTitle>
<CardValue>
{dashboardSnapshot.breakEvenMonth ?? "Not Available"}
<CardSubtext>
{!!dashboardSnapshot.breakEvenMonth
? moment(
yyyyMMToDate(dashboardSnapshot.breakEvenMonth)
).fromNow()
: null}
</CardSubtext>
</CardValue>
</Card>
<Card>
<CardTitle>
<TrendUpIcon size={20} />
Revenue Growth Rate (MoM)
</CardTitle>
<CardValue>
{formatPercentage(
dashboardSnapshot.revenueGrowthRateCMGR ?? 0,
true
)}
</CardValue>
</Card>
<Card>
<CardTitle>
<FireIcon size={20} />
Burn Efficiency
</CardTitle>
<CardValue>
<DynamicColoredValue
value={dashboardSnapshot.burnEfficiency ?? 0}
colorsMap={[
{ color: theme.colors.error, max: 1 },
{ color: theme.colors.brand.white, min: 1.1, max: 6 },
{ color: theme.colors.brand.green, min: 6.1 },
]}
size={28}
weight={700}
>
{formatPercentage(
dashboardSnapshot.burnEfficiency ?? 0,
true
)}
</DynamicColoredValue>
</CardValue>
</Card>
<Card>
<CardTitle>
<ScalesIcon size={20} />
Debt Ratio
</CardTitle>
<CardValue>
<DynamicColoredValue
value={dashboardSnapshot.funding.debtRatio ?? 0}
colorsMap={[
{ color: theme.colors.brand.green, max: 0.3 }, // Healthy (low debt)
{ color: theme.colors.brand.white, min: 0.31, max: 0.6 }, // Moderate
{ color: theme.colors.error, min: 0.61 }, // Risky (high debt)
]}
size={28}
weight={700}
>
{formatPercentage(
dashboardSnapshot.funding.debtRatio ?? 0,
true
)}
</DynamicColoredValue>
</CardValue>
</Card>
<Card>
<CardTitle>
<CalendarXIcon size={20} />
Cash-Out Date
</CardTitle>
<CardValue>
{dashboardSnapshot.cashOutDate ?? "Not Available"}
</CardValue>
</Card>
</HeaderCards>
{/* ===== Main Grid / Charts ===== */}
<MainGrid>
<ChartPlaceholder>
<ChartRevenueTrend
data={revenueStream.toMonthlyRevenueTraces()}
/>
</ChartPlaceholder>
<ChartPlaceholder>
<ChartCashflowBar data={cashflow} />
</ChartPlaceholder>
<ChartPlaceholder>
<ChartExpenseTrend data={expense.getExpenseTrendByCategory()} />
</ChartPlaceholder>
<ChartPlaceholder>
<ChartExpensePie data={expense.expenseBreakdownPlot()} />
</ChartPlaceholder>
<ChartPlaceholder>
<ChartFundingTimeSeries
data={fundingManager.generateFundingTimeSeries()}
/>
</ChartPlaceholder>
<ChartPlaceholder>
<ChartGrossMarginTrend
data={revenueStream.toGrossMarginTrendTraces()}
/>
</ChartPlaceholder>
</MainGrid>
</>
),
},
{
id: "info-revenue",
name: "Revenue Streams",
icon: CurrencyDollarSimpleIcon,
content: (
<>
<RevenueStreamManager
revenueStream={revenueStream}
onUpdate={handleUpdateRevenueStream}
/>
</>
),
},
{
id: "info-expenses",
name: "Expenses",
icon: GaugeIcon,
content: (
<>
<ExpenseManager
expenseBreakdown={expense}
onUpdate={handleUpdateExpenseBreakdown}
/>
</>
),
},
{
id: "info-funding",
name: "Funding Events",
icon: BankIcon,
content: (
<>
{" "}
<FundingEventsManager
fundingManager={fundingManager}
onUpdate={handleUpdateFundingManager}
/>
</>
),
},
{
id: "info-tax",
name: "Tax Configuration",
icon: GearIcon,
content: (
<>
<RunwayTaxConfig
formData={taxConfig}
onSubmit={handleUpdateTaxConfig}
/>
</>
),
},
];
return (
<RootContainer>
{/* ===== Tabs ===== */}
<DynamicPagedTab tabs={InformationTabs} position="left" />
</RootContainer>
);
};
export default DashboardMajikRunway;
Utilities
validateSelf(throwError?: boolean) → validates all required fieldsfinalize() → converts to JSON with auto-generated IDtoJSON() → serialize with properMajikMoneyhandlingparseFromJSON(json: string | object) → reconstruct aMajikSubscriptioninstance
Use Cases
What is Majik Runway Used For?
MajikRunway is a financial modeling and runway simulation engine designed to help teams understand, forecast, and control cash flow, burn, growth, and sustainability over time. It is optimized for startups, creative businesses, SaaS products, agencies, and investor-facing financial planning.
Core Use Cases
1. Startup Runway & Burn Management
- Calculate cash runway in months based on current burn rate
- Track net burn vs gross burn
- Identify break-even points
- Simulate cost increases or revenue growth
- Evaluate survival scenarios under different funding conditions
***Real-world

