@venturekit-pro/billing 0.0.39 → 0.0.41

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 CHANGED
@@ -89,6 +89,30 @@ the upgrade prompt and renders through `@venturekit/runtime`'s error boundary
89
89
  without a runtime dependency. Call `entitlements.invalidate(tenantId)` after a
90
90
  plan change.
91
91
 
92
+ ## Dunning
93
+
94
+ Which payment reminder an unpaid invoice is due, for a cron to ask about every open
95
+ invoice. Pure and clock-free; the mail and the invoice model are yours.
96
+
97
+ ```typescript
98
+ import { nextDunningStep, dunningScheduleRefusal } from '@venturekit-pro/billing';
99
+
100
+ const schedule = { enabled: true, offsetsDays: [-3, 7, 14, 30] }; // days from the due date
101
+ dunningScheduleRefusal(schedule.offsetsDays); // null, or why the setting is refused
102
+
103
+ const step = nextDunningStep({
104
+ dueDate: invoice.dueDate, // 'YYYY-MM-DD'
105
+ schedule,
106
+ sent: reminders.map((r) => ({ sentAtISO: r.sentAt, offsetDays: r.step })), // step null = sent by a person
107
+ today: todayIn(customer.timezone),
108
+ });
109
+ if (step !== null) await sendReminder(invoice, step); // and record { step } for next time
110
+ ```
111
+
112
+ The latest step whose day has come is due; each step goes once; steps skipped over
113
+ are gone (the schedule says *when to nudge*, not how many messages to catch up on);
114
+ and a reminder a person sent since the previous step covers the current one.
115
+
92
116
  ## Usage-to-Invoice Mapping
93
117
 
