@copilotz/billing 0.0.0-stage → 1.0.0-rc.35

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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -2
  3. package/contract.d.ts +7 -0
  4. package/contract.js +7 -0
  5. package/credits/actions/check.d.ts +5 -0
  6. package/credits/actions/check.js +97 -0
  7. package/credits/actions/manage.d.ts +9 -0
  8. package/credits/actions/manage.js +266 -0
  9. package/credits/actions/state.d.ts +2 -0
  10. package/credits/actions/state.js +172 -0
  11. package/credits/collections/credit.d.ts +110 -0
  12. package/credits/collections/credit.js +31 -0
  13. package/credits/collections/payment.d.ts +139 -0
  14. package/credits/collections/payment.js +32 -0
  15. package/credits/collections/plan.d.ts +428 -0
  16. package/credits/collections/plan.js +97 -0
  17. package/credits/collections/room.d.ts +85 -0
  18. package/credits/collections/room.js +24 -0
  19. package/credits/contract.d.ts +19 -0
  20. package/credits/contract.js +12 -0
  21. package/credits/mod.d.ts +38 -0
  22. package/credits/mod.js +72 -0
  23. package/credits/shared/apply.d.ts +66 -0
  24. package/credits/shared/apply.js +106 -0
  25. package/credits/shared/settings.d.ts +70 -0
  26. package/credits/shared/settings.js +28 -0
  27. package/credits/shared/standing.d.ts +71 -0
  28. package/credits/shared/standing.js +235 -0
  29. package/credits/shared/walk.d.ts +50 -0
  30. package/credits/shared/walk.js +127 -0
  31. package/credits/views/web/add.d.ts +25 -0
  32. package/credits/views/web/add.js +92 -0
  33. package/credits/views/web/budget.d.ts +16 -0
  34. package/credits/views/web/budget.js +62 -0
  35. package/credits/views/web/composer.d.ts +13 -0
  36. package/credits/views/web/composer.js +65 -0
  37. package/credits/views/web/history.d.ts +12 -0
  38. package/credits/views/web/history.js +55 -0
  39. package/credits/views/web/numbers.d.ts +256 -0
  40. package/credits/views/web/numbers.js +291 -0
  41. package/credits/views/web/org.d.ts +31 -0
  42. package/credits/views/web/org.js +238 -0
  43. package/credits/views/web/plan.d.ts +28 -0
  44. package/credits/views/web/plan.js +84 -0
  45. package/credits/views/web/staff.d.ts +20 -0
  46. package/credits/views/web/staff.js +265 -0
  47. package/credits/views/web/style.d.ts +9 -0
  48. package/credits/views/web/style.js +190 -0
  49. package/credits/views/web/types.d.ts +17 -0
  50. package/credits/views/web/types.js +32 -0
  51. package/credits/web.d.ts +12 -0
  52. package/credits/web.js +33 -0
  53. package/mod.d.ts +26 -0
  54. package/mod.js +25 -0
  55. package/package.json +61 -4
  56. package/stripe/actions/checkout.d.ts +4 -0
  57. package/stripe/actions/checkout.js +188 -0
  58. package/stripe/actions/fact.d.ts +1 -0
  59. package/stripe/actions/fact.js +39 -0
  60. package/stripe/mod.d.ts +30 -0
  61. package/stripe/mod.js +52 -0
  62. package/stripe/routes/webhook.d.ts +16 -0
  63. package/stripe/routes/webhook.js +56 -0
  64. package/stripe/shared/api.d.ts +14 -0
  65. package/stripe/shared/api.js +42 -0
  66. package/stripe/shared/events.d.ts +42 -0
  67. package/stripe/shared/events.js +226 -0
  68. package/stripe/shared/settings.d.ts +39 -0
  69. package/stripe/shared/settings.js +10 -0
  70. package/web.d.ts +13 -0
  71. package/web.js +13 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * The org's plan (one document, `_id: "plan"`): what it's on, on what terms,
