@thezelijah/majik-subscription 1.0.0 → 1.0.2

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/dist/enums.d.ts CHANGED
@@ -1,32 +1,38 @@
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
- }
1
+ export declare const SubscriptionStatus: {
2
+ readonly ACTIVE: "Active";
3
+ readonly INACTIVE: "Inactive";
4
+ readonly SUSPENDED: "Suspended";
5
+ readonly CANCELLED: "Cancelled";
6
+ };
7
+ export type SubscriptionStatus = (typeof SubscriptionStatus)[keyof typeof SubscriptionStatus];
8
+ export declare const SubscriptionType: {
9
+ readonly RECURRING: "Recurring";
10
+ readonly ONE_TIME: "One-Time";
11
+ readonly TRIAL: "Trial";
12
+ };
13
+ export type SubscriptionType = (typeof SubscriptionType)[keyof typeof SubscriptionType];
14
+ export declare const SubscriptionVisibility: {
15
+ readonly PUBLIC: "Public";
16
+ readonly PRIVATE: "Private";
17
+ };
18
+ export type SubscriptionVisibility = (typeof SubscriptionVisibility)[keyof typeof SubscriptionVisibility];
19
+ export declare const BillingCycle: {
20
+ readonly DAILY: "Daily";
21
+ readonly WEEKLY: "Weekly";
22
+ readonly MONTHLY: "Monthly";
23
+ readonly QUARTERLY: "Quarterly";
24
+ readonly YEARLY: "Yearly";
25
+ };
26
+ export type BillingCycle = (typeof BillingCycle)[keyof typeof BillingCycle];
27
+ export declare const RateUnit: {
28
+ readonly PER_SUBSCRIBER: "Per Subscriber";
29
+ readonly PER_ACCOUNT: "Per Account";
30
+ readonly PER_USER: "Per User";
31
+ readonly PER_MONTH: "Per Month";
32
+ };
33
+ export type RateUnit = (typeof RateUnit)[keyof typeof RateUnit];
34
+ export declare const CapacityPeriodResizeMode: {
35
+ readonly DEFAULT: "default";
36
+ readonly DISTRIBUTE: "distribute";
37
+ };
38
+ export type CapacityPeriodResizeMode = (typeof CapacityPeriodResizeMode)[keyof typeof CapacityPeriodResizeMode];
package/dist/enums.js CHANGED
@@ -1,41 +1,32 @@
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 = {}));
1
+ export const SubscriptionStatus = {
2
+ ACTIVE: "Active",
3
+ INACTIVE: "Inactive",
4
+ SUSPENDED: "Suspended",
5
+ CANCELLED: "Cancelled",
6
+ };
7
+ export const SubscriptionType = {
8
+ RECURRING: "Recurring",
9
+ ONE_TIME: "One-Time",
10
+ TRIAL: "Trial",
11
+ };
12
+ export const SubscriptionVisibility = {
13
+ PUBLIC: "Public",
14
+ PRIVATE: "Private",
15
+ };
16
+ export const BillingCycle = {
17
+ DAILY: "Daily",
18
+ WEEKLY: "Weekly",
19
+ MONTHLY: "Monthly",
20
+ QUARTERLY: "Quarterly",
21
+ YEARLY: "Yearly",
22
+ };
23
+ export const RateUnit = {
24
+ PER_SUBSCRIBER: "Per Subscriber",
25
+ PER_ACCOUNT: "Per Account",
26
+ PER_USER: "Per User",
27
+ PER_MONTH: "Per Month",
28
+ };
29
+ export const CapacityPeriodResizeMode = {
30
+ DEFAULT: "default",
31
+ DISTRIBUTE: "distribute",
32
+ };
package/dist/index.js CHANGED
@@ -1,20 +1,4 @@
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);
1
+ export * from "./majik-subscription";
2
+ export * from "./utils";
3
+ export * from "./enums";
4
+ export * from "./types";
@@ -1,6 +1,6 @@
1
1
  import { MajikMoney } from "@thezelijah/majik-money";
2
- import { COSItem, ISODateString, MonthlyCapacity, ObjectType, StartDateInput, SubscriptionID, SubscriptionMetadata, SubscriptionRate, SubscriptionSettings, YYYYMM } from "./types";
3
- import { CapacityPeriodResizeMode, RateUnit, SubscriptionStatus, SubscriptionType } from "./enums";
2
+ import { COSItem, ISODateString, MajikSubscriptionJSON, MonthlyCapacity, ObjectType, StartDateInput, SubscriptionID, SubscriptionMetadata, SubscriptionRate, SubscriptionSettings, YYYYMM } from "./types";
3
+ import { BillingCycle, CapacityPeriodResizeMode, RateUnit, SubscriptionStatus, SubscriptionType } from "./enums";
4
4
  /**
5
5
  * Represents a subscription in the Majik system.
6
6
  * Handles metadata, capacity, COS, and finance calculations (revenue, COS, profit, margins) for recurring subscriptions.
@@ -70,6 +70,12 @@ export declare class MajikSubscription {
70
70
  * @throws Will throw an error if amount is non-positive.
71
71
  */
