@odla-ai/chapter 0.58.1 → 0.59.1

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.
@@ -1,6 +1,11 @@
1
1
  import { CrmConfig, Crm, FieldConditions } from '@odla-ai/crm';
2
2
  import { initAdmin } from '@odla-ai/db';
3
3
 
4
+ /** The part of a Workers ExecutionContext a chapter route needs: let work outlive the response. */
5
+ interface ExecutionLike {
6
+ waitUntil(promise: Promise<unknown>): void;
7
+ }
8
+
4
9
  /** One membership tier a chapter offers. `priceCents: 0` means free. */
5
10
  interface ChapterTier {
6
11
  /** Stable slug, referenced by `applications.tierId`. Never renumbered. */
@@ -83,6 +88,10 @@ interface ChapterCopy {
83
88
  booking: TextFields<"unavailable" | "loadFailed" | "slotTaken" | "failed" | "loading" | "book" | "booking"> & {
84
89
  title?: string;
85
90
  intro?: string;
91
+ /** Shown under the booking button until a date is chosen. */
92
+ pickDate?: string;
93
+ /** Shown under the booking button once a date is chosen, until a time is. */
94
+ pickTime?: string;
86
95
  };
87
96
  /**
88
97
  * `consent` is the affirmative statement a member agrees to before the card
@@ -1082,7 +1091,7 @@ declare const namedSeatIncluded: (seat: {
1082
1091
  amountCents?: unknown;
1083
1092
  }) => boolean;
1084
1093
  /** Where the named recipient stands, from the purchaser's side, folded from the seat's lifecycle status. */
1085
- type NamedSeatRecipientState = "payment_pending" | "invited" | "accepted" | "joined" | "declined" | "refunded" | "canceled";
1094
+ type NamedSeatRecipientState = "payment_pending" | "invited" | "accepted" | "joined" | "declined" | "refunded" | "canceled" | "disputed";
1086
1095
  /** Fold the seat status machine into the words a members page needs: whether the person was invited, accepted, joined or left. */
1087
1096
  declare function namedSeatRecipientState(seat: {
1088
1097
  status?: unknown;
@@ -1278,6 +1287,71 @@ type NamedSeatEvidenceRequest = MembershipQuoteTarget & {
1278
1287
  eventId: string;
1279
1288
  };
1280
1289
 
1290
+ /** Stripe metadata fields used to bind one fact to an exact configured source. */
1291
+ interface CommercialIdentity {
1292
+ appId?: string;
1293
+ chapterId?: string;
1294
+ environment?: string;
1295
+ dataEnvironment?: string;
1296
+ runtime?: string;
1297
+ }
1298
+ /** Provider object categories materialized by the network commercial shadow. */
1299
+ type CommercialObjectKind = "customer" | "subscription" | "invoice" | "refund" | "seat";
1300
+ /** One verified, allowlisted, PII-free Stripe event fact retained for replay. */
1301
+ interface NetworkCommercialFact {
1302
+ eventId: string;
1303
+ digest: string;
1304
+ eventType: string;
1305
+ eventCreated: number;
1306
+ livemode: boolean;
1307
+ objectKind: CommercialObjectKind | "payment_intent" | "dispute" | "ignored";
1308
+ objectId: string;
1309
+ identity: CommercialIdentity;
1310
+ applicationId?: string;
1311
+ personId?: string;
1312
+ membershipId?: string;
1313
+ seatId?: string;
1314
+ customerId?: string;
1315
+ subscriptionId?: string;
1316
+ invoiceId?: string;
1317
+ chargeId?: string;
1318
+ paymentIntentId?: string;
1319
+ /** Stripe's dispute reason (`fraudulent`, `product_not_received`, …) on a `charge.dispute.*` fact. */
1320
+ disputeReason?: string;
1321
+ subscriptionItemId?: string;
1322
+ refundedCents?: number;
1323
+ /** Complete recurring seat item references, with no recipient PII. */
1324
+ seatSubscriptionItems?: Array<{
1325
+ seatId: string;
1326
+ membershipId: string;
1327
+ subscriptionItemId: string;
1328
+ priceId: string;
1329
+ currentPeriodEnd?: number;
1330
+ }>;
1331
+ priceId?: string;
1332
+ quantity?: number;
1333
+ status?: string;
1334
+ currency?: string;
1335
+ amountCents?: number;
1336
+ amountPaidCents?: number;
1337
+ amountReceivedCents?: number;
1338
+ invoiceTotalCents?: number;
1339
+ billingReason?: string;
1340
+ invoicePaidAt?: number;
1341
+ invoicePeriodEndsAt?: number;
1342
+ /** Complete, non-prorated recurring invoice lines; absent when ambiguous or truncated. */
1343
+ recurringInvoiceLines?: Array<{
1344
+ priceId: string;
1345
+ quantity: number;
1346
+ startsAt: number;
1347
+ endsAt: number;
1348
+ }>;
1349
+ amountRemainingCents?: number;
1350
+ currentPeriodStart?: number;
1351
+ currentPeriodEnd?: number;
1352
+ cancelAtPeriodEnd?: boolean;
1353
+ }
1354
+
1281
1355
  /** Content-free operator alert attached to an error report. Its shape is a subset of
1282
1356
  * the `ErrorReport` that `recordError` from `@odla-ai/o11y` accepts, so a host passes
1283
1357
  * that function straight through as `ChapterWorkerOptions.recordError`. */
@@ -1493,6 +1567,8 @@ declare function createWorkerContext(options: ChapterWorkerOptions): {
1493
1567
  stripeSecretName?: string;
1494
1568
  } | undefined;
1495
1569
  recordError: ChapterErrorRecorder;
1570
+ bindExecution: (req: Request, execution: ExecutionLike | undefined) => void;
1571
+ defer: (req: Request, task: () => Promise<unknown>) => Promise<void>;
1496
1572
  getPublicConfig: (env: ChapterEnv) => Promise<PublicConfig>;
1497
1573
  verifyUser: (req: Request, env: ChapterEnv) => Promise<Verified | null>;
1498
1574
  makeDb: (env: ChapterEnv) => Db;
@@ -1564,6 +1640,98 @@ type NamedSeatOperatorRefundInput = MembershipQuoteTarget & {
1564
1640
  */
1565
1641
  declare function refundNamedSeatByOperator(db: ChapterDb, input: NamedSeatOperatorRefundInput, stripeSecretKey: string, now: number): Promise<NamedSeatRefundReceipt>;
1566
1642
 
1643
+ /** Fence both absence of a collision and an audited resolution of this exact retained fact. */
1644
+ declare function commercialEvidenceGuard(db: ChapterDb, eventId: string, digest: unknown, code?: string): Promise<{
1645
+ ns: string;
1646
+ id: string;
1647
+ exists: boolean;
1648
+ equals?: undefined;
1649
+ } | {
1650
+ ns: string;
1651
+ id: string;
1652
+ exists: boolean;
1653
+ equals: {
1654
+ status: string;
1655
+ resolvedDigest: string;
1656
+ updatedAt: unknown;
1657
+ };
1658
+ }>;
1659
+
1660
+ type Row$4 = Record<string, unknown>;
1661
+ type EvidenceGuard = Awaited<ReturnType<typeof commercialEvidenceGuard>>;
1662
+ /** The seat's own standing on a membership dispute receipt, present when the disputed charge paid a named seat. */
1663
+ interface NamedSeatDisputeStanding {
1664
+ seatId: string;
1665
+ status: string;
1666
+ disputeStatus?: string;
1667
+ applied: boolean;
1668
+ }
1669
+ /** True when the disputed charge paid this seat: its own PaymentIntent, or an operator's re-issued one. A seat line on the
1670
+ * primary subscription's invoice is the primary's charge and is handled there. */
1671
+ declare function disputedChargeCoversSeat(charge: DisputedCharge, seat: Row$4): boolean;
1672
+ /** Apply one verified `charge.dispute.*` fact to the named seat whose payment it names. The primary membership is never
1673
+ * touched: the recipient loses access through the seat's status alone, and a won dispute restores exactly the seat
1674
+ * status the dispute interrupted. Idempotent per event. */
1675
+ declare function reconcileNamedSeatDispute(db: ChapterDb, input: MembershipQuoteTarget & {
1676
+ eventId: string;
1677
+ }, now: number, charge: DisputedCharge, evidence: {
1678
+ row: Row$4;
1679
+ fact: NetworkCommercialFact;
1680
+ guard: EvidenceGuard;
1681
+ }): Promise<MembershipDisputeReceipt>;
1682
+
1683
+ /** Status a membership or named seat holds while a card-network dispute has its payment pulled back. */
1684
+ declare const MEMBERSHIP_DISPUTED_STATUS = "payment_disputed";
1685
+
1686
+ /** The charge a dispute names, read from Stripe so a webhook body never chooses which membership it touches. */
1687
+ interface DisputedCharge {
1688
+ chargeId: string;
1689
+ customerId: string;
1690
+ invoiceId?: string;
1691
+ subscriptionId?: string;
1692
+ paymentIntentId?: string;
1693
+ /** Present when the charge paid a named seat's own PaymentIntent; the dispute is then the seat's, not the primary's. */
1694
+ seatId?: string;
1695
+ }
1696
+ /** Billing receipt plus the dispute's current standing and whether this event changed anything. */
1697
+ interface MembershipDisputeReceipt extends MembershipRenewalReceipt {
1698
+ disputeStatus?: string;
1699
+ applied: boolean;
1700
+ /** The seat's standing when the disputed charge paid a named seat; the membership fields are then unchanged. */
1701
+ seat?: NamedSeatDisputeStanding;
1702
+ }
1703
+ /** Resolve the disputed charge, and the subscription behind its invoice, with the network's own Stripe credential. */
1704
+ declare function readDisputedCharge(secretKey: string, chargeId: string, fetch?: typeof globalThis.fetch): Promise<DisputedCharge>;
1705
+ /** Apply one verified `charge.dispute.*` fact to the membership it names, or to the named seat when the charge paid a
1706
+ * seat's own PaymentIntent. An open dispute suspends access; a won dispute restores the status it interrupted; a lost
1707
+ * one leaves the record suspended until an operator re-issues the charge. Idempotent per event, a closed dispute
1708
+ * outranks a late opening event, and a charge that is neither this membership's nor its seat's changes nothing. */
1709
+ declare function reconcileMembershipDispute(db: ChapterDb, input: MembershipQuoteTarget & {
1710
+ eventId: string;
1711
+ }, now: number, secretKey: string, resolved?: DisputedCharge): Promise<MembershipDisputeReceipt>;
1712
+
1713
+ /** What an operator sees after asking the network to collect a lost dispute's amount again. */
1714
+ interface MembershipRechargeReceipt {
1715
+ membershipId: string;
1716
+ status: string;
1717
+ paymentIntentId: string;
1718
+ amountCents: number;
1719
+ currency: string;
1720
+ replayed?: true;
1721
+ /** Present when the re-issued charge paid a named seat; `membershipId` is then the seat's primary membership. */
1722
+ seatId?: string;
1723
+ }
1724
+ /** Stripe's final answer to a re-issued charge, with the decline code when the card refused it. */
1725
+ interface MembershipRechargeError extends Error {
1726
+ code: string;
1727
+ declineCode?: string;
1728
+ paymentStatus?: string;
1729
+ }
1730
+ /** Re-issue a membership's lost dispute amount; see {@link rechargeDisputed}. */
1731
+ declare const rechargeDisputedMembership: (db: ChapterDb, membershipId: string, secretKey: string, now: number) => Promise<MembershipRechargeReceipt>;
1732
+ /** Re-issue a named seat's lost dispute amount on the purchaser's saved card; the primary membership is untouched. */
1733
+ declare const rechargeDisputedSeat: (db: ChapterDb, seatId: string, secretKey: string, now: number) => Promise<MembershipRechargeReceipt>;
1734
+
1567
1735
  /** Seat states in which the purchaser may still name someone else: invited or accepted, never active, refunded or unpaid. */
1568
1736
  declare const REGIFTABLE_SEAT_STATUSES: readonly ["pending_acceptance", "pending_approval", "pending_primary"];
1569
1737
  /** Whether a seat may be given to a different recipient (BNF bug 38000934): final once active. */
@@ -2332,7 +2500,9 @@ declare function withActiveSignupControlTheme(response: Response, options: Activ
2332
2500
  * keep those alerts as structured `console.error` lines.
2333
2501
  */
2334
2502
  declare function chapterWorker(options: ChapterWorkerOptions): {
2335
- fetch(req: Request, env: ChapterEnv): Promise<Response>;
2503
+ fetch(req: Request, env: ChapterEnv, execution?: {
2504
+ waitUntil(promise: Promise<unknown>): void;
2505
+ }): Promise<Response>;
2336
2506
  };
2337
2507
 
2338
- export { type ActiveSignupControlHtml, type ActiveSignupControlThemeOptions, AdmissionGrantDeliveryError, type AdmissionGrantDeliveryOptions, type BuilderLibraryDeliveryOptions, type ChapterEnv, type ChapterErrorRecorder, type ChapterErrorReport, type ChapterWorkerOptions, DECLINABLE_FROM, type FormerNamedSeatRecipient, type IncludedNamedSeatReceipt, type LeaderAdmissionAssignment, type LeaderAdmissionDelivery, type LeaderAdmissionDeliveryStatus, MEMBERSHIP_AUTHORITY_PATH, MEMBERSHIP_CHAPTER_EFFECT_KINDS, type MemberContentDeliveryOptions, type MembershipAuthorityHandlerOptions, type MembershipAuthorityPeer, type MembershipChapterEffect, type MembershipChapterEffectKind, type MembershipEffectScope, type MembershipRetermInput, type MembershipRetermReceipt, type MembershipRetermSkipReason, type MembershipWorkerAdapter, type MembershipWorkerAdapterOptions, type NamedSeatOffer, type NamedSeatOfferSeat, type NamedSeatOperatorRefundInput, type NamedSeatRecipientState, type NamedSeatRefundReason, type NamedSeatRegiftReceipt, type NamedSeatReleaseInput, type NetworkTargetTransport, NetworkTargetTransportError, REGIFTABLE_SEAT_STATUSES, type RegiftNamedSeatInput, type Route, type SeatRepriceReceipt, type SeatRepriceScope, type SignupControlDeliveryOptions, type SignupEligibility, type SignupPredecessor, type WorkerContext, chapterMembershipAccess, chapterWorker, closedApplication, continueApplicationOnFreeTier, createMembershipAuthorityHandler, createMembershipAuthorityStorage, createMembershipChapterEffectRoute, createMembershipWorkerAdapter, createWorkerContext, declineApplication, deliverAdmissionGrants, deliverBuilderLibrary, deliverMemberContent, deliverMembershipChapterEffect, deliverSignupControl, drainMembershipEffects, grantIncludedNamedSeat, liftSeatPurchaseWindow, lowerSeatPrice, namedSeatIncluded, namedSeatInvitationOpen, namedSeatRecipientState, namedSeatRegiftable, readNamedSeatOffer, refundNamedSeatByOperator, regiftNamedSeat, releaseNamedSeatRecipientApplications, renderActiveSignupControlHtml, resolveNetworkTargetTransport, resolveSignupEligibility, retermMembershipDiscount, retryChapterApprovalEffects, retryLeaderAdmissionDeliveries, withActiveSignupControlTheme };
2508
+ export { type ActiveSignupControlHtml, type ActiveSignupControlThemeOptions, AdmissionGrantDeliveryError, type AdmissionGrantDeliveryOptions, type BuilderLibraryDeliveryOptions, type ChapterEnv, type ChapterErrorRecorder, type ChapterErrorReport, type ChapterWorkerOptions, DECLINABLE_FROM, type DisputedCharge, type FormerNamedSeatRecipient, type IncludedNamedSeatReceipt, type LeaderAdmissionAssignment, type LeaderAdmissionDelivery, type LeaderAdmissionDeliveryStatus, MEMBERSHIP_AUTHORITY_PATH, MEMBERSHIP_CHAPTER_EFFECT_KINDS, MEMBERSHIP_DISPUTED_STATUS, type MemberContentDeliveryOptions, type MembershipAuthorityHandlerOptions, type MembershipAuthorityPeer, type MembershipChapterEffect, type MembershipChapterEffectKind, type MembershipDisputeReceipt, type MembershipEffectScope, type MembershipRechargeError, type MembershipRechargeReceipt, type MembershipRetermInput, type MembershipRetermReceipt, type MembershipRetermSkipReason, type MembershipWorkerAdapter, type MembershipWorkerAdapterOptions, type NamedSeatDisputeStanding, type NamedSeatOffer, type NamedSeatOfferSeat, type NamedSeatOperatorRefundInput, type NamedSeatRecipientState, type NamedSeatRefundReason, type NamedSeatRegiftReceipt, type NamedSeatReleaseInput, type NetworkTargetTransport, NetworkTargetTransportError, REGIFTABLE_SEAT_STATUSES, type RegiftNamedSeatInput, type Route, type SeatRepriceReceipt, type SeatRepriceScope, type SignupControlDeliveryOptions, type SignupEligibility, type SignupPredecessor, type WorkerContext, chapterMembershipAccess, chapterWorker, closedApplication, continueApplicationOnFreeTier, createMembershipAuthorityHandler, createMembershipAuthorityStorage, createMembershipChapterEffectRoute, createMembershipWorkerAdapter, createWorkerContext, declineApplication, deliverAdmissionGrants, deliverBuilderLibrary, deliverMemberContent, deliverMembershipChapterEffect, deliverSignupControl, disputedChargeCoversSeat, drainMembershipEffects, grantIncludedNamedSeat, liftSeatPurchaseWindow, lowerSeatPrice, namedSeatIncluded, namedSeatInvitationOpen, namedSeatRecipientState, namedSeatRegiftable, readDisputedCharge, readNamedSeatOffer, rechargeDisputedMembership, rechargeDisputedSeat, reconcileMembershipDispute, reconcileNamedSeatDispute, refundNamedSeatByOperator, regiftNamedSeat, releaseNamedSeatRecipientApplications, renderActiveSignupControlHtml, resolveNetworkTargetTransport, resolveSignupEligibility, retermMembershipDiscount, retryChapterApprovalEffects, retryLeaderAdmissionDeliveries, withActiveSignupControlTheme };
@@ -1,6 +1,11 @@
1
1
  import { CrmConfig, Crm, FieldConditions } from '@odla-ai/crm';
2
2
  import { initAdmin } from '@odla-ai/db';
3
3
 
4
+ /** The part of a Workers ExecutionContext a chapter route needs: let work outlive the response. */
5
+ interface ExecutionLike {
6
+ waitUntil(promise: Promise<unknown>): void;
7
+ }
8
+
4
9
  /** One membership tier a chapter offers. `priceCents: 0` means free. */
5
10
  interface ChapterTier {
6
11
  /** Stable slug, referenced by `applications.tierId`. Never renumbered. */
@@ -83,6 +88,10 @@ interface ChapterCopy {
83
88
  booking: TextFields<"unavailable" | "loadFailed" | "slotTaken" | "failed" | "loading" | "book" | "booking"> & {
84
89
  title?: string;
85
90
  intro?: string;
91
+ /** Shown under the booking button until a date is chosen. */
92
+ pickDate?: string;
93
+ /** Shown under the booking button once a date is chosen, until a time is. */
94
+ pickTime?: string;
86
95
  };
87
96
  /**
88
97
  * `consent` is the affirmative statement a member agrees to before the card
@@ -1082,7 +1091,7 @@ declare const namedSeatIncluded: (seat: {
1082
1091
  amountCents?: unknown;
1083
1092
  }) => boolean;
1084
1093
  /** Where the named recipient stands, from the purchaser's side, folded from the seat's lifecycle status. */
1085
- type NamedSeatRecipientState = "payment_pending" | "invited" | "accepted" | "joined" | "declined" | "refunded" | "canceled";
1094
+ type NamedSeatRecipientState = "payment_pending" | "invited" | "accepted" | "joined" | "declined" | "refunded" | "canceled" | "disputed";
1086
1095
  /** Fold the seat status machine into the words a members page needs: whether the person was invited, accepted, joined or left. */
1087
1096
  declare function namedSeatRecipientState(seat: {
1088
1097
  status?: unknown;
@@ -1278,6 +1287,71 @@ type NamedSeatEvidenceRequest = MembershipQuoteTarget & {
1278
1287
  eventId: string;
1279
1288
  };
1280
1289
 
1290
+ /** Stripe metadata fields used to bind one fact to an exact configured source. */
1291
+ interface CommercialIdentity {
1292
+ appId?: string;
1293
+ chapterId?: string;
1294
+ environment?: string;
1295
+ dataEnvironment?: string;
1296
+ runtime?: string;
1297
+ }
1298
+ /** Provider object categories materialized by the network commercial shadow. */
1299
+ type CommercialObjectKind = "customer" | "subscription" | "invoice" | "refund" | "seat";
1300
+ /** One verified, allowlisted, PII-free Stripe event fact retained for replay. */
1301
+ interface NetworkCommercialFact {
1302
+ eventId: string;
1303
+ digest: string;
1304
+ eventType: string;
1305
+ eventCreated: number;
1306
+ livemode: boolean;
1307
+ objectKind: CommercialObjectKind | "payment_intent" | "dispute" | "ignored";
1308
+ objectId: string;
1309
+ identity: CommercialIdentity;
1310
+ applicationId?: string;
1311
+ personId?: string;
1312
+ membershipId?: string;
1313
+ seatId?: string;
1314
+ customerId?: string;
1315
+ subscriptionId?: string;
1316
+ invoiceId?: string;
1317
+ chargeId?: string;
1318
+ paymentIntentId?: string;
1319
+ /** Stripe's dispute reason (`fraudulent`, `product_not_received`, …) on a `charge.dispute.*` fact. */
1320
+ disputeReason?: string;
1321
+ subscriptionItemId?: string;
1322
+ refundedCents?: number;
1323
+ /** Complete recurring seat item references, with no recipient PII. */
1324
+ seatSubscriptionItems?: Array<{
1325
+ seatId: string;
1326
+ membershipId: string;
1327
+ subscriptionItemId: string;
1328
+ priceId: string;
1329
+ currentPeriodEnd?: number;
1330
+ }>;
1331
+ priceId?: string;
1332
+ quantity?: number;
1333
+ status?: string;
1334
+ currency?: string;
1335
+ amountCents?: number;
1336
+ amountPaidCents?: number;
1337
+ amountReceivedCents?: number;
1338
+ invoiceTotalCents?: number;
1339
+ billingReason?: string;
1340
+ invoicePaidAt?: number;
1341
+ invoicePeriodEndsAt?: number;
1342
+ /** Complete, non-prorated recurring invoice lines; absent when ambiguous or truncated. */
1343
+ recurringInvoiceLines?: Array<{
1344
+ priceId: string;
1345
+ quantity: number;
1346
+ startsAt: number;
1347
+ endsAt: number;
1348
+ }>;
1349
+ amountRemainingCents?: number;
1350
+ currentPeriodStart?: number;
1351
+ currentPeriodEnd?: number;
1352
+ cancelAtPeriodEnd?: boolean;
1353
+ }
1354
+
1281
1355
  /** Content-free operator alert attached to an error report. Its shape is a subset of
1282
1356
  * the `ErrorReport` that `recordError` from `@odla-ai/o11y` accepts, so a host passes
1283
1357
  * that function straight through as `ChapterWorkerOptions.recordError`. */
@@ -1493,6 +1567,8 @@ declare function createWorkerContext(options: ChapterWorkerOptions): {
1493
1567
  stripeSecretName?: string;
1494
1568
  } | undefined;
1495
1569
  recordError: ChapterErrorRecorder;
1570
+ bindExecution: (req: Request, execution: ExecutionLike | undefined) => void;
1571
+ defer: (req: Request, task: () => Promise<unknown>) => Promise<void>;
1496
1572
  getPublicConfig: (env: ChapterEnv) => Promise<PublicConfig>;
1497
1573
  verifyUser: (req: Request, env: ChapterEnv) => Promise<Verified | null>;
1498
1574
  makeDb: (env: ChapterEnv) => Db;
@@ -1564,6 +1640,98 @@ type NamedSeatOperatorRefundInput = MembershipQuoteTarget & {
1564
1640
  */
1565
1641
  declare function refundNamedSeatByOperator(db: ChapterDb, input: NamedSeatOperatorRefundInput, stripeSecretKey: string, now: number): Promise<NamedSeatRefundReceipt>;
1566
1642
 
1643
+ /** Fence both absence of a collision and an audited resolution of this exact retained fact. */
1644
+ declare function commercialEvidenceGuard(db: ChapterDb, eventId: string, digest: unknown, code?: string): Promise<{
1645
+ ns: string;
1646
+ id: string;
1647
+ exists: boolean;
1648
+ equals?: undefined;
1649
+ } | {
1650
+ ns: string;
1651
+ id: string;
1652
+ exists: boolean;
1653
+ equals: {
1654
+ status: string;
1655
+ resolvedDigest: string;
1656
+ updatedAt: unknown;
1657
+ };
1658
+ }>;
1659
+
1660
+ type Row$4 = Record<string, unknown>;
1661
+ type EvidenceGuard = Awaited<ReturnType<typeof commercialEvidenceGuard>>;
1662
+ /** The seat's own standing on a membership dispute receipt, present when the disputed charge paid a named seat. */
1663
+ interface NamedSeatDisputeStanding {
1664
+ seatId: string;
1665
+ status: string;
1666
+ disputeStatus?: string;
1667
+ applied: boolean;
1668
+ }
1669
+ /** True when the disputed charge paid this seat: its own PaymentIntent, or an operator's re-issued one. A seat line on the
1670
+ * primary subscription's invoice is the primary's charge and is handled there. */
1671
+ declare function disputedChargeCoversSeat(charge: DisputedCharge, seat: Row$4): boolean;
1672
+ /** Apply one verified `charge.dispute.*` fact to the named seat whose payment it names. The primary membership is never
1673
+ * touched: the recipient loses access through the seat's status alone, and a won dispute restores exactly the seat
1674
+ * status the dispute interrupted. Idempotent per event. */
1675
+ declare function reconcileNamedSeatDispute(db: ChapterDb, input: MembershipQuoteTarget & {
1676
+ eventId: string;
1677
+ }, now: number, charge: DisputedCharge, evidence: {
1678
+ row: Row$4;
1679
+ fact: NetworkCommercialFact;
1680
+ guard: EvidenceGuard;
1681
+ }): Promise<MembershipDisputeReceipt>;
1682
+
1683
+ /** Status a membership or named seat holds while a card-network dispute has its payment pulled back. */
1684
+ declare const MEMBERSHIP_DISPUTED_STATUS = "payment_disputed";
1685
+
1686
+ /** The charge a dispute names, read from Stripe so a webhook body never chooses which membership it touches. */
1687
+ interface DisputedCharge {
1688
+ chargeId: string;
1689
+ customerId: string;
1690
+ invoiceId?: string;
1691
+ subscriptionId?: string;
1692
+ paymentIntentId?: string;
1693
+ /** Present when the charge paid a named seat's own PaymentIntent; the dispute is then the seat's, not the primary's. */
1694
+ seatId?: string;
1695
+ }
1696
+ /** Billing receipt plus the dispute's current standing and whether this event changed anything. */
1697
+ interface MembershipDisputeReceipt extends MembershipRenewalReceipt {
1698
+ disputeStatus?: string;
1699
+ applied: boolean;
1700
+ /** The seat's standing when the disputed charge paid a named seat; the membership fields are then unchanged. */
1701
+ seat?: NamedSeatDisputeStanding;
1702
+ }
1703
+ /** Resolve the disputed charge, and the subscription behind its invoice, with the network's own Stripe credential. */
1704
+ declare function readDisputedCharge(secretKey: string, chargeId: string, fetch?: typeof globalThis.fetch): Promise<DisputedCharge>;
1705
+ /** Apply one verified `charge.dispute.*` fact to the membership it names, or to the named seat when the charge paid a
1706
+ * seat's own PaymentIntent. An open dispute suspends access; a won dispute restores the status it interrupted; a lost
1707
+ * one leaves the record suspended until an operator re-issues the charge. Idempotent per event, a closed dispute
1708
+ * outranks a late opening event, and a charge that is neither this membership's nor its seat's changes nothing. */
1709
+ declare function reconcileMembershipDispute(db: ChapterDb, input: MembershipQuoteTarget & {
1710
+ eventId: string;
1711
+ }, now: number, secretKey: string, resolved?: DisputedCharge): Promise<MembershipDisputeReceipt>;
1712
+
1713
+ /** What an operator sees after asking the network to collect a lost dispute's amount again. */
1714
+ interface MembershipRechargeReceipt {
1715
+ membershipId: string;
1716
+ status: string;
1717
+ paymentIntentId: string;
1718
+ amountCents: number;
1719
+ currency: string;
1720
+ replayed?: true;
1721
+ /** Present when the re-issued charge paid a named seat; `membershipId` is then the seat's primary membership. */
1722
+ seatId?: string;
1723
+ }
1724
+ /** Stripe's final answer to a re-issued charge, with the decline code when the card refused it. */
1725
+ interface MembershipRechargeError extends Error {
1726
+ code: string;
1727
+ declineCode?: string;
1728
+ paymentStatus?: string;
1729
+ }
1730
+ /** Re-issue a membership's lost dispute amount; see {@link rechargeDisputed}. */
1731
+ declare const rechargeDisputedMembership: (db: ChapterDb, membershipId: string, secretKey: string, now: number) => Promise<MembershipRechargeReceipt>;
1732
+ /** Re-issue a named seat's lost dispute amount on the purchaser's saved card; the primary membership is untouched. */
1733
+ declare const rechargeDisputedSeat: (db: ChapterDb, seatId: string, secretKey: string, now: number) => Promise<MembershipRechargeReceipt>;
1734
+
1567
1735
  /** Seat states in which the purchaser may still name someone else: invited or accepted, never active, refunded or unpaid. */
1568
1736
  declare const REGIFTABLE_SEAT_STATUSES: readonly ["pending_acceptance", "pending_approval", "pending_primary"];
1569
1737
  /** Whether a seat may be given to a different recipient (BNF bug 38000934): final once active. */
@@ -2332,7 +2500,9 @@ declare function withActiveSignupControlTheme(response: Response, options: Activ
2332
2500
  * keep those alerts as structured `console.error` lines.
2333
2501
  */
2334
2502
  declare function chapterWorker(options: ChapterWorkerOptions): {
2335
- fetch(req: Request, env: ChapterEnv): Promise<Response>;
2503
+ fetch(req: Request, env: ChapterEnv, execution?: {
2504
+ waitUntil(promise: Promise<unknown>): void;
2505
+ }): Promise<Response>;
2336
2506
  };
2337
2507
 
2338
- export { type ActiveSignupControlHtml, type ActiveSignupControlThemeOptions, AdmissionGrantDeliveryError, type AdmissionGrantDeliveryOptions, type BuilderLibraryDeliveryOptions, type ChapterEnv, type ChapterErrorRecorder, type ChapterErrorReport, type ChapterWorkerOptions, DECLINABLE_FROM, type FormerNamedSeatRecipient, type IncludedNamedSeatReceipt, type LeaderAdmissionAssignment, type LeaderAdmissionDelivery, type LeaderAdmissionDeliveryStatus, MEMBERSHIP_AUTHORITY_PATH, MEMBERSHIP_CHAPTER_EFFECT_KINDS, type MemberContentDeliveryOptions, type MembershipAuthorityHandlerOptions, type MembershipAuthorityPeer, type MembershipChapterEffect, type MembershipChapterEffectKind, type MembershipEffectScope, type MembershipRetermInput, type MembershipRetermReceipt, type MembershipRetermSkipReason, type MembershipWorkerAdapter, type MembershipWorkerAdapterOptions, type NamedSeatOffer, type NamedSeatOfferSeat, type NamedSeatOperatorRefundInput, type NamedSeatRecipientState, type NamedSeatRefundReason, type NamedSeatRegiftReceipt, type NamedSeatReleaseInput, type NetworkTargetTransport, NetworkTargetTransportError, REGIFTABLE_SEAT_STATUSES, type RegiftNamedSeatInput, type Route, type SeatRepriceReceipt, type SeatRepriceScope, type SignupControlDeliveryOptions, type SignupEligibility, type SignupPredecessor, type WorkerContext, chapterMembershipAccess, chapterWorker, closedApplication, continueApplicationOnFreeTier, createMembershipAuthorityHandler, createMembershipAuthorityStorage, createMembershipChapterEffectRoute, createMembershipWorkerAdapter, createWorkerContext, declineApplication, deliverAdmissionGrants, deliverBuilderLibrary, deliverMemberContent, deliverMembershipChapterEffect, deliverSignupControl, drainMembershipEffects, grantIncludedNamedSeat, liftSeatPurchaseWindow, lowerSeatPrice, namedSeatIncluded, namedSeatInvitationOpen, namedSeatRecipientState, namedSeatRegiftable, readNamedSeatOffer, refundNamedSeatByOperator, regiftNamedSeat, releaseNamedSeatRecipientApplications, renderActiveSignupControlHtml, resolveNetworkTargetTransport, resolveSignupEligibility, retermMembershipDiscount, retryChapterApprovalEffects, retryLeaderAdmissionDeliveries, withActiveSignupControlTheme };
2508
+ export { type ActiveSignupControlHtml, type ActiveSignupControlThemeOptions, AdmissionGrantDeliveryError, type AdmissionGrantDeliveryOptions, type BuilderLibraryDeliveryOptions, type ChapterEnv, type ChapterErrorRecorder, type ChapterErrorReport, type ChapterWorkerOptions, DECLINABLE_FROM, type DisputedCharge, type FormerNamedSeatRecipient, type IncludedNamedSeatReceipt, type LeaderAdmissionAssignment, type LeaderAdmissionDelivery, type LeaderAdmissionDeliveryStatus, MEMBERSHIP_AUTHORITY_PATH, MEMBERSHIP_CHAPTER_EFFECT_KINDS, MEMBERSHIP_DISPUTED_STATUS, type MemberContentDeliveryOptions, type MembershipAuthorityHandlerOptions, type MembershipAuthorityPeer, type MembershipChapterEffect, type MembershipChapterEffectKind, type MembershipDisputeReceipt, type MembershipEffectScope, type MembershipRechargeError, type MembershipRechargeReceipt, type MembershipRetermInput, type MembershipRetermReceipt, type MembershipRetermSkipReason, type MembershipWorkerAdapter, type MembershipWorkerAdapterOptions, type NamedSeatDisputeStanding, type NamedSeatOffer, type NamedSeatOfferSeat, type NamedSeatOperatorRefundInput, type NamedSeatRecipientState, type NamedSeatRefundReason, type NamedSeatRegiftReceipt, type NamedSeatReleaseInput, type NetworkTargetTransport, NetworkTargetTransportError, REGIFTABLE_SEAT_STATUSES, type RegiftNamedSeatInput, type Route, type SeatRepriceReceipt, type SeatRepriceScope, type SignupControlDeliveryOptions, type SignupEligibility, type SignupPredecessor, type WorkerContext, chapterMembershipAccess, chapterWorker, closedApplication, continueApplicationOnFreeTier, createMembershipAuthorityHandler, createMembershipAuthorityStorage, createMembershipChapterEffectRoute, createMembershipWorkerAdapter, createWorkerContext, declineApplication, deliverAdmissionGrants, deliverBuilderLibrary, deliverMemberContent, deliverMembershipChapterEffect, deliverSignupControl, disputedChargeCoversSeat, drainMembershipEffects, grantIncludedNamedSeat, liftSeatPurchaseWindow, lowerSeatPrice, namedSeatIncluded, namedSeatInvitationOpen, namedSeatRecipientState, namedSeatRegiftable, readDisputedCharge, readNamedSeatOffer, rechargeDisputedMembership, rechargeDisputedSeat, reconcileMembershipDispute, reconcileNamedSeatDispute, refundNamedSeatByOperator, regiftNamedSeat, releaseNamedSeatRecipientApplications, renderActiveSignupControlHtml, resolveNetworkTargetTransport, resolveSignupEligibility, retermMembershipDiscount, retryChapterApprovalEffects, retryLeaderAdmissionDeliveries, withActiveSignupControlTheme };