3
+ * since when, and anything waived. The terms are copied here, so a contract
4
+ * is a record like any other, and changing a plan's list doesn't change what
5
+ * an org agreed to. This, not a payment provider, says what an org has.
6
+ */
7
+ import { type Infer } from "copilotz";
8
+ export declare const planSchema: {
9
+ readonly type: "object";
10
+ readonly properties: {
11
+ /** Which of the app's plans it came from, or the contract's own name. */
12
+ readonly name: {
13
+ readonly type: "string";
14
+ };
15
+ readonly terms: {
16
+ readonly type: "object";
17
+ readonly properties: {
18
+ readonly title: {
19
+ readonly type: "string";
20
+ };
21
+ /** What a seat pays each period. */
22
+ readonly seat: {
23
+ readonly type: "object";
24
+ readonly properties: {
25
+ readonly price: {
26
+ readonly type: "number";
27
+ };
28
+ readonly currency: {
29
+ readonly type: "string";
30
+ };
31
+ };
32
+ readonly required: readonly ["price", "currency"];
33
+ };
34
+ /** Credits (USD): once ever, and each period, flat and per seat. */
35
+ readonly credits: {
36
+ readonly type: "object";
37
+ readonly properties: {
38
+ readonly once: {
39
+ readonly type: "number";
40
+ };
41
+ readonly monthly: {
42
+ readonly type: "number";
43
+ };
44
+ readonly perSeat: {
45
+ readonly type: "number";
46
+ };
47
+ };
48
+ };
49
+ /** The price list that applies (`resources.priceLists`); none: the app's. */
50
+ readonly prices: {
51
+ readonly type: "string";
52
+ };
53
+ /** The month's limit it starts with, per seat; none: the app's. */
54
+ readonly budget: {
55
+ readonly type: "object";
56
+ readonly properties: {
57
+ readonly perSeat: {
58
+ readonly type: "number";
59
+ };
60
+ };
61
+ };
62
+ /** Which orgs may be on it (`personal`, `team`); none: any. */
63
+ readonly for: {
64
+ readonly type: "string";
65
+ readonly enum: readonly ["personal", "team"];
66
+ };
67
+ /** The model tier its agents use while the org hasn't chosen one. */
68
+ readonly models: {
69
+ readonly type: "string";
70
+ };
71
+ };
72
+ readonly required: readonly ["title"];
73
+ };
74
+ /** A fixed number of seats (a contract's); none: the org's people, guests apart. */
75
+ readonly seats: {
76
+ readonly type: "number";
77
+ };
78
+ /** From when its usage counts against its credits. */
79
+ readonly since: {
80
+ readonly type: "string";
81
+ };
82
+ /** Whether that clock has started: not while limits are off. */
83
+ readonly counting: {
84
+ readonly type: "boolean";
85
+ };
86
+ /** Where its periods start: the same day each month. */
87
+ readonly anchor: {
88
+ readonly type: "string";
89
+ };
90
+ /** Who collects for it (`manual`, `stripe`…), which of its accounts, and what it's called there. */
91
+ readonly channel: {
92
+ readonly type: "string";
93
+ };
94
+ readonly seller: {
95
+ readonly type: "string";
96
+ };
97
+ readonly customer: {
98
+ readonly type: "string";
99
+ };
100
+ readonly subscription: {
101
+ readonly type: "string";
102
+ };
103
+ /** How its payments stand, as its channel last said: `active`, `past_due`, `canceled`. */
104
+ readonly status: {
105
+ readonly type: "string";
106
+ };
107
+ /** When its channel said so: anything older is out of date. */
108
+ readonly statusAt: {
109
+ readonly type: "string";
110
+ };
111
+ /** Subscriptions that have ended: news of them, however late, doesn't bring them back. */
112
+ readonly ended: {
113
+ readonly type: "array";
114
+ readonly items: {
115
+ readonly type: "string";
116
+ };
117
+ };
118
+ /** Its customer and subscription are from a test mode, so going live forgets them. */
119
+ readonly sandbox: {
120
+ readonly type: "boolean";
121
+ };
122
+ /** The month's limit (USD); absent: the app's default; null: none. */
123
+ readonly budget: {
124
+ readonly type: readonly ["number", "null"];
125
+ };
126
+ /** Its usage or seats, not charged. Usage is recorded all the same. */
127
+ readonly waived: {
128
+ readonly type: "object";
129
+ readonly properties: {
130
+ readonly usage: {
131
+ readonly type: "object";
132
+ readonly properties: {
133
+ readonly from: {
134
+ readonly type: "string";
135
+ };
136
+ /** When it ends (ISO); none: until lifted. */
137
+ readonly until: {
138
+ readonly type: "string";
139
+ };
140
+ readonly reason: {
141
+ readonly type: "string";
142
+ };
143
+ readonly by: {
144
+ readonly type: "string";
145
+ };
146
+ };
147
+ readonly required: readonly ["from", "reason"];
148
+ };
149
+ readonly seats: {
150
+ readonly type: "object";
151
+ readonly properties: {
152
+ /** Whose; none: every seat. */
153
+ readonly people: {
154
+ readonly type: "array";
155
+ readonly items: {
156
+ readonly type: "string";
157
+ };
158
+ };
159
+ readonly from: {
160
+ readonly type: "string";
161
+ };
162
+ /** When it ends (ISO); none: until lifted. */
163
+ readonly until: {
164
+ readonly type: "string";
165
+ };
166
+ readonly reason: {
167
+ readonly type: "string";
168
+ };
169
+ readonly by: {
170
+ readonly type: "string";
171
+ };
172
+ };
173
+ readonly required: readonly ["from", "reason"];
174
+ };
175
+ };
176
+ };
177
+ readonly at: {
178
+ readonly type: "string";
179
+ };
180
+ };
181
+ readonly required: readonly ["name", "terms", "since", "anchor", "at"];
182
+ };
183
+ export type Plan = Infer<typeof planSchema> & {
184
+ _id: string;
185
+ };
186
+ export declare const billingPlan: {
187
+ schema: {
188
+ readonly type: "object";
189
+ readonly properties: {
190
+ /** Which of the app's plans it came from, or the contract's own name. */
191
+ readonly name: {
192
+ readonly type: "string";
193
+ };
194
+ readonly terms: {
195
+ readonly type: "object";
196
+ readonly properties: {
197
+ readonly title: {
198
+ readonly type: "string";
199
+ };
200
+ /** What a seat pays each period. */
201
+ readonly seat: {
202
+ readonly type: "object";
203
+ readonly properties: {
204
+ readonly price: {
205
+ readonly type: "number";
206
+ };
207
+ readonly currency: {
208
+ readonly type: "string";
209
+ };
210
+ };
211
+ readonly required: readonly ["price", "currency"];
212
+ };
213
+ /** Credits (USD): once ever, and each period, flat and per seat. */
214
+ readonly credits: {
215
+ readonly type: "object";
216
+ readonly properties: {
217
+ readonly once: {
218
+ readonly type: "number";
219
+ };
220
+ readonly monthly: {
221
+ readonly type: "number";
222
+ };
223
+ readonly perSeat: {
224
+ readonly type: "number";
225
+ };
226
+ };
227
+ };
228
+ /** The price list that applies (`resources.priceLists`); none: the app's. */
229
+ readonly prices: {
230
+ readonly type: "string";
231
+ };
232
+ /** The month's limit it starts with, per seat; none: the app's. */
233
+ readonly budget: {
234
+ readonly type: "object";
235
+ readonly properties: {
236
+ readonly perSeat: {
237
+ readonly type: "number";
238
+ };
239
+ };
240
+ };
241
+ /** Which orgs may be on it (`personal`, `team`); none: any. */
242
+ readonly for: {
243
+ readonly type: "string";
244
+ readonly enum: readonly ["personal", "team"];
245
+ };
246
+ /** The model tier its agents use while the org hasn't chosen one. */
247
+ readonly models: {
248
+ readonly type: "string";
249
+ };
250
+ };
251
+ readonly required: readonly ["title"];
252
+ };
253
+ /** A fixed number of seats (a contract's); none: the org's people, guests apart. */
254
+ readonly seats: {
255
+ readonly type: "number";
256
+ };
257
+ /** From when its usage counts against its credits. */
258
+ readonly since: {
259
+ readonly type: "string";
260
+ };
261
+ /** Whether that clock has started: not while limits are off. */
262
+ readonly counting: {
263
+ readonly type: "boolean";
264
+ };
265
+ /** Where its periods start: the same day each month. */
266
+ readonly anchor: {
267
+ readonly type: "string";
268
+ };
269
+ /** Who collects for it (`manual`, `stripe`…), which of its accounts, and what it's called there. */
270
+ readonly channel: {
271
+ readonly type: "string";
272
+ };
273
+ readonly seller: {
274
+ readonly type: "string";
275
+ };
276
+ readonly customer: {
277
+ readonly type: "string";
278
+ };
279
+ readonly subscription: {
280
+ readonly type: "string";
281
+ };
282
+ /** How its payments stand, as its channel last said: `active`, `past_due`, `canceled`. */
283
+ readonly status: {
284
+ readonly type: "string";
285
+ };
286
+ /** When its channel said so: anything older is out of date. */
287
+ readonly statusAt: {
288
+ readonly type: "string";
289
+ };
290
+ /** Subscriptions that have ended: news of them, however late, doesn't bring them back. */
291
+ readonly ended: {
292
+ readonly type: "array";
293
+ readonly items: {
294
+ readonly type: "string";
295
+ };
296
+ };
297
+ /** Its customer and subscription are from a test mode, so going live forgets them. */
298
+ readonly sandbox: {
299
+ readonly type: "boolean";
300
+ };
301
+ /** The month's limit (USD); absent: the app's default; null: none. */
302
+ readonly budget: {
303
+ readonly type: readonly ["number", "null"];
304
+ };
305
+ /** Its usage or seats, not charged. Usage is recorded all the same. */
306
+ readonly waived: {
307
+ readonly type: "object";
308
+ readonly properties: {
309
+ readonly usage: {
310
+ readonly type: "object";
311
+ readonly properties: {
312
+ readonly from: {
313
+ readonly type: "string";
314
+ };
315
+ /** When it ends (ISO); none: until lifted. */
316
+ readonly until: {
317
+ readonly type: "string";
318
+ };
319
+ readonly reason: {
320
+ readonly type: "string";
321
+ };
322
+ readonly by: {
323
+ readonly type: "string";
324
+ };
325
+ };
326
+ readonly required: readonly ["from", "reason"];
327
+ };
328
+ readonly seats: {
329
+ readonly type: "object";
330
+ readonly properties: {
331
+ /** Whose; none: every seat. */
332
+ readonly people: {
333
+ readonly type: "array";
334
+ readonly items: {
335
+ readonly type: "string";
336
+ };
337
+ };
338
+ readonly from: {
339
+ readonly type: "string";
340
+ };
341
+ /** When it ends (ISO); none: until lifted. */
342
+ readonly until: {
343
+ readonly type: "string";
344
+ };
345
+ readonly reason: {
346
+ readonly type: "string";
347
+ };
348
+ readonly by: {
349
+ readonly type: "string";
350
+ };
351
+ };
352
+ readonly required: readonly ["from", "reason"];
353
+ };
354
+ };
355
+ };
356
+ readonly at: {
357
+ readonly type: "string";
358
+ };
359
+ };
360
+ readonly required: readonly ["name", "terms", "since", "anchor", "at"];
361
+ };
362
+ events?: boolean;
363
+ records?: boolean;
364
+ durable?: false;
365
+ indexes?: string[][];
366
+ private?: string[];
367
+ queries?: Record<string, import("copilotz").QueryDef<{
368
+ [x: string]: unknown;
369
+ budget?: number | null | undefined;
370
+ waived?: {
371
+ [x: string]: unknown;
372
+ seats?: {
373
+ [x: string]: unknown;
374
+ by?: string | undefined;
375
+ until?: string | undefined;
376
+ people?: string[] | undefined;
377
+ reason: string;
378
+ from: string;
379
+ } | undefined;
380
+ usage?: {
381
+ [x: string]: unknown;
382
+ by?: string | undefined;
383
+ until?: string | undefined;
384
+ reason: string;
385
+ from: string;
386
+ } | undefined;
387
+ } | undefined;
388
+ sandbox?: boolean | undefined;
389
+ seats?: number | undefined;
390
+ counting?: boolean | undefined;
391
+ channel?: string | undefined;
392
+ seller?: string | undefined;
393
+ customer?: string | undefined;
394
+ subscription?: string | undefined;
395
+ status?: string | undefined;
396
+ statusAt?: string | undefined;
397
+ ended?: string[] | undefined;
398
+ at: string;
399
+ anchor: string;
400
+ name: string;
401
+ terms: {
402
+ [x: string]: unknown;
403
+ for?: "personal" | "team" | undefined;
404
+ seat?: {
405
+ [x: string]: unknown;
406
+ price: number;
407
+ currency: string;
408
+ } | undefined;
409
+ credits?: {
410
+ [x: string]: unknown;
411
+ once?: number | undefined;
412
+ monthly?: number | undefined;
413
+ perSeat?: number | undefined;
414
+ } | undefined;
415
+ prices?: string | undefined;
416
+ budget?: {
417
+ [x: string]: unknown;
418
+ perSeat?: number | undefined;
419
+ } | undefined;
420
+ models?: string | undefined;
421
+ title: string;
422
+ };
423
+ since: string;
424
+ } & {
425
+ _id: string;
426
+ }>> | undefined;
427
+ vectors?: string[];
428
+ };
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The org's plan (one document, `_id: "plan"`): what it's on, on what terms,
3
+ * since when, and anything waived. The terms are copied here, so a contract
4
+ * is a record like any other, and changing a plan's list doesn't change what
5
+ * an org agreed to. This, not a payment provider, says what an org has.
6
+ */
7
+ import { defineCollection } from "copilotz";
8
+ const waiver = {
9
+ type: "object",
10
+ properties: {
11
+ from: { type: "string" },
12
+ /** When it ends (ISO); none: until lifted. */
13
+ until: { type: "string" },
14
+ reason: { type: "string" },
15
+ by: { type: "string" },
16
+ },
17
+ required: ["from", "reason"],
18
+ };
19
+ export const planSchema = {
20
+ type: "object",
21
+ properties: {
22
+ /** Which of the app's plans it came from, or the contract's own name. */
23
+ name: { type: "string" },
24
+ terms: {
25
+ type: "object",
26
+ properties: {
27
+ title: { type: "string" },
28
+ /** What a seat pays each period. */
29
+ seat: {
30
+ type: "object",
31
+ properties: { price: { type: "number" }, currency: { type: "string" } },
32
+ required: ["price", "currency"],
33
+ },
34
+ /** Credits (USD): once ever, and each period, flat and per seat. */
35
+ credits: {
36
+ type: "object",
37
+ properties: {
38
+ once: { type: "number" },
39
+ monthly: { type: "number" },
40
+ perSeat: { type: "number" },
41
+ },
42
+ },
43
+ /** The price list that applies (`resources.priceLists`); none: the app's. */
44
+ prices: { type: "string" },
45
+ /** The month's limit it starts with, per seat; none: the app's. */
46
+ budget: { type: "object", properties: { perSeat: { type: "number" } } },
47
+ /** Which orgs may be on it (`personal`, `team`); none: any. */
48
+ for: { type: "string", enum: ["personal", "team"] },
49
+ /** The model tier its agents use while the org hasn't chosen one. */
50
+ models: { type: "string" },
51
+ },
52
+ required: ["title"],
53
+ },
54
+ /** A fixed number of seats (a contract's); none: the org's people, guests apart. */
55
+ seats: { type: "number" },
56
+ /** From when its usage counts against its credits. */
57
+ since: { type: "string" },
58
+ /** Whether that clock has started: not while limits are off. */
59
+ counting: { type: "boolean" },
60
+ /** Where its periods start: the same day each month. */
61
+ anchor: { type: "string" },
62
+ /** Who collects for it (`manual`, `stripe`…), which of its accounts, and what it's called there. */
63
+ channel: { type: "string" },
64
+ seller: { type: "string" },
65
+ customer: { type: "string" },
66
+ subscription: { type: "string" },
67
+ /** How its payments stand, as its channel last said: `active`, `past_due`, `canceled`. */
68
+ status: { type: "string" },
69
+ /** When its channel said so: anything older is out of date. */
70
+ statusAt: { type: "string" },
71
+ /** Subscriptions that have ended: news of them, however late, doesn't bring them back. */
72
+ ended: { type: "array", items: { type: "string" } },
73
+ /** Its customer and subscription are from a test mode, so going live forgets them. */
74
+ sandbox: { type: "boolean" },
75
+ /** The month's limit (USD); absent: the app's default; null: none. */
76
+ budget: { type: ["number", "null"] },
77
+ /** Its usage or seats, not charged. Usage is recorded all the same. */
78
+ waived: {
79
+ type: "object",
80
+ properties: {
81
+ usage: waiver,
82
+ seats: {
83
+ type: "object",
84
+ properties: {
85
+ ...waiver.properties,
86
+ /** Whose; none: every seat. */
87
+ people: { type: "array", items: { type: "string" } },
88
+ },
89
+ required: ["from", "reason"],
90
+ },
91
+ },
92
+ },
93
+ at: { type: "string" },
94
+ },
95
+ required: ["name", "terms", "since", "anchor", "at"],
96
+ };
97
+ export const billingPlan = defineCollection({ schema: planSchema });
@@ -0,0 +1,85 @@
1
+ /**
2
+ * How much an org can still spend (`_id: "room"`): the answer to "may it?"
3
+ * without reading the ledger. Kept in the owner's memory, so every executor
4
+ * sees the same one and writing it commits nothing; a new owner starts
5
+ * without it and works it out again. Each use takes its price off it.
6
+ */
7
+ import { type Infer } from "copilotz";
8
+ export declare const roomSchema: {
9
+ readonly type: "object";
10
+ readonly properties: {
11
+ /** Credits left, and the month's budget left (USD); `NO_LIMIT` for none. */
12
+ readonly credits: {
13
+ readonly type: "number";
14
+ };
15
+ readonly budget: {
16
+ readonly type: "number";
17
+ };
18
+ /** The budget itself (USD), to say so when it's reached. */
19
+ readonly limit: {
20
+ readonly type: "number";
21
+ };
22
+ /** Below these it's running low. */
23
+ readonly lowCredits: {
24
+ readonly type: "number";
25
+ };
26
+ readonly lowBudget: {
27
+ readonly type: "number";
28
+ };
29
+ /** Worked out again from then on: when a date changes what it should say. */
30
+ readonly until: {
31
+ readonly type: "string";
32
+ };
33
+ };
34
+ readonly required: readonly ["credits", "budget", "until"];
35
+ };
36
+ export type Room = Infer<typeof roomSchema> & {
37
+ _id: string;
38
+ };
39
+ export declare const billingRoom: {
40
+ schema: {
41
+ readonly type: "object";
42
+ readonly properties: {
43
+ /** Credits left, and the month's budget left (USD); `NO_LIMIT` for none. */
44
+ readonly credits: {
45
+ readonly type: "number";
46
+ };
47
+ readonly budget: {
48
+ readonly type: "number";
49
+ };
50
+ /** The budget itself (USD), to say so when it's reached. */
51
+ readonly limit: {
52
+ readonly type: "number";
53
+ };
54
+ /** Below these it's running low. */
55
+ readonly lowCredits: {
56
+ readonly type: "number";
57
+ };
58
+ readonly lowBudget: {
59
+ readonly type: "number";
60
+ };
61
+ /** Worked out again from then on: when a date changes what it should say. */
62
+ readonly until: {
63
+ readonly type: "string";
64
+ };
65
+ };
66
+ readonly required: readonly ["credits", "budget", "until"];
67
+ };
68
+ events?: boolean;
69
+ records?: boolean;
70
+ durable?: false;
71
+ indexes?: string[][];
72
+ private?: string[];
73
+ queries?: Record<string, import("copilotz").QueryDef<{
74
+ [x: string]: unknown;
75
+ limit?: number | undefined;
76
+ lowCredits?: number | undefined;
77
+ lowBudget?: number | undefined;
78
+ credits: number;
79
+ budget: number;
80
+ until: string;
81
+ } & {
82
+ _id: string;
83
+ }>> | undefined;
84
+ vectors?: string[];
85
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * How much an org can still spend (`_id: "room"`): the answer to "may it?"
3
+ * without reading the ledger. Kept in the owner's memory, so every executor
4
+ * sees the same one and writing it commits nothing; a new owner starts
5
+ * without it and works it out again. Each use takes its price off it.
6
+ */
7
+ import { defineCollection } from "copilotz";
8
+ export const roomSchema = {
9
+ type: "object",
10
+ properties: {
11
+ /** Credits left, and the month's budget left (USD); `NO_LIMIT` for none. */
12
+ credits: { type: "number" },
13
+ budget: { type: "number" },
14
+ /** The budget itself (USD), to say so when it's reached. */
15
+ limit: { type: "number" },
16
+ /** Below these it's running low. */
17
+ lowCredits: { type: "number" },
18
+ lowBudget: { type: "number" },
19
+ /** Worked out again from then on: when a date changes what it should say. */
20
+ until: { type: "string" },
21
+ },
22
+ required: ["credits", "budget", "until"],
23
+ };
24
+ export const billingRoom = defineCollection({ durable: false, schema: roomSchema });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The credits plugin's contract: what a way of paying may import, the shapes of
3
+ * what it tells `billing.apply`, and whether an org's kind fits a plan
4
+ * (`unfit`, `orgKind`). The rest it reaches by name: `billing.plan` (the org's
5
+ * plan, started if need be) and `billing.apply` (a payment, or a plan
6
+ * changed), and its own Actions through `resources.billing.pay`. What model
7
+ * tier the org's plan gives its agents, others ask through the `planTier`
8
+ * resource it fills (`billing.tier`).
9
+ */
10
+ import type { Plan } from "./collections/plan.js";
11
+ export type { PaymentFact, PeriodFact, PlanFact } from "./shared/apply.js";
12
+ export type { Plan } from "./collections/plan.js";
13
+ export type { PlanTerms } from "./shared/settings.js";
14
+ export { orgKind, unfit } from "./shared/settings.js";
15
+ /**
16
+ * How many seats are charged for: its people, or its fixed number, less those
17
+ * waived while that lasts. A waiver naming nobody waives every seat.
18
+ */
19
+ export declare function billableSeats(plan: Pick<Plan, "seats" | "waived">, people: number, now: string): number;
@@ -0,0 +1,12 @@
1
+ export { orgKind, unfit } from "./shared/settings.js";
2
+ /**
3
+ * How many seats are charged for: its people, or its fixed number, less those
4
+ * waived while that lasts. A waiver naming nobody waives every seat.
5
+ */
6
+ export function billableSeats(plan, people, now) {
7
+ const total = plan.seats ?? people;
8
+ const waived = plan.waived?.seats;
9
+ if (!waived || (waived.until && waived.until <= now))
10
+ return total;
11
+ return waived.people?.length ? Math.max(total - waived.people.length, 0) : 0;
12
+ }