72
72
  setRateAmount(amount: number): this;
73
+ /**
74
+ * Updates the billing cycle.
75
+ * @param {BillingCycle} cycle - New billing cycle (e.g., monthly, quarterly). use Enum `BillingCycle`.
76
+ * @returns {MajikSubscription} - Returns self for chaining.
77
+ */
78
+ setBillingCycle(cycle: BillingCycle): this;
73
79
  /**
74
80
  * Updates the subscription category.
75
81
  * @param {string} category - New category name.
@@ -337,6 +343,7 @@ export declare class MajikSubscription {
337
343
  * @returns {readonly COSItem[]} - Array of COS items.
338
344
  */
339
345
  get cos(): readonly COSItem[];
346
+ get costBreakdown(): readonly COSItem[];
340
347
  /**
341
348
  * Returns COS for a specific month.
342
349
  * @param {YYYYMM} month - Month in YYYY-MM format.
@@ -403,16 +410,16 @@ export declare class MajikSubscription {
403
410
  finalize(): object;
404
411
  /**
405
412
  * Converts the subscription instance to a plain JSON object.
406
- * @returns {object} - Plain object representation.
413
+ * @returns {MajikSubscriptionJSON} - Plain object representation.
407
414
  */
408
- toJSON(): object;
415
+ toJSON(): MajikSubscriptionJSON;
409
416
  /**
410
417
  * Parses a plain object or JSON string into a MajikSubscription instance.
411
- * @param {string | object} json - JSON string or object.
418
+ * @param {string | MajikSubscriptionJSON} json - JSON string or object.
412
419
  * @returns {MajikSubscription} - Parsed subscription instance.
413
420
  * @throws {Error} - Throws if required properties are missing.
414
421
  */
415
- static parseFromJSON(json: string | object): MajikSubscription;
422
+ static parseFromJSON(json: string | MajikSubscriptionJSON): MajikSubscription;
416
423
  /**
417
424
  * Updates the last_update timestamp to current time.
418
425
  * Should be called whenever a property is modified.
@@ -427,5 +434,5 @@ export declare class MajikSubscription {
427
434
  */
428
435
  private assertCurrency;
429
436
  }
430
- export declare function isMajikSubscriptionClass(item: MajikSubscription): boolean;
431
- export declare function isMajikSubscriptionJSON(item: MajikSubscription): boolean;
437
+ export declare function isMajikSubscriptionClass(item: MajikSubscription | MajikSubscriptionJSON): boolean;
438
+ export declare function isMajikSubscriptionJSON(item: MajikSubscription | MajikSubscriptionJSON): boolean;
@@ -1,16 +1,25 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MajikSubscription = void 0;
4
- exports.isMajikSubscriptionClass = isMajikSubscriptionClass;
5
- exports.isMajikSubscriptionJSON = isMajikSubscriptionJSON;
6
- const majik_money_1 = require("@thezelijah/majik-money");
7
- const utils_1 = require("./utils");
8
- const enums_1 = require("./enums");
1
+ import { deserializeMoney, MajikMoney, serializeMoney, } from "@thezelijah/majik-money";
2
+ import { autogenerateID, createEmptySubscriptionFinance, monthsInPeriod, generateSlug, isValidYYYYMM, normalizeStartDate, offsetMonthsToYYYYMM, dateToYYYYMM, } from "./utils";
3
+ import { CapacityPeriodResizeMode, SubscriptionStatus, SubscriptionType, SubscriptionVisibility, } from "./enums";
9
4
  /**
10
5
  * Represents a subscription in the Majik system.
11
6
  * Handles metadata, capacity, COS, and finance calculations (revenue, COS, profit, margins) for recurring subscriptions.
12
7
  */
