@thezelijah/majik-subscription 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # Majik Subscription
2
+
3
+ **Majik Subscription** is a fully-featured class representing a subscription-based offering in the **Majik system**, designed for recurring revenue modeling, cost tracking, and subscriber capacity planning. It provides utilities for computing **MRR, ARR, revenue, profit, margins, Cost of Subscription (COS), and net income** on a per-period basis. Chainable setter methods make it easy to construct and update subscriptions fluently.
4
+
5
+ ### Live Demo
6
+
7
+ [![Majik Runway Thumbnail](https://www.thezelijah.world/_next/static/media/WA_Tools_Finance_MajikRunway.c4d2034e.webp)](https://www.thezelijah.world/tools/finance-majik-runway)
8
+
9
+ > Click the image to try Majik Subscription inside Majik Runway's revenue stream.
10
+
11
+ [![Price Genie Thumbnail](https://www.thezelijah.world/_next/static/media/WA_Tools_Business_PriceGenie.dfab6d40.webp)](https://www.thezelijah.world/tools/business-price-genie)
12
+
13
+ > Click the image to try Majik Subscription inside Price Genie.
14
+
15
+ ---
16
+
17
+ ## Table of Contents
18
+
19
+ - [Overview](#-overview)
20
+ - [Installation](#-installation)
21
+ - [Usage](#usage)
22
+ - [Create a Subscription Instance](#create-a-subscription-instance)
23
+ - [Metadata Helpers](#metadata-helpers)
24
+ - [COS Management](#cos-management)
25
+ - [Capacity Management](#capacity-management)
26
+ - [Finance Computation](#finance-computation)
27
+ - [Utilities](#utilities)
28
+ - [Use Cases](#use-cases)
29
+ - [Best Practices](#best-practices)
30
+ - [Contributing](#contributing)
31
+ - [License](#license)
32
+ - [Author](#author)
33
+ - [Contact](#contact)
34
+
35
+ ---
36
+
37
+ ## ✨ Overview
38
+
39
+ MajikSubscription manages:
40
+
41
+ - **Metadata:** name, category, type, description, SKU, photos, rate.
42
+ - **Settings:** status, visibility, system restrictions.
43
+ - **Finance:** revenue, income, profit, COS (gross & net).
44
+ - **Capacity Plan:** Monthly subscriber or seat limits with adjustments.
45
+ - **Cost of Subscription:** Infrastructure, licensing, support, and scaling costs.
46
+ - **Recurring Billing:** Billing cycles, rate units, trials, and forecasting.
47
+ - **Serialization/Deserialization:** Convert to/from JSON with full monetary safety via [MajikMoney](https://www.npmjs.com/package/@thezelijah/majik-money).
48
+
49
+ ---
50
+
51
+ ## [Full API Docs](https://www.thezelijah.world/tools/finance-majik-subscription/docs)
52
+
53
+ ---
54
+
55
+ ## 📦 Installation
56
+
57
+ ```bash
58
+ npm i @thezelijah/majik-subscription @thezelijah/majik-money@latest
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Usage
64
+
65
+ ### Create a Subscription Instance
66
+
67
+ ```ts
68
+ import { MajikSubscription } from "@thezelijah/majik-subscription";
69
+ import { MajikMoney } from "@thezelijah/majik-money";
70
+ import {
71
+ SubscriptionType,
72
+ RateUnit,
73
+ BillingCycle,
74
+ } from "@thezelijah/majik-subscription/enums";
75
+
76
+ const subscription = MajikSubscription.initialize(
77
+ "Pro SaaS Plan",
78
+ SubscriptionType.RECURRING,
79
+ {
80
+ amount: MajikMoney.fromMajor(499, "PHP"),
81
+ unit: RateUnit.PER_USER,
82
+ billingCycle: BillingCycle.MONTHLY,
83
+ },
84
+ "Professional subscription tier",
85
+ "SAAS-PRO-001"
86
+ );
87
+ ```
88
+
89
+ Defaults:
90
+ status → ACTIVE
91
+ visibility → PRIVATE
92
+ Empty COS
93
+ Empty capacity plan
94
+ Zeroed finance snapshot
95
+
96
+ ### Example Usage
97
+
98
+ ```ts
99
+ import {
100
+ SubscriptionType,
101
+ RateUnit,
102
+ BillingCycle,
103
+ CapacityPeriodResizeMode,
104
+ } from "@thezelijah/majik-subscription/enums";
105
+
106
+ const proPlan = MajikSubscription.initialize(
107
+ "Pro Plan",
108
+ SubscriptionType.RECURRING,
109
+ {
110
+ amount: MajikMoney.fromMajor(29, "PHP"),
111
+ unit: RateUnit.PER_USER,
112
+ billingCycle: BillingCycle.MONTHLY,
113
+ },
114
+ "Advanced SaaS plan",
115
+ "PRO-PLAN-001"
116
+ )
117
+ .setDescriptionHTML("<p>Best plan for growing teams.</p>")
118
+ .setDescriptionSEO("Pro SaaS subscription plan")
119
+ .addCOS("Cloud Hosting", MajikMoney.fromMajor(300, "PHP"), 1, "per user")
120
+ .addCOS("Customer Support", MajikMoney.fromMajor(100, "PHP"), 1, "per user")
121
+ .generateCapacityPlan(12, 500) // 12 months, 500 subscribers
122
+ .recomputeCapacityPeriod(
123
+ "2025-01",
124
+ "2025-12",
125
+ CapacityPeriodResizeMode.DISTRIBUTE
126
+ );
127
+
128
+ // Capacity insights
129
+ console.log("Total Capacity:", proPlan.totalCapacity);
130
+
131
+ // Monthly finance
132
+ const month = "2025-06";
133
+ console.log(`${month} Revenue:`, proPlan.getRevenue(month).value.toFormat());
134
+ console.log(`${month} COS:`, proPlan.getCOS(month).value.toFormat());
135
+ console.log(`${month} Profit:`, proPlan.getProfit(month).value.toFormat());
136
+ console.log(`${month} Margin:`, proPlan.getMargin(month).toFixed(2) + "%");
137
+
138
+ // Serialization
139
+ const json = proPlan.toJSON();
140
+ const restored = MajikSubscription.parseFromJSON(json);
141
+
142
+ console.log("Restored Subscription:", restored.metadata.description.text);
143
+ ```
144
+
145
+ ### Metadata Helpers
146
+
147
+ Chainable methods to update Subscription metadata:
148
+
149
+ | Method | Description |
150
+ | ---------------------------------- | ---------------------------------- |
151
+ | `setName(name: string)` | Updates subscription name and slug |
152
+ | `setCategory(category: string)` | Updates subscription category |
153
+ | `setType(type: SubscriptionType)` | Updates subscription type |
154
+ | `setRate(rate: SubscriptionRate)` | Updates pricing & billing cycle |
155
+ | `setDescriptionText(text: string)` | Updates plain text description |
156
+ | `setDescriptionHTML(html: string)` | Updates HTML description |
157
+ | `setDescriptionSEO(text: string)` | Updates SEO text |
158
+ | `setPhotos(urls: string[])` | Sets subscription photo URLs |
159
+
160
+ ### COS Management
161
+
162
+ Manage the Cost of Subscription per item:
163
+
164
+ | Method | Description |
165
+ | ------------------------------------------ | -------------------------------- |
166
+ | `addCOS(name, unitCost, quantity?, unit?)` | Add a new COS item |
167
+ | `pushCOS(item: COSItem)` | Push externally created COS item |
168
+ | `updateCOS(id, updates)` | Update COS item |
169
+ | `removeCOS(id)` | Remove COS item |
170
+ | `setCOS(items: COSItem[])` | Replace COS list |
171
+ | `clearCOS()` | Remove all COS items |
172
+
173
+ ### Capacity Management
174
+
175
+ > Capacity adjustments are useful for modeling churn, promotions, temporary expansions, or trials.
176
+
177
+ Manage monthly capacity and Subscription plan:
178
+
179
+ | Method | Description |
180
+ | --------------------------------------------------------------- | -------------------------------- |
181
+ | `addCapacity(month: YYYYMM, capacity, adjustment?)` | Add monthly capacity |
182
+ | `updateCapacityUnits(month, units)` | Update capacity |
183
+ | `updateCapacityAdjustment(month, adjustment?)` | Adjust capacity |
184
+ | `removeCapacity(month)` | Remove month |
185
+ | `clearCapacity()` | Remove all capacity |
186
+ | `generateCapacityPlan(months, amount, growthRate?, startDate?)` | Auto-generate capacity plan |
187
+ | `normalizeCapacityUnits(amount)` | Normalize capacity across months |
188
+ | `recomputeCapacityPeriod(start, end, mode?)` | Resize / redistribute capacity |
189
+
190
+ Capacity plan queries:
191
+
192
+ - `totalCapacity` → total units across all months
193
+ - `averageMonthlyCapacity` → average per month
194
+ - `maxCapacityMonth` / `minCapacityMonth` → highest/lowest monthly capacity
195
+
196
+ ---
197
+
198
+ ### Finance Computation
199
+
200
+ > All finance computations are normalized to monthly periods internally, regardless of billing cycle.
201
+
202
+ | Method | Description |
203
+ | ------------------- | --------------------------------------------- |
204
+ | `getRevenue(month)` | Returns gross revenue for the specified month |
205
+ | `getProfit(month)` | Returns profit for the specified month |
206
+ | `getCOS(month)` | Returns total cost of Subscription for month |
207
+ | `getMargin(month)` | Returns margin ratio |
208
+
209
+ > Calculates revenue, costs, and profits per month or across all months.
210
+
211
+ - `MRR`, `ARR`
212
+ - `grossRevenue`, `grossCost`, `grossProfit` → totals across capacity plan
213
+ - `netRevenue`(month, discounts?, returns?, allowances?) → net per month
214
+ - `netProfit`(month, operatingExpenses?, taxes?, discounts?, returns?, allowances?) → net profit per month
215
+ - `getRevenue`(month), getCOS(month), getProfit(month), getMargin(month) → month-specific
216
+ - `averageMonthlyRevenue`, `averageMonthlyProfit` → averages
217
+
218
+ > All computations use **MajikMoney** and respect currency.
219
+
220
+ ---
221
+
222
+ ### Utilities
223
+
224
+ - `validateSelf`(throwError?: boolean) → validates all required fields
225
+ - `finalize`() → converts to JSON with auto-generated ID
226
+ - `toJSON`() → serialize with proper `MajikMoney` handling
227
+ - `parseFromJSON`(json: string | object) → reconstruct a `MajikSubscription` instance
228
+
229
+ ---
230
+
231
+ ## Use Cases
232
+
233
+ **MajikSubscription** is designed for recurring-revenue businesses:
234
+
235
+ 1. SaaS & Software Products
236
+
237
+ - MRR / ARR tracking
238
+ - Unit economics
239
+ - Subscriber forecasting
240
+
241
+ 2. Membership Platforms
242
+
243
+ - Tiered plans
244
+ - Seat-based pricing
245
+ - Capacity enforcement
246
+
247
+ 3. Financial Forecasting
248
+
249
+ - Revenue projections
250
+ - Cost scaling
251
+ - Margin analysis
252
+
253
+ 4. Data Integration
254
+
255
+ - API serialization
256
+ - Persistent finance models
257
+ - Dashboard analytics
258
+
259
+ ---
260
+
261
+ ## Best Practices
262
+
263
+ To maximize reliability, maintainability, and performance:
264
+
265
+ 1. Use Chainable Setters
266
+
267
+ - Always modify subscriptions via setter methods (`setRate`, `addCOS`, `setCapacity`) to ensure timestamps and finance recalculations are handled automatically.
268
+
269
+ 2. Validate Before Finalization
270
+
271
+ - Call `validateSelf`(true) before exporting or persisting the subscription to ensure all required fields are properly set.
272
+
273
+ 3. Maintain Currency Consistency
274
+
275
+ - All monetary operations use MajikMoney. Avoid mixing currencies; setter methods validate against subscription Rate currency.
276
+
277
+ 4. Leverage Supply Plan Utilities
278
+
279
+ - Use `generateCapacityPlan`, `normalizeCapacityUnits`, or `recomputeCapacityPeriod` to programmatically manage monthly supply rather than manually modifying arrays.
280
+
281
+ 5. Keep COS Accurate
282
+
283
+ - Always ensure unitCost and subtotal calculations are correct. Prefer addCOS or pushCOS instead of direct array mutation.
284
+
285
+ 6. Minimize Finance Recomputations for Bulk Updates
286
+
287
+ - When performing bulk updates to COS or supply, consider batching changes and calling recomputeFinance once at the end to avoid repeated expensive calculations.
288
+
289
+ 7. Use Snapshots for Reporting
290
+
291
+ - Use `getMonthlySnapshot`(month) for consistent monthly financial reporting and dashboards.
292
+
293
+ 8. Error Handling
294
+
295
+ - All setters throw on invalid input. Wrap critical updates in try/catch to handle edge cases gracefully.
296
+
297
+ 9. Serialization & Deserialization
298
+
299
+ - Use `toJSON` / finalize for exporting, and parseFromJSON for reconstruction. Avoid manually modifying the serialized object to prevent integrity issues.
300
+
301
+ ---
302
+
303
+ ## Conclusion
304
+
305
+ **MajikSubscription** provides a robust, financial-first approach to modeling subscriptions and recurring revenue, suitable for SaaS, memberships, and enterprise-grade forecasting systems.
306
+
307
+ ## Contributing
308
+
309
+ Contributions, bug reports, and suggestions are welcome! Feel free to fork and open a pull request.
310
+
311
+ ---
312
+
313
+ ## License
314
+
315
+ [ISC](LICENSE) — free for personal and commercial use.
316
+
317
+ ---
318
+
319
+ ## Author
320
+
321
+ Made with 💙 by [@thezelijah](https://github.com/jedlsf)
322
+
323
+ ## About the Developer
324
+
325
+ - **Developer**: Josef Elijah Fabian
326
+ - **GitHub**: [https://github.com/jedlsf](https://github.com/jedlsf)
327
+ - **Project Repository**: [https://github.com/jedlsf/majik-subscription](https://github.com/jedlsf/majik-subscription)
328
+
329
+ ---
330
+
331
+ ## Contact
332
+
333
+ - **Business Email**: [business@thezelijah.world](mailto:business@thezelijah.world)
334
+ - **Official Website**: [https://www.thezelijah.world](https://www.thezelijah.world)
335
+
336
+ ---
@@ -0,0 +1,32 @@
1
+ export declare enum SubscriptionStatus {
2
+ ACTIVE = "Active",
3
+ INACTIVE = "Inactive",
4
+ SUSPENDED = "Suspended",
5
+ CANCELLED = "Cancelled"
6
+ }
7
+ export declare enum SubscriptionType {
8
+ RECURRING = "Recurring",
9
+ ONE_TIME = "One-Time",
10
+ TRIAL = "Trial"
11
+ }
12
+ export declare enum SubscriptionVisibility {
13
+ PUBLIC = "Public",
14
+ PRIVATE = "Private"
15
+ }
16
+ export declare enum BillingCycle {
17
+ DAILY = "Daily",
18
+ WEEKLY = "Weekly",
19
+ MONTHLY = "Monthly",
20
+ QUARTERLY = "Quarterly",
21
+ YEARLY = "Yearly"
22
+ }
23
+ export declare enum RateUnit {
24
+ PER_SUBSCRIBER = "Per Subscriber",
25
+ PER_ACCOUNT = "Per Account",
26
+ PER_USER = "Per User",
27
+ PER_MONTH = "Per Month"
28
+ }
29
+ export declare enum CapacityPeriodResizeMode {
30
+ DEFAULT = "default",// trim or pad, keep per-month units
31
+ DISTRIBUTE = "distribute"
32
+ }
package/dist/enums.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CapacityPeriodResizeMode = exports.RateUnit = exports.BillingCycle = exports.SubscriptionVisibility = exports.SubscriptionType = exports.SubscriptionStatus = void 0;
4
+ var SubscriptionStatus;
5
+ (function (SubscriptionStatus) {
6
+ SubscriptionStatus["ACTIVE"] = "Active";
7
+ SubscriptionStatus["INACTIVE"] = "Inactive";
8
+ SubscriptionStatus["SUSPENDED"] = "Suspended";
9
+ SubscriptionStatus["CANCELLED"] = "Cancelled";
10
+ })(SubscriptionStatus || (exports.SubscriptionStatus = SubscriptionStatus = {}));
11
+ var SubscriptionType;
12
+ (function (SubscriptionType) {
13
+ SubscriptionType["RECURRING"] = "Recurring";
14
+ SubscriptionType["ONE_TIME"] = "One-Time";
15
+ SubscriptionType["TRIAL"] = "Trial";
16
+ })(SubscriptionType || (exports.SubscriptionType = SubscriptionType = {}));
17
+ var SubscriptionVisibility;
18
+ (function (SubscriptionVisibility) {
19
+ SubscriptionVisibility["PUBLIC"] = "Public";
20
+ SubscriptionVisibility["PRIVATE"] = "Private";
21
+ })(SubscriptionVisibility || (exports.SubscriptionVisibility = SubscriptionVisibility = {}));
22
+ var BillingCycle;
23
+ (function (BillingCycle) {
24
+ BillingCycle["DAILY"] = "Daily";
25
+ BillingCycle["WEEKLY"] = "Weekly";
26
+ BillingCycle["MONTHLY"] = "Monthly";
27
+ BillingCycle["QUARTERLY"] = "Quarterly";
28
+ BillingCycle["YEARLY"] = "Yearly";
29
+ })(BillingCycle || (exports.BillingCycle = BillingCycle = {}));
30
+ var RateUnit;
31
+ (function (RateUnit) {
32
+ RateUnit["PER_SUBSCRIBER"] = "Per Subscriber";
33
+ RateUnit["PER_ACCOUNT"] = "Per Account";
34
+ RateUnit["PER_USER"] = "Per User";
35
+ RateUnit["PER_MONTH"] = "Per Month";
36
+ })(RateUnit || (exports.RateUnit = RateUnit = {}));
37
+ var CapacityPeriodResizeMode;
38
+ (function (CapacityPeriodResizeMode) {
39
+ CapacityPeriodResizeMode["DEFAULT"] = "default";
40
+ CapacityPeriodResizeMode["DISTRIBUTE"] = "distribute";
41
+ })(CapacityPeriodResizeMode || (exports.CapacityPeriodResizeMode = CapacityPeriodResizeMode = {}));
@@ -0,0 +1,4 @@
1
+ export * from "./majik-subscription";
2
+ export * from "./utils";
3
+ export * from "./enums";
4
+ export * from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./majik-subscription"), exports);
18
+ __exportStar(require("./utils"), exports);
19
+ __exportStar(require("./enums"), exports);
20
+ __exportStar(require("./types"), exports);