@arnaudjnn/billing-tools 0.55.0 → 0.57.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.
Files changed (59) hide show
  1. package/dist/adapters/workos-org.d.ts +2 -0
  2. package/dist/adapters/workos-org.d.ts.map +1 -1
  3. package/dist/adapters/workos-org.js +4 -0
  4. package/dist/adapters/workos-org.js.map +1 -1
  5. package/dist/allowance.d.ts +81 -0
  6. package/dist/allowance.d.ts.map +1 -0
  7. package/dist/allowance.js +130 -0
  8. package/dist/allowance.js.map +1 -0
  9. package/dist/billing.d.ts.map +1 -1
  10. package/dist/billing.js +4 -0
  11. package/dist/billing.js.map +1 -1
  12. package/dist/doctor.d.ts +11 -0
  13. package/dist/doctor.d.ts.map +1 -1
  14. package/dist/doctor.js +109 -0
  15. package/dist/doctor.js.map +1 -1
  16. package/dist/index.d.ts +7 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +18 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/metering.d.ts +38 -14
  21. package/dist/metering.d.ts.map +1 -1
  22. package/dist/metering.js +65 -42
  23. package/dist/metering.js.map +1 -1
  24. package/dist/plan-model.d.ts +395 -0
  25. package/dist/plan-model.d.ts.map +1 -0
  26. package/dist/plan-model.js +342 -0
  27. package/dist/plan-model.js.map +1 -0
  28. package/dist/plans.d.ts +25 -53
  29. package/dist/plans.d.ts.map +1 -1
  30. package/dist/plans.js +55 -34
  31. package/dist/plans.js.map +1 -1
  32. package/dist/pricing.d.ts +130 -0
  33. package/dist/pricing.d.ts.map +1 -0
  34. package/dist/pricing.js +260 -0
  35. package/dist/pricing.js.map +1 -0
  36. package/dist/sync.d.ts +3 -16
  37. package/dist/sync.d.ts.map +1 -1
  38. package/dist/sync.js +45 -26
  39. package/dist/sync.js.map +1 -1
  40. package/dist/tax-ids.d.ts +26 -0
  41. package/dist/tax-ids.d.ts.map +1 -0
  42. package/dist/tax-ids.js +50 -0
  43. package/dist/tax-ids.js.map +1 -0
  44. package/dist/types.d.ts +5 -0
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/types.js.map +1 -1
  47. package/dist/ui/index.d.ts +1 -0
  48. package/dist/ui/index.d.ts.map +1 -1
  49. package/dist/ui/index.js +2 -0
  50. package/dist/ui/index.js.map +1 -1
  51. package/dist/ui/tax-id-types.d.ts +10 -0
  52. package/dist/ui/tax-id-types.d.ts.map +1 -0
  53. package/dist/ui/tax-id-types.js +140 -0
  54. package/dist/ui/tax-id-types.js.map +1 -0
  55. package/dist/usage-ledger.d.ts +87 -0
  56. package/dist/usage-ledger.d.ts.map +1 -0
  57. package/dist/usage-ledger.js +117 -0
  58. package/dist/usage-ledger.js.map +1 -0
  59. package/package.json +6 -1