94
118
  ```typescript
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Dunning schedule — when an unpaid invoice is due a payment reminder.
3
+ *
4
+ * Every product that invoices ends up chasing payment, and the rule is the same
5
+ * everywhere: a handful of **steps**, each a number of days from the due date
6
+ * (negative before it — "three days to go", positive after — "a week late"),
7
+ * each sent once, and never on top of a human who has already written. Payment
8
+ * processing, the mail itself and the invoice model are the app's; this module
9
+ * is only the decision, pure and clock-free, so a cron can ask it for every open
10
+ * invoice and a test can pin every edge.
11
+ *
12
+ * Three rules, in {@link nextDunningStep}:
13
+ *
14
+ * 1. **The latest step whose day has come is the candidate.** Steps skipped
15
+ * over — the cron was down, the schedule was edited — are gone: the schedule
16
+ * describes *when to nudge*, not a quota of messages to catch up on.
17
+ * 2. **Each step goes once.** A step the schedule already sent (or a later one:
18
+ * a step sent a day late is still that step) is not due again.
19
+ * 3. **Never over a person.** A reminder somebody sent by hand since the
20
+ * previous step's day has done that stretch's nudging; the step is skipped.
21
+ *
22
+ * Days are compared as `YYYY-MM-DD` strings in whatever zone the caller renders
23
+ * them in — the app decides what "today" is for its customers (their business
24
+ * zone, not the server's), and passes it in.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const schedule = { enabled: true, offsetsDays: [-3, 7, 14, 30] };
29
+ * const step = nextDunningStep({
30
+ * dueDate: invoice.dueDate,
31
+ * schedule,
32
+ * sent: reminders.map((r) => ({ sentAtISO: r.sentAt, offsetDays: r.offsetDays })),
33
+ * today: todayIn('Africa/Casablanca'),
34
+ * });
35
+ * if (step !== null) await sendReminder(invoice, step);
36
+ * ```
37
+ */
38
+ /** The steps of a dunning schedule, in days from the invoice's due date. */
39
+ export interface DunningSchedule {
40
+ /** Off means no automatic reminder at all; by-hand reminders are unaffected. */
41
+ enabled: boolean;
42
+ /** Days from the due date; negative is before it. Any order, duplicates ignored. */
43
+ offsetsDays: readonly number[];
44
+ }
45
+ /**
46
+ * A reminder already sent about the invoice: when, and — for one the schedule
47
+ * sent — which step it was. `offsetDays: null` is a reminder a person sent.
48
+ */
49
+ export interface DunningReminderSent {
50
+ sentAtISO: string;
51
+ offsetDays: number | null;
52
+ }
53
+ export interface NextDunningStepInput {
54
+ /** The invoice's due date: `YYYY-MM-DD`, or an ISO instant whose date part is used. */
55
+ dueDate: string;
56
+ schedule: DunningSchedule;
57
+ /** Every reminder already sent about this invoice, by the schedule or by hand. */
58
+ sent: readonly DunningReminderSent[];
59
+ /** Today, `YYYY-MM-DD`, in the zone the app chases in. */
60
+ today: string;
61
+ }
62
+ /**
63
+ * The bounds a schedule is kept within: a handful of steps, none absurdly far from
64
+ * the due date. Apps that expose the schedule as a setting apply the same bounds on
65
+ * the form ({@link dunningScheduleRefusal}).
66
+ */
67
+ export declare const DUNNING_SCHEDULE_LIMITS: {
68
+ readonly maxSteps: 8;
69
+ readonly minOffsetDays: -60;
70
+ readonly maxOffsetDays: 365;
71
+ };
72
+ /** Why a schedule's steps are refused, or `null` when they are acceptable. */
73
+ export declare function dunningScheduleRefusal(offsetsDays: readonly number[]): string | null;
74
+ /** The steps as the schedule applies them: distinct, ascending. */
75
+ export declare function normalizeDunningSteps(offsetsDays: readonly number[]): number[];
76
+ /** The `YYYY-MM-DD` part of a date or instant. */
77
+ export declare function dayOf(dateOrInstant: string): string;
78
+ /** `YYYY-MM-DD` moved by `days`, computed in UTC so the calendar arithmetic is zone-free. */
79
+ export declare function addDays(day: string, days: number): string;
80
+ /**
81
+ * The step due today for an invoice, or `null` — see the module note for the
82
+ * three rules. The invoice's own state (paid, void, draft) is the caller's to
83
+ * check first: this decides *which* reminder, not *whether* the invoice is owed.
84
+ */
85
+ export declare function nextDunningStep({ dueDate, schedule, sent, today }: NextDunningStepInput): number | null;
86
+ //# sourceMappingURL=schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/dunning/schedule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,uFAAuF;IACvF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,eAAe,CAAC;IAC1B,kFAAkF;IAClF,IAAI,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACrC,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;CAI1B,CAAC;AAEX,8EAA8E;AAC9E,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAQpF;AAED,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAE9E;AAED,kDAAkD;AAClD,wBAAgB,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,6FAA6F;AAC7F,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,CAcvG"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Dunning schedule — when an unpaid invoice is due a payment reminder.
3
+ *
4
+ * Every product that invoices ends up chasing payment, and the rule is the same
5
+ * everywhere: a handful of **steps**, each a number of days from the due date
6
+ * (negative before it — "three days to go", positive after — "a week late"),
7
+ * each sent once, and never on top of a human who has already written. Payment
8
+ * processing, the mail itself and the invoice model are the app's; this module
9
+ * is only the decision, pure and clock-free, so a cron can ask it for every open
10
+ * invoice and a test can pin every edge.
11
+ *
12
+ * Three rules, in {@link nextDunningStep}:
13
+ *
14
+ * 1. **The latest step whose day has come is the candidate.** Steps skipped
15
+ * over — the cron was down, the schedule was edited — are gone: the schedule
16
+ * describes *when to nudge*, not a quota of messages to catch up on.
17
+ * 2. **Each step goes once.** A step the schedule already sent (or a later one:
18
+ * a step sent a day late is still that step) is not due again.
19
+ * 3. **Never over a person.** A reminder somebody sent by hand since the
20
+ * previous step's day has done that stretch's nudging; the step is skipped.
21
+ *
22
+ * Days are compared as `YYYY-MM-DD` strings in whatever zone the caller renders
23
+ * them in — the app decides what "today" is for its customers (their business
24
+ * zone, not the server's), and passes it in.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const schedule = { enabled: true, offsetsDays: [-3, 7, 14, 30] };
29
+ * const step = nextDunningStep({
30
+ * dueDate: invoice.dueDate,
31
+ * schedule,
32
+ * sent: reminders.map((r) => ({ sentAtISO: r.sentAt, offsetDays: r.offsetDays })),
33
+ * today: todayIn('Africa/Casablanca'),
34
+ * });
35
+ * if (step !== null) await sendReminder(invoice, step);
36
+ * ```
37
+ */
38
+ /**
39
+ * The bounds a schedule is kept within: a handful of steps, none absurdly far from
40
+ * the due date. Apps that expose the schedule as a setting apply the same bounds on
41
+ * the form ({@link dunningScheduleRefusal}).
42
+ */
43
+ export const DUNNING_SCHEDULE_LIMITS = {
44
+ maxSteps: 8,
45
+ minOffsetDays: -60,
46
+ maxOffsetDays: 365,
47
+ };
48
+ /** Why a schedule's steps are refused, or `null` when they are acceptable. */
49
+ export function dunningScheduleRefusal(offsetsDays) {
50
+ const { maxSteps, minOffsetDays, maxOffsetDays } = DUNNING_SCHEDULE_LIMITS;
51
+ if (offsetsDays.length > maxSteps)
52
+ return `At most ${maxSteps} reminder steps`;
53
+ if (offsetsDays.some((d) => !Number.isInteger(d) || d < minOffsetDays || d > maxOffsetDays)) {
54
+ return `Each reminder step is a whole number of days between ${minOffsetDays} and ${maxOffsetDays}`;
55
+ }
56
+ if (new Set(offsetsDays).size !== offsetsDays.length)
57
+ return 'Reminder steps must be distinct';
58
+ return null;
59
+ }
60
+ /** The steps as the schedule applies them: distinct, ascending. */
61
+ export function normalizeDunningSteps(offsetsDays) {
62
+ return [...new Set(offsetsDays)].sort((a, b) => a - b);
63
+ }
64
+ /** The `YYYY-MM-DD` part of a date or instant. */
65
+ export function dayOf(dateOrInstant) {
66
+ return dateOrInstant.slice(0, 10);
67
+ }
68
+ /** `YYYY-MM-DD` moved by `days`, computed in UTC so the calendar arithmetic is zone-free. */
69
+ export function addDays(day, days) {
70
+ const d = new Date(`${day}T00:00:00Z`);
71
+ d.setUTCDate(d.getUTCDate() + days);
72
+ return d.toISOString().slice(0, 10);
73
+ }
74
+ /**
75
+ * The step due today for an invoice, or `null` — see the module note for the
76
+ * three rules. The invoice's own state (paid, void, draft) is the caller's to
77
+ * check first: this decides *which* reminder, not *whether* the invoice is owed.
78
+ */
79
+ export function nextDunningStep({ dueDate, schedule, sent, today }) {
80
+ if (!schedule.enabled)
81
+ return null;
82
+ const steps = normalizeDunningSteps(schedule.offsetsDays);
83
+ const due = dayOf(dueDate);
84
+ const dayFor = (offset) => addDays(due, offset);
85
+ const index = steps.filter((offset) => dayFor(offset) <= today).length - 1;
86
+ if (index < 0)
87
+ return null;
88
+ const step = steps[index];
89
+ if (sent.some((r) => r.offsetDays !== null && r.offsetDays >= step))
90
+ return null;
91
+ const previousStepDay = index > 0 ? dayFor(steps[index - 1]) : null;
92
+ const byHandSince = sent.some((r) => r.offsetDays === null && (previousStepDay === null || dayOf(r.sentAtISO) > previousStepDay));
93
+ return byHandSince ? null : step;
94
+ }
95
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/dunning/schedule.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -30,6 +30,8 @@ export { createFeatureGate, } from './gating/feature-gate.js';
30
30
  export type { FeatureCheckResult, FeatureGateOptions, } from './gating/feature-gate.js';
31
31
  export { resolveEntitlements, createEntitlementsResolver, EntitlementError, } from './entitlements/entitlements.js';
32
32
  export type { Entitlements, EntitlementOverrides, EntitlementErrorCode, EntitlementsResolver, EntitlementsResolverOptions, ResolvedFeature, LimitCheck, } from './entitlements/entitlements.js';
33
+ export { nextDunningStep, dunningScheduleRefusal, normalizeDunningSteps, DUNNING_SCHEDULE_LIMITS, } from './dunning/schedule.js';
34
+ export type { DunningSchedule, DunningReminderSent, NextDunningStepInput, } from './dunning/schedule.js';
33
35
  /**
34
36
  * Returns the path to the billing migration SQL file.
35
37
  * Used by `vk migrate` to auto-create billing tables
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,WAAW,EACX,eAAe,EACf,UAAU,EACV,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpE,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,eAAe,GAChB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,YAAY,EACZ,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,eAAe,EACf,UAAU,GACX,MAAM,gCAAgC,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAGhD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EACL,WAAW,EACX,eAAe,EACf,UAAU,EACV,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpE,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,eAAe,GAChB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,YAAY,EACZ,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,eAAe,EACf,UAAU,GACX,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAGhD"}
package/dist/index.js CHANGED
@@ -33,6 +33,8 @@ export { createUsageTracker, createMemoryUsageStore, } from './usage/tracker.js'
33
33
  export { createFeatureGate, } from './gating/feature-gate.js';
34
34
  // Entitlements (plan + overrides → what a tenant may do, HTTP-mapped refusals)
35
35
  export { resolveEntitlements, createEntitlementsResolver, EntitlementError, } from './entitlements/entitlements.js';
36
+ // Dunning (which payment reminder an unpaid invoice is due — pure, for a cron)
37
+ export { nextDunningStep, dunningScheduleRefusal, normalizeDunningSteps, DUNNING_SCHEDULE_LIMITS, } from './dunning/schedule.js';
36
38
  /**
37
39
  * Returns the path to the billing migration SQL file.
38
40
  * Used by `vk migrate` to auto-create billing tables
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,0BAA0B;AAC1B,OAAO,EACL,WAAW,EACX,eAAe,EACf,UAAU,EACV,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,0BAA0B;AAC1B,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAOpC,qBAAqB;AACrB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AAMjC,iBAAiB;AACjB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAO5B,iBAAiB;AACjB,OAAO,EACL,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAMlC,+EAA+E;AAC/E,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,gCAAgC,CAAC;AAWxC;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,QAAQ;AACR,cAAc,kBAAkB,CAAC;AAEjC,0BAA0B;AAC1B,OAAO,EACL,WAAW,EACX,eAAe,EACf,UAAU,EACV,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,0BAA0B;AAC1B,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAOpC,qBAAqB;AACrB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AAMjC,iBAAiB;AACjB,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAO5B,iBAAiB;AACjB,OAAO,EACL,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAMlC,+EAA+E;AAC/E,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,gCAAgC,CAAC;AAWxC,+EAA+E;AAC/E,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAO/B;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAC5C,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venturekit-pro/billing",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "description": "Invoicing, plans, usage tracking, and feature gating for VentureKit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
- "@venturekit/core": "0.0.39"
33
+ "@venturekit/core": "0.0.41"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^26.5.1",