@happyvertical/smrt-commerce 0.51.25 → 0.51.27
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/AGENTS.md +17 -1
- package/README.md +72 -3
- package/dist/billing/cycles.d.ts +31 -0
- package/dist/billing/cycles.d.ts.map +1 -0
- package/dist/billing/index.d.ts +1 -0
- package/dist/billing/index.d.ts.map +1 -1
- package/dist/billing/jobs.d.ts +4 -2
- package/dist/billing/jobs.d.ts.map +1 -1
- package/dist/billing/period-close.d.ts +18 -2
- package/dist/billing/period-close.d.ts.map +1 -1
- package/dist/billing/runtime.d.ts +22 -1
- package/dist/billing/runtime.d.ts.map +1 -1
- package/dist/billing/units.d.ts +2 -0
- package/dist/billing/units.d.ts.map +1 -1
- package/dist/chunks/{units--tTn2ps8.js → units-Bk_o7kRV.js} +5 -2
- package/dist/chunks/{units--tTn2ps8.js.map → units-Bk_o7kRV.js.map} +1 -1
- package/dist/index.js +419 -69
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +83 -9
- package/dist/models/billing.d.ts +34 -5
- package/dist/models/billing.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +25 -5
- package/package.json +9 -9
package/AGENTS.md
CHANGED
|
@@ -42,6 +42,20 @@ for usage.
|
|
|
42
42
|
close that nets to a credit keeps its claims and ends `carried_forward`;
|
|
43
43
|
its subtotal is a `credit_carry_forward` source for the payer's next billed
|
|
44
44
|
close, which marks the carried close `completed`.
|
|
45
|
+
- **Cycles and flat-plan coverage (#3116).** `cycles.ts` is the schedule
|
|
46
|
+
(pure; `BillingAccount.billingAnchorAt`, null = calendar months). A close
|
|
47
|
+
with no period closes each payer's `lastEndedBillingPeriod()`. Flat-plan
|
|
48
|
+
claims are the exception to "id from the source": their `sourceId` is
|
|
49
|
+
`<subscriptionId>:seq:<chainSequence>` and their `periodStart/End` is the
|
|
50
|
+
exact time they bill. `claimFlatWindow()` reads the last number, then the
|
|
51
|
+
coverage (claims ending after the window start), claims one gap as number
|
|
52
|
+
n + 1, and retries on conflict; reading in that order is what makes it
|
|
53
|
+
safe. That numbering, not the period, stops overlaps when schedules or
|
|
54
|
+
payers change — never claim a flat plan outside it, and keep both reads
|
|
55
|
+
bounded (no whole-history scans). Each numbered claim is its own invoice
|
|
56
|
+
line keyed `lineKey|periodStart` (a function of the claim alone, so a
|
|
57
|
+
resumed close rebuilds the same line ids). Proration is
|
|
58
|
+
`prorateMinorUnits()` (half up, BigInt) against the period's full cycle.
|
|
45
59
|
- **System tables.** `BillingAccount`, `BillingPeriodClose`, and
|
|
46
60
|
`BillingLineSource` are not tenant-scoped and have no generated surface.
|
|
47
61
|
Collections stay unexported; models are root exports (#3082). Invoices,
|
|
@@ -67,7 +81,9 @@ for usage.
|
|
|
67
81
|
two-decimal only (sdk#1269); `autoTopUp` cannot charge a saved card
|
|
68
82
|
(sdk#1270); Stripe line discounts and uncollectible status are worked around
|
|
69
83
|
as negative lines and event types (sdk#1271). `RetailCharge` has no
|
|
70
|
-
adjustment ledger. Flat plans are monthly, in arrears
|
|
84
|
+
adjustment ledger. Flat plans are monthly, in arrears; plan changes are not
|
|
85
|
+
prorated (#3119). The Stripe adapter passes line service periods but
|
|
86
|
+
the accounting SDK does not send them to Stripe yet (happyvertical/sdk#1274).
|
|
71
87
|
|
|
72
88
|
## Cross-Package References
|
|
73
89
|
|
package/README.md
CHANGED
|
@@ -187,7 +187,16 @@ await billing.upsertAccount({
|
|
|
187
187
|
billingAddress: { country: 'CA', postalCode: 'T0L 0A0' },
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
//
|
|
190
|
+
// Optional: bill this payer from its activation date instead of calendar
|
|
191
|
+
// months, prorating flat plans added mid-period (see "Billing cycles").
|
|
192
|
+
await billing.upsertAccount({
|
|
193
|
+
payerTenantId,
|
|
194
|
+
name: 'Network Co',
|
|
195
|
+
billingAnchorAt: activatedAt,
|
|
196
|
+
prorateFlatPlans: true,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Close each payer's last ended period — safe to run on every schedule tick.
|
|
191
200
|
registerBillingRuntime('platform', billing);
|
|
192
201
|
await enqueueBillingPeriodClose({ runtime: 'platform' });
|
|
193
202
|
|
|
@@ -216,15 +225,75 @@ const { url } = await billing.createCreditCheckout({
|
|
|
216
225
|
it owns, to each subscriber's billing owner. A `reseller` runtime bills the
|
|
217
226
|
`RetailCharge`s its children owe it plus its own flat plans. Charges approved
|
|
218
227
|
before the period end are billed once, whatever period that is; flat plans
|
|
219
|
-
are billed in arrears for
|
|
228
|
+
are billed in arrears for the payer's closed period (trials and
|
|
220
229
|
provider-managed subscriptions are skipped). One invoice per payer and
|
|
221
230
|
currency carries both kinds of lines.
|
|
222
231
|
- **Replay safety.** Close, invoice, and line ids are derived from the payer,
|
|
223
232
|
currency, and period; each charge is claimed by exactly one close; the
|
|
224
233
|
provider invoice uses the close id as its idempotency key; and every step is
|
|
225
234
|
persisted before the next, so a retry resumes where it stopped. A lease stops
|
|
226
|
-
two workers advancing the same close. A
|
|
235
|
+
two workers advancing the same close. A close without a period also resumes
|
|
236
|
+
every unfinished close whose period has ended, even one the payer's current
|
|
237
|
+
schedule no longer produces (#3116). A payer whose charges net to a credit
|
|
227
238
|
gets no invoice; the credit is carried to its next invoice.
|
|
239
|
+
- **Billing cycles (#3116).** A payer's periods follow its account's
|
|
240
|
+
`billingAnchorAt`. Unset (the default) they are UTC calendar months. Set,
|
|
241
|
+
they run monthly from the anchor at its UTC time of day; a day the month
|
|
242
|
+
lacks is clamped to its last day, always from the anchor itself (an anchor
|
|
243
|
+
on Jan 31 bills Jan 31 → Feb 28 → Mar 31). Before the anchor the schedule is
|
|
244
|
+
calendar months, and the anchor's own month ends at the anchor (a *stub*).
|
|
245
|
+
`closePeriod()` / `enqueueBillingPeriodClose()` with no period close each
|
|
246
|
+
payer's **last ended period on its own schedule**, so payers on different
|
|
247
|
+
anchors are closed by the same daily tick. An explicit
|
|
248
|
+
`{ periodStart, periodEnd }` closes that period for calendar payers (flat
|
|
249
|
+
plans only when it is a calendar month, as before) and for anchored payers
|
|
250
|
+
whose schedule has exactly that period. `billingPeriodFor(payer, at)` returns
|
|
251
|
+
a payer's current period; `billingPeriodContaining()`,
|
|
252
|
+
`lastEndedBillingPeriod()`, and `prorateMinorUnits()` are exported.
|
|
253
|
+
- **Proration.** A flat plan is priced `price × billed time / cycle length`,
|
|
254
|
+
in integer minor units rounded half up (exact integer arithmetic; the
|
|
255
|
+
cycle is the period's own month, so February prorates over 28 or 29 days).
|
|
256
|
+
With `prorateFlatPlans` off (default) the billed time is the whole period
|
|
257
|
+
whenever the subscription was active in it and out of trial at its start —
|
|
258
|
+
the pre-#3116 behavior. With it on, the billed time is exactly the time the
|
|
259
|
+
subscription was active and out of trial: a subscription starting, leaving
|
|
260
|
+
trial, or canceled mid-period is billed pro rata. Proration happens before
|
|
261
|
+
the account's flat discount. Split into parts, prorated amounts can differ
|
|
262
|
+
from the full price by a minor unit.
|
|
263
|
+
- **First period.** *Anchor at signup* (set `billingAnchorAt` to the
|
|
264
|
+
signup/activation instant): the first period is a full period from signup;
|
|
265
|
+
nothing before it is billed. *Bill from signup to the next anchor* (keep a
|
|
266
|
+
fixed anchor, or calendar months, and set `prorateFlatPlans`): the first
|
|
267
|
+
period is billed pro rata from signup. Items added later are prorated to the
|
|
268
|
+
payer's anchor when `prorateFlatPlans` is on and billed a full period
|
|
269
|
+
otherwise.
|
|
270
|
+
- **Mid-period changes.** Billing is in arrears, so a cancellation needs no
|
|
271
|
+
credit: with proration the canceled period is billed up to `canceledAt`,
|
|
272
|
+
without it in full. A plan change is billed at the plan in effect at close
|
|
273
|
+
for the whole window (no plan-change proration yet, #3119), and a cancellation
|
|
274
|
+
back-dated into an already billed period is not credited — issue an
|
|
275
|
+
adjustment.
|
|
276
|
+
- **No double billing across schedule changes.** Every flat-plan claim records
|
|
277
|
+
the exact time it bills, and a subscription's claims are numbered, so only
|
|
278
|
+
one worker can write the next claim and two can never claim the same time.
|
|
279
|
+
Moving an account from calendar months to an anchor, moving an anchor, or
|
|
280
|
+
moving a subscription to a payer on another schedule bills only time not yet
|
|
281
|
+
billed, prorated. For example, calendar months through February and then an
|
|
282
|
+
anchor on Mar 20 bills the Mar 1 → Mar 20 stub pro rata, then Mar 20 → Apr
|
|
283
|
+
20. Time around an already billed window is billed as separate lines, each
|
|
284
|
+
with its own service period. Stop workers running a pre-#3116 version
|
|
285
|
+
before relying on this: they claim flat plans outside the numbering.
|
|
286
|
+
- **Service periods.** Each flat-plan invoice line carries its billed window
|
|
287
|
+
as `periodStart`/`periodEnd`, down to the provider port. The Stripe adapter
|
|
288
|
+
passes them on; `@happyvertical/accounting` does not send them to Stripe yet
|
|
289
|
+
(happyvertical/sdk#1274).
|
|
290
|
+
- **Upgrading to #3116.** Additive schema only: `_smrt_billing_accounts` gains
|
|
291
|
+
`billing_anchor_at` (nullable) and `prorate_flat_plans` (default false), and
|
|
292
|
+
`_smrt_billing_line_sources` gains `chain_sequence` (default 0) and an index
|
|
293
|
+
on `(line_key, chain_sequence)`; run `smrt db:migrate`.
|
|
294
|
+
Existing accounts stay on calendar months, and claims written before the
|
|
295
|
+
upgrade (`<subscriptionId>:<periodStart>`, number 0) count as billed
|
|
296
|
+
coverage, so nothing is billed again after it.
|
|
228
297
|
- **Tax** comes from the provider (Stripe Tax) using the account customer's
|
|
229
298
|
`defaultBillingAddress`; the invoice records it as `providerTaxAmount`.
|
|
230
299
|
- **Events** are verified, stored in smrt-jobs' durable delivery inbox, and
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { BillingPeriod } from './period-close.js';
|
|
2
|
+
/** A period of a payer's schedule and the full cycle it prorates against. */
|
|
3
|
+
export interface ScheduledBillingPeriod extends BillingPeriod {
|
|
4
|
+
/** Start of the full monthly cycle (equals `periodStart` except for a stub). */
|
|
5
|
+
cycleStart: Date;
|
|
6
|
+
/** End of the full monthly cycle (equals `periodEnd` except for a stub). */
|
|
7
|
+
cycleEnd: Date;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* `anchor` plus `months` calendar months at the anchor's UTC time of day,
|
|
11
|
+
* with the day clamped to the target month's last day.
|
|
12
|
+
*/
|
|
13
|
+
export declare function addBillingMonths(anchor: Date, months: number): Date;
|
|
14
|
+
/**
|
|
15
|
+
* The period of a schedule (calendar months when `anchor` is null) that
|
|
16
|
+
* contains `at`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function billingPeriodContaining(anchor: Date | null | undefined, at: Date): ScheduledBillingPeriod;
|
|
19
|
+
/**
|
|
20
|
+
* The most recent period of a schedule that has ended by `now` — what a
|
|
21
|
+
* period close with no explicit period bills. With no anchor this is the
|
|
22
|
+
* previous calendar month.
|
|
23
|
+
*/
|
|
24
|
+
export declare function lastEndedBillingPeriod(anchor: Date | null | undefined, now?: Date): ScheduledBillingPeriod;
|
|
25
|
+
/**
|
|
26
|
+
* `amount × coveredMs / cycleMs` in integer minor units, rounded half up
|
|
27
|
+
* (exact integer arithmetic, no floating point). Coverage of the whole cycle
|
|
28
|
+
* or more returns `amount` unchanged; no coverage returns 0.
|
|
29
|
+
*/
|
|
30
|
+
export declare function prorateMinorUnits(amount: number, coveredMs: number, cycleMs: number): number;
|
|
31
|
+
//# sourceMappingURL=cycles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycles.d.ts","sourceRoot":"","sources":["../../src/billing/cycles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,6EAA6E;AAC7E,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,gFAAgF;IAChF,UAAU,EAAE,IAAI,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,IAAI,CAAC;CAChB;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgBnE;AAyBD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAC/B,EAAE,EAAE,IAAI,GACP,sBAAsB,CAgCxB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAC/B,GAAG,GAAE,IAAiB,GACrB,sBAAsB,CAMxB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAWR"}
|
package/dist/billing/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
export type { CreateCreditCheckoutInput } from './credits.js';
|
|
7
|
+
export { addBillingMonths, billingPeriodContaining, lastEndedBillingPeriod, prorateMinorUnits, type ScheduledBillingPeriod, } from './cycles.js';
|
|
7
8
|
export { type EnqueueBillingJobOptions, enqueueBillingEvents, enqueueBillingPeriodClose, getBillingRuntime, registerBillingRuntime, unregisterBillingRuntime, } from './jobs.js';
|
|
8
9
|
export { type BillingPeriod, BillingPeriodCloseError, type ClosePeriodInput, type PeriodCloseGroupResult, type PeriodCloseOutcome, type PeriodCloseResult, previousCalendarMonth, } from './period-close.js';
|
|
9
10
|
export { type BillingInvoiceEventType, type BillingProvider, type BillingProviderCheckoutInput, type BillingProviderCheckoutSession, type BillingProviderCustomerInput, type BillingProviderEvent, type BillingProviderInvoiceInput, type BillingProviderInvoiceLine, type BillingProviderInvoiceState, type BillingProviderInvoiceStatus, type BillingProviderSubscriptionState, BillingWebhookVerificationError, } from './provider.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,KAAK,wBAAwB,EAC7B,oBAAoB,EACpB,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,gCAAgC,EACrC,+BAA+B,GAChC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,GAC/B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,2BAA2B,EAC3B,KAAK,4BAA4B,GAClC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,wBAAwB,EAC7B,oBAAoB,EACpB,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,gCAAgC,EACrC,+BAA+B,GAChC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,qBAAqB,EAC1B,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,GAC/B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,2BAA2B,EAC3B,KAAK,4BAA4B,GAClC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|
package/dist/billing/jobs.d.ts
CHANGED
|
@@ -17,8 +17,10 @@ export interface EnqueueBillingJobOptions {
|
|
|
17
17
|
maxAttempts?: number;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Queue a period close. Without a period the job closes
|
|
21
|
-
*
|
|
20
|
+
* Queue a period close. Without a period the job closes each payer's last
|
|
21
|
+
* ended period on its own schedule when it runs (the previous calendar month,
|
|
22
|
+
* or the last anchor-to-anchor period for an anchored account, #3116), so
|
|
23
|
+
* enqueueing it daily is safe.
|
|
22
24
|
*/
|
|
23
25
|
export declare function enqueueBillingPeriodClose(options: EnqueueBillingJobOptions & {
|
|
24
26
|
periodStart?: Date;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/billing/jobs.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,OAAO,EAAqB,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAmBnD,6DAA6D;AAC7D,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,GACtB,IAAI,CAGN;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAM9D;AAkBD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAK5B;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAGhC;AAED,MAAM,WAAW,wBAAwB;IACvC,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA4BD
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/billing/jobs.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,OAAO,EAAqB,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAmBnD,6DAA6D;AAC7D,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,cAAc,GACtB,IAAI,CAGN;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAM9D;AAkBD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAK5B;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAGhC;AAED,MAAM,WAAW,wBAAwB;IACvC,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA4BD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,wBAAwB,GAAG;IAAE,WAAW,CAAC,EAAE,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,IAAI,CAAA;CAAE,GAC3E,OAAO,CAAC,OAAO,CAAC,CAelB;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,wBAAwB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,OAAO,CAAC,OAAO,CAAC,CAclB"}
|
|
@@ -5,6 +5,14 @@ export interface BillingPeriod {
|
|
|
5
5
|
/** Exclusive end; charges approved before it are billable. */
|
|
6
6
|
periodEnd: Date;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Without a period, each payer's most recent ended period on its own schedule
|
|
10
|
+
* is closed (#3116): the previous calendar month for a payer without a
|
|
11
|
+
* billing anchor, the last anchor-to-anchor period for one with. With a
|
|
12
|
+
* period, that exact period is closed for every calendar-schedule payer (flat
|
|
13
|
+
* plans only when it is a calendar month) and for each anchored payer whose
|
|
14
|
+
* schedule contains exactly that period.
|
|
15
|
+
*/
|
|
8
16
|
export interface ClosePeriodInput {
|
|
9
17
|
periodStart?: Date;
|
|
10
18
|
periodEnd?: Date;
|
|
@@ -15,12 +23,19 @@ export type PeriodCloseOutcome = 'completed' | 'empty' | 'busy' | 'carried_forwa
|
|
|
15
23
|
export interface PeriodCloseGroupResult {
|
|
16
24
|
payerTenantId: string;
|
|
17
25
|
currency: string;
|
|
26
|
+
/** The payer's period this group closed (#3116). */
|
|
27
|
+
periodStart: Date;
|
|
28
|
+
periodEnd: Date;
|
|
18
29
|
closeId?: string;
|
|
19
30
|
invoiceId?: string;
|
|
20
31
|
providerInvoiceId?: string;
|
|
21
32
|
outcome: PeriodCloseOutcome;
|
|
22
33
|
error?: string;
|
|
23
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* `periodStart`/`periodEnd` are the explicit period, or the previous calendar
|
|
37
|
+
* month for a close without one; each group carries the payer's own period.
|
|
38
|
+
*/
|
|
24
39
|
export interface PeriodCloseResult extends BillingPeriod {
|
|
25
40
|
sellerTenantId: string;
|
|
26
41
|
groups: PeriodCloseGroupResult[];
|
|
@@ -34,8 +49,9 @@ export declare class BillingPeriodCloseError extends Error {
|
|
|
34
49
|
export declare function previousCalendarMonth(now?: Date): BillingPeriod;
|
|
35
50
|
export declare function periodCloseId(runtime: Pick<BillingRuntime, 'sellerTenantId' | 'kind'>, payerTenantId: string, currency: string, period: BillingPeriod): Promise<string>;
|
|
36
51
|
/**
|
|
37
|
-
* Close
|
|
38
|
-
*
|
|
52
|
+
* Close billing periods for a runtime's seller: each payer's last ended
|
|
53
|
+
* period, or one explicit period (see {@link ClosePeriodInput}). Idempotent:
|
|
54
|
+
* re-running it resumes unfinished payers and bills nothing twice.
|
|
39
55
|
*/
|
|
40
56
|
export declare function closeBillingPeriod(runtime: BillingRuntime, input?: ClosePeriodInput): Promise<PeriodCloseResult>;
|
|
41
57
|
//# sourceMappingURL=period-close.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"period-close.d.ts","sourceRoot":"","sources":["../../src/billing/period-close.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"period-close.d.ts","sourceRoot":"","sources":["../../src/billing/period-close.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAQnD,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,WAAW,EAAE,IAAI,CAAC;IAClB,8DAA8D;IAC9D,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,iDAAiD;IACjD,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,OAAO,GACP,MAAM,GACN,iBAAiB,GACjB,QAAQ,CAAC;AAEb,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,sBAAsB,EAAE,CAAC;CAClC;AAED,yEAAyE;AACzE,qBAAa,uBAAwB,SAAQ,KAAK;IACpC,QAAQ,CAAC,MAAM,EAAE,iBAAiB;gBAAzB,MAAM,EAAE,iBAAiB;CAW/C;AAmDD,4DAA4D;AAC5D,wBAAgB,qBAAqB,CAAC,GAAG,OAAa,GAAG,aAAa,CAQrE;AAkCD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,gBAAgB,GAAG,MAAM,CAAC,EACxD,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,CAAC,CAUjB;AAsED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,KAAK,GAAE,gBAAqB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAwE5B"}
|
|
@@ -9,6 +9,7 @@ import { BillingAccount, BillingAccountCollection, BillingCloseKind, BillingLine
|
|
|
9
9
|
import { Invoice } from '../models/Invoice.js';
|
|
10
10
|
import { Address } from '../types/index.js';
|
|
11
11
|
import { CreateCreditCheckoutInput } from './credits.js';
|
|
12
|
+
import { ScheduledBillingPeriod } from './cycles.js';
|
|
12
13
|
import { ClosePeriodInput, PeriodCloseResult } from './period-close.js';
|
|
13
14
|
import { BillingProvider, BillingProviderCheckoutSession } from './provider.js';
|
|
14
15
|
/** Ledger accounts billing posts to, in the seller's books. */
|
|
@@ -73,6 +74,15 @@ export interface UpsertBillingAccountInput {
|
|
|
73
74
|
paymentTermsDays?: number;
|
|
74
75
|
/** Link an existing provider customer (for example from checkout). */
|
|
75
76
|
providerCustomerId?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Billing-cycle anchor (#3116): periods run monthly from this instant
|
|
79
|
+
* (anchor at signup/activation for no partial first period). `null` returns
|
|
80
|
+
* the payer to UTC calendar months; omitted leaves it unchanged. Changing it
|
|
81
|
+
* never bills time twice.
|
|
82
|
+
*/
|
|
83
|
+
billingAnchorAt?: Date | null;
|
|
84
|
+
/** Prorate flat plans to the time they were active in a period (#3116). */
|
|
85
|
+
prorateFlatPlans?: boolean;
|
|
76
86
|
}
|
|
77
87
|
/** A synced account: its provider customer id is always present. */
|
|
78
88
|
export interface SyncedBillingAccount {
|
|
@@ -133,8 +143,19 @@ export declare class BillingRuntime {
|
|
|
133
143
|
* dies between creation and this save leaves an unused provider customer.
|
|
134
144
|
*/
|
|
135
145
|
ensureProviderCustomer(account: BillingAccount): Promise<SyncedBillingAccount>;
|
|
136
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* Close billing periods. Without a period, each payer's last ended period
|
|
148
|
+
* on its own schedule (the previous UTC calendar month unless its account
|
|
149
|
+
* has a `billingAnchorAt`), so running it daily is safe. See
|
|
150
|
+
* {@link ClosePeriodInput}.
|
|
151
|
+
*/
|
|
137
152
|
closePeriod(input?: ClosePeriodInput): Promise<PeriodCloseResult>;
|
|
153
|
+
/**
|
|
154
|
+
* The payer's billing period containing `at` (default now) on its account's
|
|
155
|
+
* schedule — for example to show the next invoice date. A payer without an
|
|
156
|
+
* account is on calendar months.
|
|
157
|
+
*/
|
|
158
|
+
billingPeriodFor(payerTenantId: string, at?: Date): Promise<ScheduledBillingPeriod>;
|
|
138
159
|
getInvoice(invoiceId: string): Promise<Invoice>;
|
|
139
160
|
/** Take the close's lease; returns the token, or null if another holds it. */
|
|
140
161
|
acquireCloseLease(closeId: string, now?: Date): Promise<string | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/billing/runtime.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EACL,2BAA2B,EAC3B,KAAK,yBAAyB,EAC9B,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AACxF,OAAO,EACL,KAAK,cAAc,EACnB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,2BAA2B,EAC3B,4BAA4B,EAC5B,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/billing/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/billing/units.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export declare function majorToMinorUnits(amount: number, currency: string): num
|
|
|
26
26
|
export declare function deterministicId(parts: string[]): Promise<string>;
|
|
27
27
|
/** Validate and lower-case a tenant UUID. */
|
|
28
28
|
export declare function canonicalTenantId(id: string, label?: string): string;
|
|
29
|
+
/** Whether `id` is a tenant UUID (any case). */
|
|
30
|
+
export declare function isTenantId(id: string): boolean;
|
|
29
31
|
/** Lower-case a possibly-empty tenant id for comparison. */
|
|
30
32
|
export declare function tenantKey(id: string | null | undefined): string;
|
|
31
33
|
//# sourceMappingURL=units.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/billing/units.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOlE;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AAKD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAc,GAAG,MAAM,CAKzE;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE/D"}
|
|
1
|
+
{"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/billing/units.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ1D;AAED,6EAA6E;AAC7E,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOlE;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AAKD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAc,GAAG,MAAM,CAKzE;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE/D"}
|
|
@@ -43,10 +43,13 @@ function canonicalTenantId(id, label = "Tenant ID") {
|
|
|
43
43
|
if (typeof id !== "string" || !UUID_PATTERN.test(id)) throw new Error(`${label} must be a UUID.`);
|
|
44
44
|
return id.toLowerCase();
|
|
45
45
|
}
|
|
46
|
+
function isTenantId(id) {
|
|
47
|
+
return typeof id === "string" && UUID_PATTERN.test(id);
|
|
48
|
+
}
|
|
46
49
|
function tenantKey(id) {
|
|
47
50
|
return id ? String(id).toLowerCase() : "";
|
|
48
51
|
}
|
|
49
52
|
//#endregion
|
|
50
|
-
export {
|
|
53
|
+
export { majorToMinorUnits as a, tenantKey as c, isTenantId as i, currencyMinorUnitExponent as n, minorToMajorUnits as o, deterministicId as r, normalizeCurrency as s, canonicalTenantId as t };
|
|
51
54
|
|
|
52
|
-
//# sourceMappingURL=units
|
|
55
|
+
//# sourceMappingURL=units-Bk_o7kRV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"units
|
|
1
|
+
{"version":3,"file":"units-Bk_o7kRV.js","names":[],"sources":["../../src/billing/units.ts"],"sourcesContent":["/**\n * Money-unit and identity helpers for billing-period close (#3060).\n *\n * Commerce stores money as integer minor units. `@happyvertical/accounting`\n * takes and returns currency *major* units and converts to the provider's own\n * representation itself, so the only conversion this package performs is the\n * ISO 4217 one: minor units ÷ 10^exponent.\n */\n\nconst CURRENCY_PATTERN = /^[A-Z]{3}$/;\n\n/** Upper-case, validate, and return an ISO 4217-shaped currency code. */\nexport function normalizeCurrency(currency: string): string {\n const code = String(currency ?? '')\n .trim()\n .toUpperCase();\n if (!CURRENCY_PATTERN.test(code)) {\n throw new Error(`Currency '${currency}' must be a three-letter code.`);\n }\n return code;\n}\n\n/** The ISO 4217 minor-unit exponent of a currency (2 for USD, 0 for JPY). */\nexport function currencyMinorUnitExponent(currency: string): number {\n const code = normalizeCurrency(currency);\n const digits = new Intl.NumberFormat('en', {\n style: 'currency',\n currency: code,\n }).resolvedOptions().maximumFractionDigits;\n return typeof digits === 'number' ? digits : 2;\n}\n\n/** Integer minor units → major units (`1999` USD → `19.99`). */\nexport function minorToMajorUnits(amount: number, currency: string): number {\n if (!Number.isSafeInteger(amount)) {\n throw new Error(\n `Amount ${amount} must be a safe integer number of minor units.`,\n );\n }\n return amount / 10 ** currencyMinorUnitExponent(currency);\n}\n\n/**\n * Major units → integer minor units. Rejects a value that is not a whole\n * number of minor units instead of rounding it: a provider amount that does\n * not round-trip exactly means the two sides disagree about the currency.\n */\nexport function majorToMinorUnits(amount: number, currency: string): number {\n if (!Number.isFinite(amount)) {\n throw new Error(`Provider amount ${amount} is not a finite number.`);\n }\n const scaled = amount * 10 ** currencyMinorUnitExponent(currency);\n const minor = Math.round(scaled);\n if (Math.abs(scaled - minor) > 1e-6 || !Number.isSafeInteger(minor)) {\n throw new Error(\n `Provider amount ${amount} ${normalizeCurrency(currency)} is not a whole number of minor units.`,\n );\n }\n return minor;\n}\n\n/**\n * A stable UUID derived from its parts (SHA-256, version/variant bits set).\n * Deterministic ids are what make every period-close write replay-safe: a\n * retry recomputes the same identity instead of creating a second row.\n */\nexport async function deterministicId(parts: string[]): Promise<string> {\n const bytes = new TextEncoder().encode(JSON.stringify(parts));\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n const uuid = digest.slice(0, 16);\n uuid[6] = (uuid[6] & 0x0f) | 0x50;\n uuid[8] = (uuid[8] & 0x3f) | 0x80;\n const hex = Array.from(uuid, (byte) => byte.toString(16).padStart(2, '0'));\n const joined = hex.join('');\n return [\n joined.slice(0, 8),\n joined.slice(8, 12),\n joined.slice(12, 16),\n joined.slice(16, 20),\n joined.slice(20),\n ].join('-');\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Validate and lower-case a tenant UUID. */\nexport function canonicalTenantId(id: string, label = 'Tenant ID'): string {\n if (typeof id !== 'string' || !UUID_PATTERN.test(id)) {\n throw new Error(`${label} must be a UUID.`);\n }\n return id.toLowerCase();\n}\n\n/** Whether `id` is a tenant UUID (any case). */\nexport function isTenantId(id: string): boolean {\n return typeof id === 'string' && UUID_PATTERN.test(id);\n}\n\n/** Lower-case a possibly-empty tenant id for comparison. */\nexport function tenantKey(id: string | null | undefined): string {\n return id ? String(id).toLowerCase() : '';\n}\n"],"mappings":";AASA,IAAM,mBAAmB;AAGlB,SAAS,kBAAkB,UAA0B;CAC1D,MAAM,OAAO,OAAO,YAAY,EAAE,CAAA,CAC/B,KAAK,CAAA,CACL,YAAY;CACf,IAAI,CAAC,iBAAiB,KAAK,IAAI,GAC7B,MAAM,IAAI,MAAM,aAAa,SAAQ,+BAAgC;CAEvE,OAAO;AACT;AAGO,SAAS,0BAA0B,UAA0B;CAClE,MAAM,OAAO,kBAAkB,QAAQ;CACvC,MAAM,SAAS,IAAI,KAAK,aAAa,MAAM;EACzC,OAAO;EACP,UAAU;CACZ,CAAC,CAAA,CAAE,gBAAgB,CAAA,CAAE;CACrB,OAAO,OAAO,WAAW,WAAW,SAAS;AAC/C;AAGO,SAAS,kBAAkB,QAAgB,UAA0B;CAC1E,IAAI,CAAC,OAAO,cAAc,MAAM,GAC9B,MAAM,IAAI,MACR,UAAU,OAAM,+CAClB;CAEF,OAAO,SAAS,MAAM,0BAA0B,QAAQ;AAC1D;AAOO,SAAS,kBAAkB,QAAgB,UAA0B;CAC1E,IAAI,CAAC,OAAO,SAAS,MAAM,GACzB,MAAM,IAAI,MAAM,mBAAmB,OAAM,yBAA0B;CAErE,MAAM,SAAS,SAAS,MAAM,0BAA0B,QAAQ;CAChE,MAAM,QAAQ,KAAK,MAAM,MAAM;CAC/B,IAAI,KAAK,IAAI,SAAS,KAAK,IAAI,QAAQ,CAAC,OAAO,cAAc,KAAK,GAChE,MAAM,IAAI,MACR,mBAAmB,OAAM,GAAI,kBAAkB,QAAQ,EAAC,uCAC1D;CAEF,OAAO;AACT;AAOA,eAAsB,gBAAgB,OAAkC;CACtE,MAAM,QAAQ,IAAI,YAAY,CAAA,CAAE,OAAO,KAAK,UAAU,KAAK,CAAC;CAE5D,MAAM,OAAO,IADM,WAAW,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,CAC5D,CAAA,CAAO,MAAM,GAAG,EAAE;CAC/B,KAAK,KAAM,KAAK,KAAK,KAAQ;CAC7B,KAAK,KAAM,KAAK,KAAK,KAAQ;CAE7B,MAAM,SADM,MAAM,KAAK,OAAO,SAAS,KAAK,SAAS,EAAE,CAAA,CAAE,SAAS,GAAG,GAAG,CACzD,CAAA,CAAI,KAAK,EAAE;CAC1B,OAAO;EACL,OAAO,MAAM,GAAG,CAAC;EACjB,OAAO,MAAM,GAAG,EAAE;EAClB,OAAO,MAAM,IAAI,EAAE;EACnB,OAAO,MAAM,IAAI,EAAE;EACnB,OAAO,MAAM,EAAE;CACjB,CAAA,CAAE,KAAK,GAAG;AACZ;AAEA,IAAM,eACJ;AAGK,SAAS,kBAAkB,IAAY,QAAQ,aAAqB;CACzE,IAAI,OAAO,OAAO,YAAY,CAAC,aAAa,KAAK,EAAE,GACjD,MAAM,IAAI,MAAM,GAAG,MAAK,iBAAkB;CAE5C,OAAO,GAAG,YAAY;AACxB;AAGO,SAAS,WAAW,IAAqB;CAC9C,OAAO,OAAO,OAAO,YAAY,aAAa,KAAK,EAAE;AACvD;AAGO,SAAS,UAAU,IAAuC;CAC/D,OAAO,KAAK,OAAO,EAAE,CAAA,CAAE,YAAY,IAAI;AACzC"}
|