@@ -0,0 +1,342 @@
1
+ // What a plan IS, as data — and the normaliser that turns any accepted shape
2
+ // into one internal form.
3
+ //
4
+ // ZERO IMPORTS, deliberately. `plans.ts` imports `billing.ts` → `stripe`, so
5
+ // anything defined there can never be read by a browser bundle or a docs
6
+ // generator. The plan model has to be readable by both (a pricing card and a
7
+ // markdown table are the same derivation), so it lives here on its own, like
8
+ // `ui/limits.ts` does for the same reason.
9
+ //
10
+ // ── Why five axes instead of one ────────────────────────────────────────────
11
+ //
12
+ // A plan used to be `{ seats, tokensPerSeat, price, seatTypes?, allowanceMode? }`
13
+ // and that shape can only really express one product: per-seat, with a per-seat
14
+ // pack. Everything else was squeezed into `allowanceMode`, which despite its
15
+ // name does exactly ONE thing — skip the per-seat cap. There was no org-level
16
+ // allowance anywhere, so "we don't care about seats, here is a package of N tool
17
+ // requests" was unrepresentable, and a plan-level `tokensPerSeat` on such a plan
18
+ // was simply never read.
19
+ //
20
+ // What varies between products turns out to be five independent things:
21
+ //
22
+ // sells what Stripe charges for (nothing | seats | flat)
23
+ // grant what is CREDITED as money (none | purchased_seats | …)
24
+ // cap what is INCLUDED, as a limit (wallet | per_seat | pool)
25
+ // replenish how to get more (purchase | autoReload | request)
26
+ // sale whether it can be bought (free | self_serve | quote | legacy)
27
+ //
28
+ // Only `sells` is a discriminated union, because it alone decides which other
29
+ // fields are REQUIRED and what `ensurePlans` mints. Tagging all five under one
30
+ // `kind` would need a member per combination (`flat_pool_block_quote`), so the
31
+ // rest are plain fields with defaults derived from `sells`.
32
+ //
33
+ // ── Why `grant` and `cap` are different axes ────────────────────────────────
34
+ //
35
+ // This is the distinction the old model was missing, and it is a money bug, not
36
+ // a modelling preference. `grant` credits the Stripe customer balance. A Stripe
37
+ // credit balance AUTO-APPLIES to the customer's next invoice and cannot be
38
+ // opted out of — measured: granting 1000 tokens to a customer on a €21.04 seat
39
+ // produced an invoice with `starting_balance: -1000` and `amount_due: 1104`. So
40
+ // crediting a plan's "included" allowance discounts its own renewal by half, and
41
+ // an annual pool credited as money would invoice year two at zero.
42
+ //
43
+ // An included allowance is therefore a `cap`: a window that usage is COUNTED
44
+ // against, with no money moving. Credit stays what it is actually for —
45
+ // PURCHASED tokens (a top-up), which the customer really can spend.
46
+ export const INTERVALS = ["monthly", "yearly"];
47
+ export const isLegacyPlan = (d) => !("sells" in d);
48
+ /**
49
+ * Identity helper for a plans config.
50
+ *
51
+ * Annotating with `PlanCatalog` widens the keys to `string` and loses `display`
52
+ * autocomplete; this keeps the literal types while still checking the shape.
53
+ */
54
+ export function definePlans(plans) {
55
+ return plans;
56
+ }
57
+ const hasPrice = (p) => p.monthly > 0 || p.yearly > 0;
58
+ function normalizeSeatTypes(seatTypes) {
59
+ return Object.entries(seatTypes).map(([key, s]) => {
60
+ const legacy = s;
61
+ const spec = s;
62
+ return {
63
+ key,
64
+ price: s.price,
65
+ includedTokens: s.includedTokens ?? 0,
66
+ min: spec.min ?? 0,
67
+ // `seats` was the legacy name for a per-type cap.
68
+ max: spec.max ?? legacy.seats ?? null,
69
+ shared: spec.shared ?? false,
70
+ display: spec.display ?? (legacy.label ? { label: legacy.label } : null),
71
+ };
72
+ });
73
+ }
74
+ /** Intervals a price actually exists for, so a plan can't advertise an interval
75
+ * Stripe has no price for. */
76
+ function soldIntervals(declared, price, seatTypes) {
77
+ if (declared)
78
+ return declared;
79
+ const has = (i) => (price ? price[i] > 0 : false) || seatTypes.some((s) => s.price[i] > 0);
80
+ return INTERVALS.filter(has);
81
+ }
82
+ export function normalizePlan(key, spec) {
83
+ if (!isLegacyPlan(spec)) {
84
+ const seatTypes = spec.sells.kind === "seats" ? normalizeSeatTypes(spec.sells.seatTypes) : [];
85
+ const price = spec.sells.kind === "flat" ? spec.sells.price : null;
86
+ const declared = spec.sells.kind === "nothing" ? [] : spec.sells.intervals;
87
+ return {
88
+ key,
89
+ sells: spec.sells,
90
+ grant: spec.grant ?? { kind: spec.sells.kind === "seats" ? "purchased_seats" : "none" },
91
+ cap: spec.cap ?? { kind: spec.sells.kind === "seats" ? "per_seat" : "wallet" },
92
+ replenish: spec.replenish ?? {},
93
+ sale: spec.sale,
94
+ limits: { members: spec.limits?.members ?? null },
95
+ display: spec.display ?? null,
96
+ intervals: soldIntervals(declared, price, seatTypes),
97
+ seatTypes,
98
+ price,
99
+ legacy: false,
100
+ };
101
+ }
102
+ // Legacy. Every mapping here is behaviour-preserving; the property to hold is
103
+ // that no legacy config can produce `cap: "pool"`, so the new entitlement
104
+ // path stays dead until a config opts in.
105
+ const seatTypes = spec.seatTypes ? normalizeSeatTypes(spec.seatTypes) : [];
106
+ const sells = spec.seatTypes
107
+ ? { kind: "seats", seatTypes: spec.seatTypes }
108
+ : hasPrice(spec.price)
109
+ ? { kind: "flat", price: spec.price }
110
+ : { kind: "nothing" };
111
+ const grant = spec.seatTypes
112
+ ? { kind: "purchased_seats" }
113
+ : hasPrice(spec.price)
114
+ ? { kind: "per_member", tokens: spec.tokensPerSeat }
115
+ : { kind: "none" };
116
+ return {
117
+ key,
118
+ sells,
119
+ grant,
120
+ // "global" meant "no cap", full stop.
121
+ cap: spec.allowanceMode === "global"
122
+ ? { kind: "wallet" }
123
+ : { kind: "per_seat", onExhausted: "block" },
124
+ replenish: {},
125
+ // Not knowable from the legacy shape, so it is guessed from whether ANY
126
+ // price exists — note a seat-typed plan whose every seat is 0 mints no
127
+ // Stripe price and therefore cannot be bought, which the old `price > 0`
128
+ // test on the plan-level amount got wrong in both directions. The doctor
129
+ // warns either way, because guessing this is what sold a quote-only plan.
130
+ sale: soldIntervals(undefined, sells.kind === "flat" ? spec.price : null, seatTypes).length === 0
131
+ ? "free"
132
+ : "self_serve",
133
+ limits: { members: spec.seats },
134
+ display: null,
135
+ intervals: soldIntervals(undefined, sells.kind === "flat" ? spec.price : null, seatTypes),
136
+ seatTypes,
137
+ price: sells.kind === "flat" ? spec.price : null,
138
+ legacy: true,
139
+ };
140
+ }
141
+ export function normalizePlans(plans) {
142
+ return Object.entries(plans).map(([key, spec]) => normalizePlan(key, spec));
143
+ }
144
+ export function planModel(plans, key) {
145
+ if (!key)
146
+ return null;
147
+ const spec = plans[key];
148
+ return spec ? normalizePlan(key, spec) : null;
149
+ }
150
+ /** Plan keys matching a predicate, e.g. `plansWhere(PLANS, p => p.sale === "self_serve")`.
151
+ * Replaces the several hand-maintained lists of "which plans can be bought". */
152
+ export function plansWhere(plans, predicate) {
153
+ return normalizePlans(plans)
154
+ .filter(predicate)
155
+ .map((m) => m.key);
156
+ }
157
+ /** Plans a customer can buy without talking to anyone. */
158
+ export const selfServePlans = (plans) => plansWhere(plans, (p) => p.sale === "self_serve" && p.sells.kind !== "nothing");
159
+ /** Where a seat stepper starts: each type's `min`, with the plan's `minSeats`
160
+ * absorbed by the first non-shared type so the default basket is already valid. */
161
+ export function defaultBasket(model) {
162
+ const basket = {};
163
+ for (const s of model.seatTypes)
164
+ basket[s.key] = s.min;
165
+ if (model.sells.kind !== "seats")
166
+ return basket;
167
+ const min = model.sells.minSeats ?? 0;
168
+ const total = Object.values(basket).reduce((a, b) => a + b, 0);
169
+ if (total < min) {
170
+ const first = model.seatTypes.find((s) => !s.shared) ?? model.seatTypes[0];
171
+ if (first)
172
+ basket[first.key] = (basket[first.key] ?? 0) + (min - total);
173
+ }
174
+ return basket;
175
+ }
176
+ /**
177
+ * Everything wrong with a basket, as data.
178
+ *
179
+ * Pure and network-free, so it can run in a stepper and at the checkout
180
+ * boundary from one implementation. `seats` and the per-type caps were declared
181
+ * for a long time and enforced nowhere: a crafted request could buy any
182
+ * quantity of any seat type, including fifty of a seat meant to be unique.
183
+ */
184
+ export function validateBasket(plans, opts) {
185
+ const model = planModel(plans, opts.plan);
186
+ if (!model)
187
+ return [{ code: "unknown_plan" }];
188
+ const problems = [];
189
+ const purchasing = (opts.for ?? "purchase") === "purchase";
190
+ if (purchasing && model.sale !== "self_serve") {
191
+ problems.push({ code: "not_purchasable", sale: model.sale });
192
+ }
193
+ if (opts.interval && model.intervals.length && !model.intervals.includes(opts.interval)) {
194
+ problems.push({ code: "interval_unavailable", interval: opts.interval });
195
+ }
196
+ if (model.sells.kind !== "seats")
197
+ return problems;
198
+ const seats = opts.seats ?? {};
199
+ const known = new Map(model.seatTypes.map((s) => [s.key, s]));
200
+ let total = 0;
201
+ for (const [key, qty] of Object.entries(seats)) {
202
+ if (qty <= 0)
203
+ continue;
204
+ const type = known.get(key);
205
+ if (!type) {
206
+ problems.push({ code: "unknown_seat_type", seatType: key });
207
+ continue;
208
+ }
209
+ total += qty;
210
+ if (type.max !== null && qty > type.max) {
211
+ problems.push({ code: "seat_type_limit", seatType: key, max: type.max, got: qty });
212
+ }
213
+ }
214
+ const min = model.sells.minSeats ?? 0;
215
+ if (total < min)
216
+ problems.push({ code: "below_minimum", min, got: total });
217
+ const max = model.sells.maxSeats ?? null;
218
+ if (max !== null && total > max)
219
+ problems.push({ code: "seat_limit", max, got: total });
220
+ const members = model.limits.members;
221
+ if (members !== null && total > members) {
222
+ problems.push({ code: "member_limit", max: members, got: total });
223
+ }
224
+ return problems;
225
+ }
226
+ /** One-line summary of a basket problem, for an error message. */
227
+ export function describeBasketProblem(p) {
228
+ switch (p.code) {
229
+ case "unknown_plan":
230
+ return "Unknown plan";
231
+ case "not_purchasable":
232
+ return `This plan is not self-serve (${p.sale})`;
233
+ case "unknown_seat_type":
234
+ return `Unknown seat type "${p.seatType}"`;
235
+ case "below_minimum":
236
+ return `At least ${p.min} seats are required (got ${p.got})`;
237
+ case "seat_limit":
238
+ return `At most ${p.max} seats (got ${p.got})`;
239
+ case "seat_type_limit":
240
+ return `At most ${p.max} "${p.seatType}" seat(s) (got ${p.got})`;
241
+ case "member_limit":
242
+ return `This plan allows at most ${p.max} members (got ${p.got})`;
243
+ case "interval_unavailable":
244
+ return `This plan is not sold ${p.interval}`;
245
+ }
246
+ }
247
+ // ── Allowance sizing ────────────────────────────────────────────────────────
248
+ /** Tokens to CREDIT for a paid cycle. Zero for `grant: none`, which is now the
249
+ * default for anything whose allowance is an entitlement. */
250
+ export function grantFor(model, ctx) {
251
+ if (!model)
252
+ return 0;
253
+ switch (model.grant.kind) {
254
+ case "none":
255
+ return 0;
256
+ case "fixed":
257
+ return model.grant.tokens;
258
+ case "per_member":
259
+ // The old flat path floored the count at 1 — a subscription with no
260
+ // recorded members still granted one seat's worth.
261
+ return model.grant.tokens * Math.max(1, ctx.memberCount ?? 0);
262
+ case "purchased_seats": {
263
+ const counts = ctx.seatCounts ?? {};
264
+ let sum = 0;
265
+ for (const s of model.seatTypes)
266
+ sum += s.includedTokens * (counts[s.key] ?? 0);
267
+ return sum;
268
+ }
269
+ }
270
+ }
271
+ /** The org-wide entitlement for a cycle, or null when the plan has no pool.
272
+ * Falls back to the grant size when a plan both pools and credits. */
273
+ export function poolSizeOf(model) {
274
+ if (!model || model.cap.kind !== "pool")
275
+ return null;
276
+ if (model.cap.tokens != null)
277
+ return model.cap.tokens;
278
+ return model.grant.kind === "fixed" ? model.grant.tokens : 0;
279
+ }
280
+ /** The pack a caller is entitled to for the cycle, or null when uncapped. */
281
+ export function packSizeOf(model, seatType) {
282
+ if (!model || model.cap.kind !== "per_seat" || !seatType)
283
+ return null;
284
+ const type = model.seatTypes.find((s) => s.key === seatType);
285
+ return type ? type.includedTokens : null;
286
+ }
287
+ /**
288
+ * What to do when a window is used up.
289
+ *
290
+ * An agent's usage overflows into the wallet; a person's blocks. That was
291
+ * previously a hardcoded `caller.kind === "user"` test in the meter, so the
292
+ * caller kind is still honoured — a legacy config has no way to mark a seat type
293
+ * as shared, and changing behaviour for it silently is not on. A config that
294
+ * DOES declare `shared: true` gets the same answer without depending on how the
295
+ * call arrived.
296
+ */
297
+ export function exhaustedPolicy(model, caller) {
298
+ if (!model)
299
+ return "wallet";
300
+ if (caller?.kind === "api")
301
+ return "wallet";
302
+ const type = caller?.seatType
303
+ ? model.seatTypes.find((s) => s.key === caller.seatType)
304
+ : undefined;
305
+ if (type?.shared)
306
+ return "wallet";
307
+ if (model.cap.kind === "pool")
308
+ return model.cap.onExhausted ?? "block";
309
+ if (model.cap.kind === "per_seat")
310
+ return model.cap.onExhausted ?? "block";
311
+ return "wallet";
312
+ }
313
+ const startOfMonthUTC = (now) => {
314
+ const d = new Date(now);
315
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1);
316
+ };
317
+ /**
318
+ * The window usage is measured over.
319
+ *
320
+ * The SUBSCRIPTION period, when one is known — an annual pool measured over
321
+ * calendar months would reset twelve times a year, handing out twelve times the
322
+ * package. The calendar month remains the fallback for an org with no
323
+ * subscription (a free plan, or a pure wallet), which is what this library did
324
+ * unconditionally before.
325
+ *
326
+ * `rollover` widens the window to the subscription's start instead, which is all
327
+ * "unused allowance carries over" has to mean.
328
+ */
329
+ export function cycleWindowFor(model, period, now = Date.now()) {
330
+ const ms = (v) => v == null ? null : typeof v === "number" ? v : Date.parse(v) || null;
331
+ const start = ms(period?.start);
332
+ const end = ms(period?.end);
333
+ const rollover = model?.cap.kind === "pool" && model.cap.rollover === true;
334
+ if (start && (rollover || start <= now)) {
335
+ const from = start;
336
+ const until = rollover ? null : end;
337
+ return { start: from, end: until, key: new Date(from).toISOString().slice(0, 10) };
338
+ }
339
+ const month = startOfMonthUTC(now);
340
+ return { start: month, end: null, key: new Date(month).toISOString().slice(0, 7) };
341
+ }
342
+ //# sourceMappingURL=plan-model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-model.js","sourceRoot":"","sources":["../src/plan-model.ts"],"names":[],"mappings":"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"}
package/dist/plans.d.ts CHANGED
@@ -1,46 +1,7 @@
1
1
  import type Stripe from "stripe";