13
- class MajikSubscription {
8
+ export class MajikSubscription {
9
+ __type = "MajikSubscription";
10
+ __object = "class";
11
+ id;
12
+ slug;
13
+ name;
14
+ category;
15
+ rate;
16
+ status;
17
+ type;
18
+ timestamp;
19
+ last_update;
20
+ metadata;
21
+ settings;
22
+ financeDirty = true;
14
23
  /**
15
24
  * Creates a new `MajikSubscription` instance.
16
25
  * @param {SubscriptionID | undefined} id - Optional subscription ID. Auto-generated if undefined.
@@ -22,16 +31,13 @@ class MajikSubscription {
22
31
  * @param {ISODateString} [last_update=new Date().toISOString()] - Optional last update timestamp.
23
32
  */
24
33
  constructor(id, slug, name, metadata, settings, timestamp = new Date().toISOString(), last_update = new Date().toISOString()) {
25
- this.__type = "MajikSubscription";
26
- this.__object = "class";
27
- this.financeDirty = true;
28
- this.id = id || (0, utils_1.autogenerateID)("mjksub");
29
- this.slug = slug || (0, utils_1.generateSlug)(name);
34
+ this.id = id || autogenerateID("mjksub");
35
+ this.slug = slug || generateSlug(name);
30
36
  this.name = name;
31
37
  this.metadata = metadata;
32
38
  this.settings = settings ?? {
33
- status: enums_1.SubscriptionStatus.ACTIVE,
34
- visibility: enums_1.SubscriptionVisibility.PRIVATE,
39
+ status: SubscriptionStatus.ACTIVE,
40
+ visibility: SubscriptionVisibility.PRIVATE,
35
41
  system: { isRestricted: false },
36
42
  };
37
43
  this.type = this.metadata.type;
@@ -52,14 +58,14 @@ class MajikSubscription {
52
58
  */
53
59
  DEFAULT_ZERO(currencyCode) {
54
60
  const code = currencyCode || this.rate?.amount?.currency?.code || "PHP";
55
- return majik_money_1.MajikMoney.fromMinor(0, code);
61
+ return MajikMoney.fromMinor(0, code);
56
62
  }
57
63
  /**
58
64
  * Initializes and creates a new `MajikSubscription` with default and null values.
59
65
  * @param type - The type of service to initialize. Defaults to `TIME_BASED`. Use Enum `ServiceType`.
60
66
  * @returns A new `MajikSubscription` instance.
61
67
  */
62
- static initialize(name, type = enums_1.SubscriptionType.RECURRING, rate, category = "Other", descriptionText, skuID) {
68
+ static initialize(name, type = SubscriptionType.RECURRING, rate, category = "Other", descriptionText, skuID) {
63
69
  if (!name || typeof name !== "string" || name.trim() === "") {
64
70
  throw new Error("Name must be a valid non-empty string.");
65
71
  }
@@ -76,11 +82,11 @@ class MajikSubscription {
76
82
  rate: rate,
77
83
  sku: skuID || undefined,
78
84
  cos: [],
79
- finance: (0, utils_1.createEmptySubscriptionFinance)(rate.amount.currency.code),
85
+ finance: createEmptySubscriptionFinance(rate.amount.currency.code),
80
86
  };
81
87
  const defaultSettings = {
82
- visibility: enums_1.SubscriptionVisibility.PRIVATE,
83
- status: enums_1.SubscriptionStatus.ACTIVE,
88
+ visibility: SubscriptionVisibility.PRIVATE,
89
+ status: SubscriptionStatus.ACTIVE,
84
90
  system: {
85
91
  isRestricted: false,
86
92
  },
@@ -95,7 +101,7 @@ class MajikSubscription {
95
101
  */
96
102
  setName(name) {
97
103
  this.name = name;
98
- this.slug = (0, utils_1.generateSlug)(name);
104
+ this.slug = generateSlug(name);
99
105
  this.updateTimestamp();
100
106
  return this;
101
107
  }
@@ -132,8 +138,20 @@ class MajikSubscription {
132
138
  setRateAmount(amount) {
133
139
  if (amount <= 0)
134
140
  throw new Error("Rate Amount must be positive");
135
- this.rate.amount = majik_money_1.MajikMoney.fromMajor(amount, this.rate.amount.currency.code);
136
- this.metadata.rate.amount = majik_money_1.MajikMoney.fromMajor(amount, this.metadata.rate.amount.currency.code);
141
+ this.rate.amount = MajikMoney.fromMajor(amount, this.rate.amount.currency.code);
142
+ this.metadata.rate.amount = MajikMoney.fromMajor(amount, this.metadata.rate.amount.currency.code);
143
+ this.updateTimestamp();
144
+ this.markFinanceDirty();
145
+ return this;
146
+ }
147
+ /**
148
+ * Updates the billing cycle.
149
+ * @param {BillingCycle} cycle - New billing cycle (e.g., monthly, quarterly). use Enum `BillingCycle`.
150
+ * @returns {MajikSubscription} - Returns self for chaining.
151
+ */
152
+ setBillingCycle(cycle) {
153
+ this.rate.billingCycle = cycle;
154
+ this.metadata.rate.billingCycle = cycle;
137
155
  this.updateTimestamp();
138
156
  this.markFinanceDirty();
139
157
  return this;
@@ -213,7 +231,7 @@ class MajikSubscription {
213
231
  * @throws Will throw an error if the `type` is not provided or is not a string.
214
232
  */
215
233
  setType(type) {
216
- if (!Object.values(enums_1.SubscriptionType).includes(type)) {
234
+ if (!Object.values(SubscriptionType).includes(type)) {
217
235
  throw new Error("Invalid Subscription type.");
218
236
  }
219
237
  this.metadata.type = type;
@@ -252,7 +270,7 @@ class MajikSubscription {
252
270
  throw new Error("COS quantity must be greater than zero");
253
271
  this.assertCurrency(unitCost);
254
272
  const newItem = {
255
- id: (0, utils_1.autogenerateID)("mjksubcost"),
273
+ id: autogenerateID("mjksubcost"),
256
274
  item: name,
257
275
  quantity,
258
276
  unitCost,
@@ -450,7 +468,7 @@ class MajikSubscription {
450
468
  if (growthRate < 0) {
451
469
  throw new Error("Growth rate cannot be negative");
452
470
  }
453
- const start = (0, utils_1.normalizeStartDate)(startDate);
471
+ const start = normalizeStartDate(startDate);
454
472
  const supplyPlan = [];
455
473
  let currentUnits = amount;
456
474
  for (let i = 0; i < months; i++) {
@@ -496,8 +514,8 @@ class MajikSubscription {
496
514
  this.markFinanceDirty();
497
515
  return this;
498
516
  }
499
- recomputeCapacityPeriod(start, end, mode = enums_1.CapacityPeriodResizeMode.DEFAULT) {
500
- if (!(0, utils_1.isValidYYYYMM)(start) || !(0, utils_1.isValidYYYYMM)(end)) {
517
+ recomputeCapacityPeriod(start, end, mode = CapacityPeriodResizeMode.DEFAULT) {
518
+ if (!isValidYYYYMM(start) || !isValidYYYYMM(end)) {
501
519
  throw new Error("Invalid YYYYMM period");
502
520
  }
503
521
  if (!this.hasCapacity()) {
@@ -506,21 +524,21 @@ class MajikSubscription {
506
524
  if (start > end) {
507
525
  throw new Error("Start month must be <= end month");
508
526
  }
509
- const newLength = (0, utils_1.monthsInPeriod)(start, end);
527
+ const newLength = monthsInPeriod(start, end);
510
528
  const oldPlan = [...this.metadata.capacityPlan];
511
529
  const oldLength = oldPlan.length;
512
530
  const newPlan = [];
513
- if (mode === enums_1.CapacityPeriodResizeMode.DEFAULT) {
531
+ if (mode === CapacityPeriodResizeMode.DEFAULT) {
514
532
  for (let i = 0; i < newLength; i++) {
515
533
  const source = i < oldLength ? oldPlan[i] : oldPlan[oldLength - 1]; // extend using last known value
516
534
  newPlan.push({
517
- month: (0, utils_1.offsetMonthsToYYYYMM)(start, i),
535
+ month: offsetMonthsToYYYYMM(start, i),
518
536
  capacity: source.capacity,
519
537
  adjustment: source.adjustment,
520
538
  });
521
539
  }
522
540
  }
523
- if (mode === enums_1.CapacityPeriodResizeMode.DISTRIBUTE) {
541
+ if (mode === CapacityPeriodResizeMode.DISTRIBUTE) {
524
542
  const total = this.totalCapacity;
525
543
  const base = Math.floor(total / newLength);
526
544
  let remainder = total % newLength;
@@ -528,7 +546,7 @@ class MajikSubscription {
528
546
  const extra = remainder > 0 ? 1 : 0;
529
547
  remainder--;
530
548
  newPlan.push({
531
- month: (0, utils_1.offsetMonthsToYYYYMM)(start, i),
549
+ month: offsetMonthsToYYYYMM(start, i),
532
550
  capacity: base + extra,
533
551
  });
534
552
  }
@@ -546,7 +564,7 @@ class MajikSubscription {
546
564
  */
547
565
  setCapacity(capacityPlan) {
548
566
  capacityPlan.forEach((s) => {
549
- if (!(0, utils_1.isValidYYYYMM)(s.month))
567
+ if (!isValidYYYYMM(s.month))
550
568
  throw new Error(`Invalid month: ${s.month}`);
551
569
  if (typeof s.capacity !== "number")
552
570
  throw new Error("Capacity must be a number");
@@ -565,10 +583,9 @@ class MajikSubscription {
565
583
  * @throws Will throw an error if month already exists.
566
584
  */
567
585
  addCapacity(month, units, adjustment) {
568
- var _a;
569
- if (!(0, utils_1.isValidYYYYMM)(month))
586
+ if (!isValidYYYYMM(month))
570
587
  throw new Error("Invalid month");
571
- (_a = this.metadata).capacityPlan ?? (_a.capacityPlan = []);
588
+ this.metadata.capacityPlan ??= [];
572
589
  if (this.metadata.capacityPlan.some((s) => s.month === month)) {
573
590
  throw new Error(`Month ${month} already exists. Use updateCapacityUnits or updateCapacityAdjustment`);
574
591
  }
@@ -585,7 +602,7 @@ class MajikSubscription {
585
602
  * @throws Will throw an error if month does not exist.
586
603
  */
587
604
  updateCapacityUnits(month, units) {
588
- if (!(0, utils_1.isValidYYYYMM)(month))
605
+ if (!isValidYYYYMM(month))
589
606
  throw new Error("Invalid month");
590
607
  const plan = this.metadata.capacityPlan?.find((s) => s.month === month);
591
608
  if (!plan)
@@ -603,7 +620,7 @@ class MajikSubscription {
603
620
  * @throws Will throw an error if month does not exist.
604
621
  */
605
622
  updateCapacityAdjustment(month, adjustment) {
606
- if (!(0, utils_1.isValidYYYYMM)(month))
623
+ if (!isValidYYYYMM(month))
607
624
  throw new Error("Invalid month");
608
625
  const plan = this.metadata.capacityPlan?.find((s) => s.month === month);
609
626
  if (!plan)
@@ -620,7 +637,7 @@ class MajikSubscription {
620
637
  * @throws Will throw an error if month does not exist.
621
638
  */
622
639
  removeCapacity(month) {
623
- if (!(0, utils_1.isValidYYYYMM)(month))
640
+ if (!isValidYYYYMM(month))
624
641
  throw new Error("Invalid month");
625
642
  const index = this.metadata.capacityPlan?.findIndex((s) => s.month === month);
626
643
  if (index === undefined || index === -1)
@@ -772,7 +789,7 @@ class MajikSubscription {
772
789
  * @returns {MajikMoney} Net Revenue
773
790
  */
774
791
  getNetRevenue(month, discounts, returns, allowances) {
775
- if (!(0, utils_1.isValidYYYYMM)(month))
792
+ if (!isValidYYYYMM(month))
776
793
  throw new Error("Invalid month");
777
794
  let net = this.getRevenue(month);
778
795
  if (discounts)
@@ -794,7 +811,7 @@ class MajikSubscription {
794
811
  * @returns {MajikMoney} Net Profit
795
812
  */
796
813
  getNetProfit(month, operatingExpenses, taxes, discounts, returns, allowances) {
797
- if (!(0, utils_1.isValidYYYYMM)(month))
814
+ if (!isValidYYYYMM(month))
798
815
  throw new Error("Invalid month");
799
816
  let netRev = this.getNetRevenue(month, discounts, returns, allowances);
800
817
  if (operatingExpenses)
@@ -807,7 +824,7 @@ class MajikSubscription {
807
824
  * Alias for getNetProfit, same as Net Income
808
825
  */
809
826
  getNetIncome(month, operatingExpenses, taxes, discounts, returns, allowances) {
810
- if (!(0, utils_1.isValidYYYYMM)(month))
827
+ if (!isValidYYYYMM(month))
811
828
  throw new Error("Invalid month");
812
829
  return this.getNetProfit(month, operatingExpenses, taxes, discounts, returns, allowances);
813
830
  }
@@ -818,7 +835,7 @@ class MajikSubscription {
818
835
  * @returns {MajikMoney} - Monthly revenue.
819
836
  */
820
837
  getRevenue(month) {
821
- if (!(0, utils_1.isValidYYYYMM)(month))
838
+ if (!isValidYYYYMM(month))
822
839
  throw new Error("Invalid month");
823
840
  const plan = this.metadata.capacityPlan?.find((s) => s.month === month);
824
841
  if (!plan)
@@ -832,13 +849,16 @@ class MajikSubscription {
832
849
  get cos() {
833
850
  return this.metadata.cos;
834
851
  }
852
+ get costBreakdown() {
853
+ return this.cos;
854
+ }
835
855
  /**
836
856
  * Returns COS for a specific month.
837
857
  * @param {YYYYMM} month - Month in YYYY-MM format.
838
858
  * @returns {MajikMoney} - Monthly COS.
839
859
  */
840
860
  getCOS(month) {
841
- if (!(0, utils_1.isValidYYYYMM)(month))
861
+ if (!isValidYYYYMM(month))
842
862
  throw new Error("Invalid month");
843
863
  const plan = this.metadata.capacityPlan?.find((s) => s.month === month);
844
864
  if (!plan)
@@ -860,7 +880,7 @@ class MajikSubscription {
860
880
  * @returns {MajikMoney} - Monthly profit.
861
881
  */
862
882
  getProfit(month) {
863
- if (!(0, utils_1.isValidYYYYMM)(month))
883
+ if (!isValidYYYYMM(month))
864
884
  throw new Error("Invalid month");
865
885
  return this.getRevenue(month).subtract(this.getCOS(month));
866
886
  }
@@ -870,7 +890,7 @@ class MajikSubscription {
870
890
  * @returns {number} - Profit margin (0–1).
871
891
  */
872
892
  getMargin(month) {
873
- if (!(0, utils_1.isValidYYYYMM)(month))
893
+ if (!isValidYYYYMM(month))
874
894
  throw new Error("Invalid month");
875
895
  const revenue = this.getRevenue(month);
876
896
  return revenue.isZero()
@@ -948,7 +968,7 @@ class MajikSubscription {
948
968
  /** Monthly Recurring Revenue (MRR) for a specific month or current month if not provided */
949
969
  getMRR(month) {
950
970
  if (!month) {
951
- month = (0, utils_1.dateToYYYYMM)(new Date());
971
+ month = dateToYYYYMM(new Date());
952
972
  }
953
973
  return this.getRevenue(month);
954
974
  }
@@ -990,11 +1010,11 @@ class MajikSubscription {
990
1010
  * @returns {object} - Serialized subscription.
991
1011
  */
992
1012
  finalize() {
993
- return { ...this.toJSON(), id: (0, utils_1.autogenerateID)("mjksub") };
1013
+ return { ...this.toJSON(), id: autogenerateID("mjksub") };
994
1014
  }
995
1015
  /**
996
1016
  * Converts the subscription instance to a plain JSON object.
997
- * @returns {object} - Plain object representation.
1017
+ * @returns {MajikSubscriptionJSON} - Plain object representation.
998
1018
  */
999
1019
  toJSON() {
1000
1020
  const preJSON = {
@@ -1012,17 +1032,17 @@ class MajikSubscription {
1012
1032
  metadata: this.metadata,
1013
1033
  settings: this.settings,
1014
1034
  };
1015
- return (0, majik_money_1.serializeMoney)(preJSON);
1035
+ return serializeMoney(preJSON);
1016
1036
  }
1017
1037
  /**
1018
1038
  * Parses a plain object or JSON string into a MajikSubscription instance.
1019
- * @param {string | object} json - JSON string or object.
1039
+ * @param {string | MajikSubscriptionJSON} json - JSON string or object.
1020
1040
  * @returns {MajikSubscription} - Parsed subscription instance.
1021
1041
  * @throws {Error} - Throws if required properties are missing.
1022
1042
  */
1023
1043
  static parseFromJSON(json) {
1024
1044
  const rawParse = typeof json === "string" ? JSON.parse(json) : structuredClone(json);
1025
- const parsedData = (0, majik_money_1.deserializeMoney)(rawParse);
1045
+ const parsedData = deserializeMoney(rawParse);
1026
1046
  if (!parsedData.id)
1027
1047
  throw new Error("Missing required property: 'id'");
1028
1048
  if (!parsedData.timestamp)
@@ -1053,10 +1073,9 @@ class MajikSubscription {
1053
1073
  }
1054
1074
  }
1055
1075
  }
1056
- exports.MajikSubscription = MajikSubscription;
1057
- function isMajikSubscriptionClass(item) {
1076
+ export function isMajikSubscriptionClass(item) {
1058
1077
  return item.__object === "class";
1059
1078
  }
1060
- function isMajikSubscriptionJSON(item) {
1079
+ export function isMajikSubscriptionJSON(item) {
1061
1080
  return item.__object === "json";
1062
1081
  }
package/dist/types.d.ts CHANGED
@@ -93,3 +93,18 @@ export interface SubscriptionSettings {
93
93
  restrictedUntil?: ISODateString;
94
94
  };
95
95
  }
96
+ export interface MajikSubscriptionJSON {
97
+ __type: "MajikSubscription";
98
+ __object: "json";
99
+ id: SubscriptionID;
100
+ slug: string;
101
+ name: string;
102
+ category: string;
103
+ rate: SubscriptionRate;
104
+ status: SubscriptionStatus;
105
+ type: SubscriptionType;
106
+ timestamp: ISODateString;
107
+ last_update: ISODateString;
108
+ metadata: SubscriptionMetadata;
109
+ settings: SubscriptionSettings;
110
+ }
package/dist/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
package/dist/utils.js CHANGED
@@ -1,19 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateSlug = generateSlug;
4
- exports.autogenerateID = autogenerateID;
5
- exports.isValidYYYYMM = isValidYYYYMM;
6
- exports.createZeroValueRatio = createZeroValueRatio;
7
- exports.createEmptySubscriptionFinance = createEmptySubscriptionFinance;
8
- exports.isoToYYYYMM = isoToYYYYMM;
9
- exports.yyyyMMToISO = yyyyMMToISO;
10
- exports.dateToYYYYMM = dateToYYYYMM;
11
- exports.yyyyMMToDate = yyyyMMToDate;
12
- exports.offsetMonthsToYYYYMM = offsetMonthsToYYYYMM;
13
- exports.monthsInPeriod = monthsInPeriod;
14
- exports.normalizeStartDate = normalizeStartDate;
15
- const nanoid_1 = require("nanoid");
16
- const majik_money_1 = require("@thezelijah/majik-money");
1
+ import { customAlphabet } from "nanoid";
2
+ import { MajikMoney } from "@thezelijah/majik-money";
17
3
  /**
18
4
  * Generates a URL-friendly slug from the name,
19
5
  * appending a Unix timestamp to ensure uniqueness.
@@ -21,9 +7,9 @@ const majik_money_1 = require("@thezelijah/majik-money");
21
7
  * @param name - The name to base the slug on.
22
8
  * @returns A unique slug string.
23
9
  */
24
- function generateSlug(name) {
10
+ export function generateSlug(name) {
25
11
  // Create the generator function ONCE with your custom alphabet and length
26
- const generateSlugID = (0, nanoid_1.customAlphabet)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 6);
12
+ const generateSlugID = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 6);
27
13
  const genUID = generateSlugID(); // e.g., 'X4tF9z'
28
14
  const slugText = name
29
15
  .toLowerCase()
@@ -37,24 +23,24 @@ function generateSlug(name) {
37
23
  * @param prefix - The prefix string name to add.
38
24
  * @returns A unique ID string prefixed.
39
25
  */
40
- function autogenerateID(prefix = "majik") {
26
+ export function autogenerateID(prefix = "majik") {
41
27
  // Create the generator function ONCE with your custom alphabet and length
42
- const generateID = (0, nanoid_1.customAlphabet)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 8);
28
+ const generateID = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 8);
43
29
  // Call the generator function to produce the actual ID string
44
30
  const genUID = generateID(); // Example output: 'G7K2aZp9'
45
31
  return `${prefix}-${genUID}`;
46
32
  }
47
- function isValidYYYYMM(month) {
33
+ export function isValidYYYYMM(month) {
48
34
  return /^\d{4}-0[1-9]|1[0-2]$/.test(month);
49
35
  }
50
- function createZeroValueRatio(currencyCode) {
51
- const zero = majik_money_1.MajikMoney.fromMinor(0, currencyCode);
36
+ export function createZeroValueRatio(currencyCode) {
37
+ const zero = MajikMoney.fromMinor(0, currencyCode);
52
38
  return {
53
39
  value: zero,
54
40
  marginRatio: 0,
55
41
  };
56
42
  }
57
- function createEmptySubscriptionFinance(currencyCode) {
43
+ export function createEmptySubscriptionFinance(currencyCode) {
58
44
  return {
59
45
  revenue: {
60
46
  gross: createZeroValueRatio(currencyCode),
@@ -74,7 +60,7 @@ function createEmptySubscriptionFinance(currencyCode) {
74
60
  },
75
61
  };
76
62
  }
77
- function isoToYYYYMM(isoDate) {
63
+ export function isoToYYYYMM(isoDate) {
78
64
  const date = new Date(isoDate);
79
65
  if (isNaN(date.getTime())) {
80
66
  throw new Error("Invalid ISO date");
@@ -84,12 +70,12 @@ function isoToYYYYMM(isoDate) {
84
70
  const month = String(date.getUTCMonth() + 1).padStart(2, "0");
85
71
  return `${year}-${month}`;
86
72
  }
87
- function yyyyMMToISO(yyyyMM) {
73
+ export function yyyyMMToISO(yyyyMM) {
88
74
  const [year, month] = yyyyMM.split("-").map(Number);
89
75
  // Construct as UTC Midnight
90
76
  return new Date(Date.UTC(year, month - 1, 1)).toISOString();
91
77
  }
92
- function dateToYYYYMM(date) {
78
+ export function dateToYYYYMM(date) {
93
79
  if (isNaN(date.getTime())) {
94
80
  throw new Error("Invalid Date object");
95
81
  }
@@ -97,11 +83,11 @@ function dateToYYYYMM(date) {
97
83
  const month = String(date.getUTCMonth() + 1).padStart(2, "0");
98
84
  return `${year}-${month}`;
99
85
  }
100
- function yyyyMMToDate(yyyyMM) {
86
+ export function yyyyMMToDate(yyyyMM) {
101
87
  const [year, month] = yyyyMM.split("-").map(Number);
102
88
  return new Date(Date.UTC(year, month - 1, 1));
103
89
  }
104
- function offsetMonthsToYYYYMM(input, offsetMonths) {
90
+ export function offsetMonthsToYYYYMM(input, offsetMonths) {
105
91
  const date = normalizeStartDate(input);
106
92
  // Since normalizeStartDate now returns a UTC date,
107
93
  // we can safely use UTC methods here.
@@ -110,7 +96,7 @@ function offsetMonthsToYYYYMM(input, offsetMonths) {
110
96
  const result = new Date(Date.UTC(year, month, 1));
111
97
  return dateToYYYYMM(result);
112
98
  }
113
- function monthsInPeriod(earlier, later) {
99
+ export function monthsInPeriod(earlier, later) {
114
100
  const start = yyyyMMToDate(earlier);
115
101
  const end = yyyyMMToDate(later);
116
102
  const startYear = start.getUTCFullYear();
@@ -119,7 +105,7 @@ function monthsInPeriod(earlier, later) {
119
105
  const endMonth = end.getUTCMonth();
120
106
  return (endYear - startYear) * 12 + (endMonth - startMonth) + 1;
121
107
  }
122
- function normalizeStartDate(input) {
108
+ export function normalizeStartDate(input) {
123
109
  if (!input) {
124
110
  const now = new Date();
125
111
  // Return UTC version of the first of the current month
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thezelijah/majik-subscription",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "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.",
5
5
  "license": "ISC",
6
6
  "author": "Zelijah",
@@ -15,30 +15,30 @@
15
15
  "type": "git",
16
16
  "url": "git+https://github.com/jedlsf/majik-subscription.git"
17
17
  },
18
- "keywords": [
19
- "majik",
20
- "majik-subscription",
21
- "subscription-management",
22
- "subscription-finance",
23
- "cost-of-subscription",
24
- "COS",
25
- "revenue-calculation",
26
- "profit-calculation",
27
- "financial-modeling",
28
- "capacity-planning",
29
- "resource-planning",
30
- "timesheet",
31
- "billing",
32
- "pricing",
33
- "business-tools",
34
- "typescript",
35
- "javascript",
36
- "enterprise-tools",
37
- "monetary-calculation",
38
- "majik-money",
39
- "subscription-analytics",
40
- "forecasting"
41
- ],
18
+ "keywords": [
19
+ "majik",
20
+ "majik-subscription",
21
+ "subscription-management",
22
+ "subscription-finance",
23
+ "cost-of-subscription",
24
+ "COS",
25
+ "revenue-calculation",
26
+ "profit-calculation",
27
+ "financial-modeling",
28
+ "capacity-planning",
29
+ "resource-planning",
30
+ "timesheet",
31
+ "billing",
32
+ "pricing",
33
+ "business-tools",
34
+ "typescript",
35
+ "javascript",
36
+ "enterprise-tools",
37
+ "monetary-calculation",
38
+ "majik-money",
39
+ "subscription-analytics",
40
+ "forecasting"
41
+ ],
42
42
  "homepage": "https://github.com/jedlsf/majik-subscription#readme",
43
43
  "bugs": {
44
44
  "url": "https://github.com/jedlsf/majik-subscription/issues"
@@ -49,7 +49,7 @@
49
49
  "prepublishOnly": "npm run build"
50
50
  },
51
51
  "dependencies": {
52
- "@thezelijah/majik-money": "^1.0.3",
52
+ "@thezelijah/majik-money": "^1.0.4",
53
53
  "nanoid": "^5.1.6",
54
54
  "typescript": "^5.9.3"
55
55
  }