2
- /** One seat type within a plan (e.g. `standard` vs `premium`). Each type has
3
- * its own recurring per-seat price and included-token allowance, so a plan's
4
- * subscription carries one line item per seat type (quantity = members of that
5
- * type). Introduced for the Claude-style Standard/Premium seat model. */
6
- export interface SeatTypeDef {
7
- /** Per-seat recurring price (cents). 0 = free (no Stripe price). */
8
- price: {
9
- monthly: number;
10
- yearly: number;
11
- };
12
- /** Included tokens granted per seat of THIS type, per billing cycle. */
13
- includedTokens: number;
14
- /** Optional cap on seats of this type (null/undefined = unlimited). */
15
- seats?: number | null;
16
- /** Optional display label. */
17
- label?: string;
18
- }
19
- export interface PlanDef {
20
- /** Max members per workspace. null = unlimited. */
21
- seats: number | null;
22
- /** Included tokens granted per seat, per billing cycle (flat model). */
23
- tokensPerSeat: number;
24
- /** Recurring price in the smallest currency unit (cents). 0 = free (no Stripe price). */
25
- price: {
26
- monthly: number;
27
- yearly: number;
28
- };
29
- /** Optional multi-seat-type pricing. When present, `ensurePlans` mints one
30
- * Stripe price per (plan, seatType, interval), the subscription carries one
31
- * line item per seat type, and included tokens sum per type
32
- * (`includedTokensByType`). When ABSENT the flat {seats, tokensPerSeat,
33
- * price} model applies unchanged — existing consumers are unaffected. */
34
- seatTypes?: Record<string, SeatTypeDef>;
35
- /** How usage is metered against the plan's seats (default `per_seat`):
36
- * • `per_seat` — each seat has its own per-cycle pack (user seats capped
37
- * personally; the API seat is a shared pool + top-up).
38
- * • `global` — pay per seat, but NO per-seat cap: all usage draws one
39
- * committed workspace token pool (e.g. an Enterprise annual commitment). */
40
- allowanceMode?: "per_seat" | "global";
41
- }
42
- export type PlansConfig = Record<string, PlanDef>;
43
- export type BillingInterval = "monthly" | "yearly";
2
+ import { type BillingInterval, type PlanCatalog, type Sale, type SeatTypeDef } from "./plan-model.js";
3
+ export type { BillingInterval, Money, IntervalPrice, SeatTypeDef, SeatTypeSpec, SeatTypeDisplay, PlanDef, PlanSpec, PlanDisplay, PlanLimits, PlansConfig, PlanCatalog, PlanModel, NormalSeatType, Sells, Grant, Cap, Exhausted, Replenish, Sale, Quantities, BasketProblem, CycleWindow, } from "./plan-model.js";
4
+ export { definePlans, isLegacyPlan, normalizePlan, normalizePlans, planModel, plansWhere, selfServePlans, defaultBasket, validateBasket, describeBasketProblem, grantFor, poolSizeOf, packSizeOf, exhaustedPolicy, cycleWindowFor, } from "./plan-model.js";
44
5
  export declare const DEFAULT_SEAT_TYPES: Record<string, SeatTypeDef>;
45
6
  export declare const lookupKeyFor: (plan: string, interval: BillingInterval, seatType?: string) => string;
46
7
  export interface EnsuredPrice {
@@ -56,7 +17,7 @@ export interface EnsuredPrice {
56
17
  /** Idempotently create/reconcile Stripe products + prices for the paid plans.
57
18
  * Returns the resolved price for every paid plan × interval. Free plans (both
58
19
  * prices 0) create no Stripe objects. Safe to call on every boot / first use. */
59
- export declare function ensurePlans(plans: PlansConfig, opts?: {
20
+ export declare function ensurePlans(plans: PlanCatalog, opts?: {
60
21
  currency?: string;
61
22
  taxBehavior?: Stripe.Price.TaxBehavior;
62
23
  /**
@@ -115,7 +76,7 @@ export interface MigrateSubscriptionsResult {
115
76
  * price attached by hand in the Dashboard is never moved out from under you.
116
77
  */
117
78
  export declare function migrateSubscriptions(opts: {
118
- plans: PlansConfig;
79
+ plans: PlanCatalog;
119
80
  plan: string;
120
81
  interval: BillingInterval;
121
82
  currency?: string;
@@ -136,7 +97,7 @@ export type PlanPrices = ReadonlyMap<string, string>;
136
97
  *
137
98
  * Look ids up with `lookupKeyFor(plan, interval, seatType)`.
138
99
  */
139
- export declare function resolvePlanPrices(plans: PlansConfig, opts?: {
100
+ export declare function resolvePlanPrices(plans: PlanCatalog, opts?: {
140
101
  currency?: string;
141
102
  taxBehavior?: Stripe.Price.TaxBehavior;
142
103
  }): Promise<PlanPrices>;
@@ -157,12 +118,23 @@ export declare function planForPriceId(priceId: string): Promise<string | null>;
157
118
  * flat (non-seat-typed) price. */
158
119
  export declare function seatTypeForPriceId(priceId: string): Promise<string | null>;
159
120
  /** Seat limit for a plan (null = unlimited, undefined plan = null). */
160
- export declare function seatLimit(plans: PlansConfig, plan: string): number | null;
161
- /** Included tokens for `seatCount` members on a plan (per cycle, flat model). */
162
- export declare function includedTokens(plans: PlansConfig, plan: string, seatCount: number): number;
163
- /** Included tokens for a seat-typed plan given member counts per seat type
164
- * (per cycle): Σ seatTypes[t].includedTokens × counts[t]. Falls back to the
165
- * flat `includedTokens` (over the total member count) for plans without seat
166
- * types, so callers can use it uniformly. */
167
- export declare function includedTokensByType(plans: PlansConfig, plan: string | null, counts: Record<string, number>): number;
121
+ /** Member limit for a plan (null = unlimited, unknown plan = null). */
122
+ export declare function seatLimit(plans: PlanCatalog, plan: string): number | null;
123
+ /** Per-type seat cap, or null when that type is unlimited. This is what makes a
124
+ * `max: 1` shared API seat mean something. */
125
+ export declare function seatTypeLimit(plans: PlanCatalog, plan: string, seatType: string): number | null;
126
+ /** How a plan is sold, which decides whether any checkout path may accept it. */
127
+ export declare function planSale(plans: PlanCatalog, plan: string): Sale | null;
128
+ /**
129
+ * Tokens to CREDIT for `seatCount` members on a plan, per cycle.
130
+ *
131
+ * Now expressed over `grant`, so a plan whose allowance is an ENTITLEMENT
132
+ * (`grant: none`, the default for everything but a credit-selling plan) returns
133
+ * 0 — crediting it would discount that plan's own invoice.
134
+ */
135
+ export declare function includedTokens(plans: PlanCatalog, plan: string, seatCount: number): number;
136
+ /** Tokens to CREDIT given purchased counts per seat type. Falls back to the
137
+ * member-count form for plans without seat types, so callers can use it
138
+ * uniformly. */
139
+ export declare function includedTokensByType(plans: PlanCatalog, plan: string | null, counts: Record<string, number>): number;
168
140
  //# sourceMappingURL=plans.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,WAAW,EAIhB,KAAK,IAAI,EACT,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAczB,YAAY,EACV,eAAe,EACf,KAAK,EACL,aAAa,EACb,WAAW,EACX,YAAY,EACZ,eAAe,EACf,OAAO,EACP,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,cAAc,EACd,KAAK,EACL,KAAK,EACL,GAAG,EACH,SAAS,EACT,SAAS,EACT,IAAI,EACJ,UAAU,EACV,aAAa,EACb,WAAW,GACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,SAAS,EACT,UAAU,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,QAAQ,EACR,UAAU,EACV,UAAU,EACV,eAAe,EACf,cAAc,GACf,MAAM,iBAAiB,CAAC;AAQzB,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAI1D,CAAC;AAQF,eAAO,MAAM,YAAY,GACvB,MAAM,MAAM,EACZ,UAAU,eAAe,EACzB,WAAW,MAAM,KAChB,MAAgF,CAAC;AAEpF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AA8FD;;kFAEkF;AAClF,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,IAAI,GAAE;IACJ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;IACvC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CAC7B,GACL,OAAO,CAAC,YAAY,EAAE,CAAC,CA4FzB;AAcD,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,sDAAsD;IACtD,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACjB;AAYD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC,wBAAwB,CAAC,iBAAiB,CAAC;IACtE,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4FtC;AAoCD,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAWrD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,WAAW,EAClB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAA;CAAO,GACvE,OAAO,CAAC,UAAU,CAAC,CAkBrB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;4CAC4C;AAC5C,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBxB;AAED,2EAA2E;AAC3E,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAO5E;AAED;mCACmC;AACnC,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOhF;AAED,uEAAuE;AACvE,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEzE;AAED;+CAC+C;AAC/C,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAEf;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,MAAM,CAER;AAED;;iBAEiB;AACjB,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,MAAM,CAKR"}
package/dist/plans.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { getStripe } from "./billing.js";
2
+ import { grantFor, INTERVALS, normalizePlans, planModel, } from "./plan-model.js";
3
+ export { definePlans, isLegacyPlan, normalizePlan, normalizePlans, planModel, plansWhere, selfServePlans, defaultBasket, validateBasket, describeBasketProblem, grantFor, poolSizeOf, packSizeOf, exhaustedPolicy, cycleWindowFor, } from "./plan-model.js";
2
4
  // Library DEFAULT seat types, priced in USD (the lib's default currency — see
3
5
  // ensurePlans `opts.currency ?? "usd"`). Consumers use these as-is or override
4
6
  // (scartoffie, for instance, sets currency "eur" and its own EUR amounts).
@@ -10,7 +12,6 @@ export const DEFAULT_SEAT_TYPES = {
10
12
  premium: { label: "Premium", price: { monthly: 12500, yearly: 120000 }, includedTokens: 5000 }, // $125/mo · $100/mo annually
11
13
  api: { label: "API", price: { monthly: 62500, yearly: 600000 }, includedTokens: 25000, seats: 1 }, // ≈ 5× premium, one per workspace
12
14
  };
13
- const INTERVALS = ["monthly", "yearly"];
14
15
  const STRIPE_INTERVAL = {
15
16
  monthly: "month",
16
17
  yearly: "year",
@@ -22,21 +23,34 @@ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
22
23
  * intervals/seat types produce no Stripe object, so they're dropped here. */
23
24
  function priceSpecs(plans) {
24
25
  const specs = [];
25
- for (const [plan, def] of Object.entries(plans)) {
26
+ for (const model of normalizePlans(plans)) {
26
27
  const of = (interval, amount, seatType) => {
27
- if (amount > 0) {
28
- specs.push({ plan, interval, seatType, amount, lookupKey: lookupKeyFor(plan, interval, seatType) });
28
+ // A plan can decline an interval — an annual-only commitment declares
29
+ // `intervals: ["yearly"]` and no monthly price is ever minted for it.
30
+ if (amount > 0 && model.intervals.includes(interval)) {
31
+ specs.push({
32
+ plan: model.key,
33
+ interval,
34
+ seatType,
35
+ amount,
36
+ lookupKey: lookupKeyFor(model.key, interval, seatType),
37
+ });
29
38
  }
30
39
  };
31
- if (def.seatTypes) {
32
- for (const [seatType, st] of Object.entries(def.seatTypes)) {
40
+ switch (model.sells.kind) {
41
+ case "seats":
42
+ for (const seat of model.seatTypes) {
43
+ for (const interval of INTERVALS)
44
+ of(interval, seat.price[interval], seat.key);
45
+ }
46
+ break;
47
+ case "flat":
33
48
  for (const interval of INTERVALS)
34
- of(interval, st.price[interval], seatType);
35
- }
36
- }
37
- else {
38
- for (const interval of INTERVALS)
39
- of(interval, def.price[interval]);
49
+ of(interval, model.sells.price[interval]);
50
+ break;
51
+ case "nothing":
52
+ // Free, or a pure prepaid wallet: no Stripe object at all.
53
+ break;
40
54
  }
41
55
  }
42
56
  return specs;
@@ -140,7 +154,9 @@ export async function ensurePlans(plans, opts = {}) {
140
154
  let productId = productByPlan.get(plan);
141
155
  if (!productId) {
142
156
  const product = await stripe.products.create({
143
- name: cap(plan),
157
+ // The display name when the config has one, so the Stripe Dashboard and
158
+ // an invoice line read like the product rather than like its key.
159
+ name: planModel(plans, plan)?.display?.name ?? cap(plan),
144
160
  metadata: { managedBy: MANAGED_BY, plan },
145
161
  });
146
162
  productId = product.id;
@@ -397,32 +413,37 @@ export async function seatTypeForPriceId(priceId) {
397
413
  }
398
414
  }
399
415
  /** Seat limit for a plan (null = unlimited, undefined plan = null). */
416
+ /** Member limit for a plan (null = unlimited, unknown plan = null). */
400
417
  export function seatLimit(plans, plan) {
401
- return plans[plan]?.seats ?? null;
418
+ return planModel(plans, plan)?.limits.members ?? null;
419
+ }
420
+ /** Per-type seat cap, or null when that type is unlimited. This is what makes a
421
+ * `max: 1` shared API seat mean something. */
422
+ export function seatTypeLimit(plans, plan, seatType) {
423
+ return planModel(plans, plan)?.seatTypes.find((s) => s.key === seatType)?.max ?? null;
402
424
  }
403
- /** Included tokens for `seatCount` members on a plan (per cycle, flat model). */
425
+ /** How a plan is sold, which decides whether any checkout path may accept it. */
426
+ export function planSale(plans, plan) {
427
+ return planModel(plans, plan)?.sale ?? null;
428
+ }
429
+ /**
430
+ * Tokens to CREDIT for `seatCount` members on a plan, per cycle.
431
+ *
432
+ * Now expressed over `grant`, so a plan whose allowance is an ENTITLEMENT
433
+ * (`grant: none`, the default for everything but a credit-selling plan) returns
434
+ * 0 — crediting it would discount that plan's own invoice.
435
+ */
404
436
  export function includedTokens(plans, plan, seatCount) {
405
- const def = plans[plan];
406
- if (!def)
407
- return 0;
408
- return def.tokensPerSeat * Math.max(1, seatCount);
437
+ return grantFor(planModel(plans, plan), { memberCount: seatCount });
409
438
  }
410
- /** Included tokens for a seat-typed plan given member counts per seat type
411
- * (per cycle): Σ seatTypes[t].includedTokens × counts[t]. Falls back to the
412
- * flat `includedTokens` (over the total member count) for plans without seat
413
- * types, so callers can use it uniformly. */
439
+ /** Tokens to CREDIT given purchased counts per seat type. Falls back to the
440
+ * member-count form for plans without seat types, so callers can use it
441
+ * uniformly. */
414
442
  export function includedTokensByType(plans, plan, counts) {
415
- const def = plan ? plans[plan] : undefined;
416
- if (!def)
443
+ const model = planModel(plans, plan);
444
+ if (!model)
417
445
  return 0;
418
- if (!def.seatTypes) {
419
- const total = Object.values(counts).reduce((a, b) => a + b, 0);
420
- return includedTokens(plans, plan, total);
421
- }
422
- let sum = 0;
423
- for (const [type, st] of Object.entries(def.seatTypes)) {
424
- sum += st.includedTokens * (counts[type] ?? 0);
425
- }
426
- return sum;
446
+ const total = Object.values(counts).reduce((a, b) => a + b, 0);
447
+ return grantFor(model, { seatCounts: counts, memberCount: total });
427
448
  }
428
449
  //# sourceMappingURL=plans.js.map