@odla-ai/chapter 0.58.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/{chunk-FHKZVF5G.js → chunk-GADLTNJF.js} +4 -2
- package/dist/{chunk-FHKZVF5G.js.map → chunk-GADLTNJF.js.map} +1 -1
- package/dist/{chunk-L2IPS2FQ.js → chunk-WFTWT3V7.js} +141 -56
- package/dist/chunk-WFTWT3V7.js.map +1 -0
- package/dist/{chunk-CKC6X5H2.js → chunk-Y2H4VURI.js} +399 -308
- package/dist/chunk-Y2H4VURI.js.map +1 -0
- package/dist/{copy-context-DE4YMAbz.d.ts → copy-context-BuJBQafl.d.ts} +4 -0
- package/dist/index.cjs +218 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +214 -80
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +36 -3
- package/dist/ui/admin/index.js +6 -2
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +15 -3
- package/dist/ui/member/index.d.ts +33 -5
- package/dist/ui/member/index.js +10 -2
- package/dist/worker/index.cjs +611 -184
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +64 -2
- package/dist/worker/index.d.ts +64 -2
- package/dist/worker/index.js +604 -177
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-CKC6X5H2.js.map +0 -1
- package/dist/chunk-L2IPS2FQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1560,7 +1560,9 @@ var DEFAULT_JOIN_COPY = {
|
|
|
1560
1560
|
failed: "The booking could not be completed.",
|
|
1561
1561
|
loading: "Loading available times\u2026",
|
|
1562
1562
|
book: "Book this time",
|
|
1563
|
-
booking: "Booking\u2026"
|
|
1563
|
+
booking: "Booking\u2026",
|
|
1564
|
+
pickDate: "Pick a date",
|
|
1565
|
+
pickTime: "Pick a time"
|
|
1564
1566
|
},
|
|
1565
1567
|
payment: {
|
|
1566
1568
|
setupFailed: "Payment could not be set up. Please try again.",
|
|
@@ -1712,8 +1714,8 @@ var APPLICATION_LABELS = {
|
|
|
1712
1714
|
state: "State"
|
|
1713
1715
|
};
|
|
1714
1716
|
function humanize(id11) {
|
|
1715
|
-
const
|
|
1716
|
-
return
|
|
1717
|
+
const text7 = id11.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
1718
|
+
return text7 ? `${text7[0].toUpperCase()}${text7.slice(1)}` : id11;
|
|
1717
1719
|
}
|
|
1718
1720
|
function applicationDefs(chapter) {
|
|
1719
1721
|
try {
|
|
@@ -2512,6 +2514,7 @@ function normalizeNetworkCommercialStripeEvent(input, digest) {
|
|
|
2512
2514
|
else if (eventType.startsWith("refund.")) objectKind = "refund";
|
|
2513
2515
|
else if (eventType === "charge.refunded") objectKind = "refund";
|
|
2514
2516
|
else if (eventType.startsWith("payment_intent.")) objectKind = "payment_intent";
|
|
2517
|
+
else if (eventType.startsWith("charge.dispute.")) objectKind = "dispute";
|
|
2515
2518
|
const customerId = refId(object2.customer, "cus_");
|
|
2516
2519
|
const subscriptionId = objectKind === "subscription" ? providerId(objectId, "sub_") : subscriptionRef(object2);
|
|
2517
2520
|
const invoiceId = objectKind === "invoice" ? providerId(objectId, "in_") : refId(object2.invoice, "in_");
|
|
@@ -2521,6 +2524,7 @@ function normalizeNetworkCommercialStripeEvent(input, digest) {
|
|
|
2521
2524
|
const amountCents = integer(eventType === "charge.refunded" ? object2.amount_refunded : object2.amount);
|
|
2522
2525
|
const currency = text(object2.currency)?.toLowerCase();
|
|
2523
2526
|
const status2 = text(object2.status);
|
|
2527
|
+
const disputeReason = objectKind === "dispute" ? text(object2.reason) : void 0;
|
|
2524
2528
|
return {
|
|
2525
2529
|
eventId,
|
|
2526
2530
|
digest,
|
|
@@ -2539,6 +2543,7 @@ function normalizeNetworkCommercialStripeEvent(input, digest) {
|
|
|
2539
2543
|
...invoiceId ? { invoiceId } : {},
|
|
2540
2544
|
...chargeId ? { chargeId } : {},
|
|
2541
2545
|
...paymentIntentId ? { paymentIntentId } : {},
|
|
2546
|
+
...disputeReason ? { disputeReason } : {},
|
|
2542
2547
|
...price,
|
|
2543
2548
|
...objectKind === "subscription" && commercialSeatItems(object2) ? { seatSubscriptionItems: commercialSeatItems(object2) } : {},
|
|
2544
2549
|
...status2 ? { status: status2 } : {},
|
|
@@ -2581,6 +2586,7 @@ function ownRef(fact) {
|
|
|
2581
2586
|
if (fact.objectKind === "subscription") return `subscription:${fact.objectId}`;
|
|
2582
2587
|
if (fact.objectKind === "invoice") return `invoice:${fact.objectId}`;
|
|
2583
2588
|
if (fact.objectKind === "payment_intent") return `payment_intent:${fact.objectId}`;
|
|
2589
|
+
if (fact.objectKind === "dispute") return `dispute:${fact.objectId}`;
|
|
2584
2590
|
if (fact.eventType.startsWith("refund.")) return `refund:${fact.objectId}`;
|
|
2585
2591
|
return fact.eventType === "charge.refunded" ? `charge:${fact.objectId}` : null;
|
|
2586
2592
|
}
|
|
@@ -2636,6 +2642,7 @@ function projectNetworkCommercialEvents(input, sources) {
|
|
|
2636
2642
|
continue;
|
|
2637
2643
|
}
|
|
2638
2644
|
const source = assignments.get(fact.eventId);
|
|
2645
|
+
if (!source && fact.objectKind === "dispute") continue;
|
|
2639
2646
|
if (!source) {
|
|
2640
2647
|
const direct = directSource(fact, sources);
|
|
2641
2648
|
const keys2 = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
|
|
@@ -2652,6 +2659,7 @@ function projectNetworkCommercialEvents(input, sources) {
|
|
|
2652
2659
|
const subscriptions = rows3.filter((row2) => row2.objectKind === "subscription");
|
|
2653
2660
|
const invoices = rows3.filter((row2) => row2.objectKind === "invoice");
|
|
2654
2661
|
const seatPayments = rows3.filter((row2) => row2.objectKind === "payment_intent" && row2.seatId && row2.membershipId && !row2.invoiceId);
|
|
2662
|
+
const disputes = rows3.filter((row2) => row2.objectKind === "dispute");
|
|
2655
2663
|
const refunds = rows3.filter((row2) => row2.objectKind === "refund" && row2.eventType.startsWith("refund."));
|
|
2656
2664
|
for (const charge of rows3.filter((row2) => row2.eventType === "charge.refunded")) {
|
|
2657
2665
|
if (!refunds.some((refund) => refund.chargeId === charge.objectId)) {
|
|
@@ -2672,6 +2680,7 @@ function projectNetworkCommercialEvents(input, sources) {
|
|
|
2672
2680
|
for (const row2 of refunds) add(row2, "refundedCents", row2.amountCents ?? 0);
|
|
2673
2681
|
return {
|
|
2674
2682
|
...seatPayments.length ? { seatPayments, seats: projectCommercialSeats(seatPayments, subscriptions, refunds) } : {},
|
|
2683
|
+
...disputes.length ? { disputes } : {},
|
|
2675
2684
|
customers,
|
|
2676
2685
|
subscriptions,
|
|
2677
2686
|
invoices,
|
|
@@ -4275,7 +4284,7 @@ async function refundNetworkMembership(db, input, secretKey, now) {
|
|
|
4275
4284
|
}
|
|
4276
4285
|
|
|
4277
4286
|
// src/membership-provider-lifecycle.ts
|
|
4278
|
-
import { stripeRequest as
|
|
4287
|
+
import { stripeRequest as stripeRequest5 } from "@odla-ai/stripe";
|
|
4279
4288
|
|
|
4280
4289
|
// src/membership-seat-reservation.ts
|
|
4281
4290
|
async function namedSeatRecipientClaimId(input, email) {
|
|
@@ -4566,12 +4575,113 @@ async function reconcileMembershipRenewal(db, input, now) {
|
|
|
4566
4575
|
return membershipFail("membership_renewal_retry_required");
|
|
4567
4576
|
}
|
|
4568
4577
|
|
|
4578
|
+
// src/membership-dispute-authority.ts
|
|
4579
|
+
import { stripeRequest as stripeRequest4 } from "@odla-ai/stripe";
|
|
4580
|
+
var MEMBERSHIP_DISPUTED_STATUS = "payment_disputed";
|
|
4581
|
+
var DISPUTE_WON_STATUSES = ["won", "warning_closed"];
|
|
4582
|
+
var CLOSED_STATUSES = [...DISPUTE_WON_STATUSES, "lost"];
|
|
4583
|
+
var TERMINAL = ["refunded", "refund_pending", "canceled", "declined", "restarting", "superseded"];
|
|
4584
|
+
var text3 = (value) => typeof value === "string" && value ? value : void 0;
|
|
4585
|
+
var refOf = (value) => typeof value === "string" ? text3(value) : text3(value?.id);
|
|
4586
|
+
async function readDisputedCharge(secretKey, chargeId, fetch2) {
|
|
4587
|
+
if (!/^ch_[A-Za-z0-9]+$/.test(chargeId)) membershipFail("invalid_dispute_charge");
|
|
4588
|
+
const config = { secretKey, ...fetch2 ? { fetch: fetch2 } : {} };
|
|
4589
|
+
const charge = await stripeRequest4(config, "GET", `/v1/charges/${chargeId}`);
|
|
4590
|
+
if (!charge.ok || charge.body.id !== chargeId) membershipFail("membership_dispute_provider_unavailable");
|
|
4591
|
+
const customerId = refOf(charge.body.customer), invoiceId = refOf(charge.body.invoice), paymentIntentId = refOf(charge.body.payment_intent);
|
|
4592
|
+
if (!customerId) membershipFail("membership_dispute_provider_unavailable");
|
|
4593
|
+
let subscriptionId;
|
|
4594
|
+
if (invoiceId) {
|
|
4595
|
+
const invoice = await stripeRequest4(config, "GET", `/v1/invoices/${invoiceId}`);
|
|
4596
|
+
if (!invoice.ok || invoice.body.id !== invoiceId) membershipFail("membership_dispute_provider_unavailable");
|
|
4597
|
+
subscriptionId = refOf(invoice.body.subscription) ?? refOf(invoice.body.parent?.subscription_details?.subscription);
|
|
4598
|
+
}
|
|
4599
|
+
const seatId = text3(charge.body.metadata?.seatId);
|
|
4600
|
+
return {
|
|
4601
|
+
chargeId,
|
|
4602
|
+
customerId,
|
|
4603
|
+
...invoiceId ? { invoiceId } : {},
|
|
4604
|
+
...subscriptionId ? { subscriptionId } : {},
|
|
4605
|
+
...paymentIntentId ? { paymentIntentId } : {},
|
|
4606
|
+
...seatId ? { seatId } : {}
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4609
|
+
function disputedChargeCoversMembership(charge, member) {
|
|
4610
|
+
return charge.customerId === member.providerCustomerId && !charge.seatId && (charge.invoiceId !== void 0 && charge.invoiceId === member.providerInitialInvoiceId || charge.subscriptionId !== void 0 && charge.subscriptionId === member.providerSubscriptionId || charge.paymentIntentId !== void 0 && charge.paymentIntentId === member.rechargePaymentIntentId);
|
|
4611
|
+
}
|
|
4612
|
+
async function reconcileMembershipDispute(db, input, now, secretKey, resolved) {
|
|
4613
|
+
membershipClock(now);
|
|
4614
|
+
const row2 = await membershipLoad(db, "networkCommercialEvents", `network-commercial-event:${input.eventId}`);
|
|
4615
|
+
const fact = row2?.fact;
|
|
4616
|
+
const evidenceGuard = await commercialEvidenceGuard(db, input.eventId, row2?.digest, "membership_billing_evidence_pending");
|
|
4617
|
+
if (!fact || fact.objectKind !== "dispute" || fact.eventId !== input.eventId || fact.digest !== row2.digest || !fact.chargeId || !fact.objectId || fact.livemode !== (input.environment === "prod")) membershipFail("membership_billing_evidence_pending");
|
|
4618
|
+
const id11 = await networkMembershipId(input);
|
|
4619
|
+
const receipt = (value, applied = value.disputeEventId === input.eventId) => ({
|
|
4620
|
+
membershipId: id11,
|
|
4621
|
+
status: String(value.status),
|
|
4622
|
+
termEndsAt: Number(value.termEndsAt),
|
|
4623
|
+
graceEndsAt: Number(value.graceEndsAt ?? 0),
|
|
4624
|
+
autoRenew: value.autoRenew === true,
|
|
4625
|
+
...text3(value.disputeStatus) ? { disputeStatus: text3(value.disputeStatus) } : {},
|
|
4626
|
+
applied
|
|
4627
|
+
});
|
|
4628
|
+
const known = await membershipLoad(db, "networkMembership", id11);
|
|
4629
|
+
requireMembershipScope(known, input);
|
|
4630
|
+
if (known.disputeEventId === input.eventId) return receipt(known);
|
|
4631
|
+
const charge = resolved && resolved.chargeId === fact.chargeId ? resolved : await readDisputedCharge(secretKey, fact.chargeId);
|
|
4632
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
4633
|
+
const state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id11);
|
|
4634
|
+
requireMembershipScope(member, input);
|
|
4635
|
+
if (charge.customerId !== member.providerCustomerId) membershipFail("membership_dispute_scope_mismatch");
|
|
4636
|
+
if (!disputedChargeCoversMembership(charge, member) || member.disputeEventId === input.eventId) return receipt(member);
|
|
4637
|
+
const status2 = String(fact.status ?? "unknown"), closed = CLOSED_STATUSES.includes(status2);
|
|
4638
|
+
const sameDispute = member.disputeId === fact.objectId;
|
|
4639
|
+
if (sameDispute && Number(member.disputeClosedAt ?? 0) > 0 && !closed) return receipt(member, false);
|
|
4640
|
+
const won = DISPUTE_WON_STATUSES.includes(status2);
|
|
4641
|
+
const suspended = member.status === MEMBERSHIP_DISPUTED_STATUS, terminal = TERMINAL.includes(String(member.status));
|
|
4642
|
+
const next = terminal ? String(member.status) : won ? suspended ? String(member.disputePriorStatus ?? (member.journeyApprovedAt ? "active" : "paid_pending_approval")) : String(member.status) : MEMBERSHIP_DISPUTED_STATUS;
|
|
4643
|
+
const attrs = {
|
|
4644
|
+
status: next,
|
|
4645
|
+
disputeId: fact.objectId,
|
|
4646
|
+
disputeStatus: status2,
|
|
4647
|
+
disputeEventId: input.eventId,
|
|
4648
|
+
disputeClosedAt: closed ? now : 0,
|
|
4649
|
+
...fact.disputeReason ? { disputeReason: fact.disputeReason } : {},
|
|
4650
|
+
...fact.amountCents !== void 0 ? { disputeAmountCents: fact.amountCents } : {},
|
|
4651
|
+
...sameDispute ? {} : { disputedAt: now },
|
|
4652
|
+
...next === MEMBERSHIP_DISPUTED_STATUS && !suspended ? { disputePriorStatus: String(member.status) } : {},
|
|
4653
|
+
revision: Number(member.revision) + 1,
|
|
4654
|
+
updatedAt: now
|
|
4655
|
+
};
|
|
4656
|
+
try {
|
|
4657
|
+
await db.transact([membershipUpdate("networkMembership", id11, attrs)], { mutationId: `membership-dispute:${id11}:${input.eventId}`, guards: [
|
|
4658
|
+
{ ns: "networkMembership", id: id11, exists: true, equals: { revision: member.revision } },
|
|
4659
|
+
{ ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } },
|
|
4660
|
+
{ ns: "networkCommercialEvents", id: String(row2.id), exists: true, equals: { digest: fact.digest } },
|
|
4661
|
+
evidenceGuard
|
|
4662
|
+
] });
|
|
4663
|
+
} catch (error) {
|
|
4664
|
+
if (error.code !== "transact_guard_failed") throw error;
|
|
4665
|
+
continue;
|
|
4666
|
+
}
|
|
4667
|
+
const saved = await membershipLoad(db, "networkMembership", id11);
|
|
4668
|
+
requireMembershipScope(saved, input);
|
|
4669
|
+
return receipt(saved, true);
|
|
4670
|
+
}
|
|
4671
|
+
return membershipFail("membership_dispute_retry_required");
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4569
4674
|
// src/membership-provider-lifecycle.ts
|
|
4570
4675
|
async function reconcileMembershipBilling(db, input, now, stripeSecretName = "stripe_secret_key") {
|
|
4571
4676
|
membershipClock(now);
|
|
4572
4677
|
const row2 = await membershipLoad(db, "networkCommercialEvents", `network-commercial-event:${input.eventId}`);
|
|
4573
4678
|
const fact = row2?.fact;
|
|
4574
4679
|
if (fact?.objectKind === "invoice") return reconcileMembershipRenewal(db, input, now);
|
|
4680
|
+
if (fact?.objectKind === "dispute") {
|
|
4681
|
+
const secret = await getVaultSecret(db, stripeSecretName);
|
|
4682
|
+
if (!secret) membershipFail("membership_billing_provider_unavailable");
|
|
4683
|
+
return reconcileMembershipDispute(db, input, now, secret);
|
|
4684
|
+
}
|
|
4575
4685
|
const evidenceGuard = await commercialEvidenceGuard(db, input.eventId, row2?.digest, "membership_billing_evidence_pending");
|
|
4576
4686
|
if (!fact || fact.eventId !== input.eventId || fact.digest !== row2.digest || !/^[a-f0-9]{64}$/.test(fact.digest) || !["customer.subscription.updated", "customer.subscription.deleted", "charge.refunded"].includes(fact.eventType) || fact.livemode !== (input.environment === "prod") || fact.applicationId !== input.applicationId || !fact.identity || Object.entries({
|
|
4577
4687
|
appId: input.appId,
|
|
@@ -4614,7 +4724,7 @@ async function reconcileMembershipBilling(db, input, now, stripeSecretName = "st
|
|
|
4614
4724
|
}
|
|
4615
4725
|
if (fact.subscriptionId !== member.providerSubscriptionId || fact.objectKind !== "subscription") membershipFail("membership_billing_scope_mismatch");
|
|
4616
4726
|
if (["refund_pending", "refunded", "declined", "canceled", "restarting"].includes(String(member.status))) return receipt();
|
|
4617
|
-
const result = await
|
|
4727
|
+
const result = await stripeRequest5({ secretKey: sk }, "GET", `/v1/subscriptions/${encodeURIComponent(String(member.providerSubscriptionId))}`);
|
|
4618
4728
|
const sub = result.body;
|
|
4619
4729
|
if (!result.ok || sub.id !== member.providerSubscriptionId || sub.customer !== member.providerCustomerId || sub.livemode !== (input.environment === "prod") || typeof sub.cancel_at_period_end !== "boolean" || !["active", "trialing", "past_due", "unpaid", "canceled"].includes(sub.status) || !Object.entries({ ...stripePartitionMetadata(input), applicationId: input.applicationId }).every(([key3, value]) => sub.metadata?.[key3] === value)) {
|
|
4620
4730
|
membershipFail("membership_billing_provider_mismatch");
|
|
@@ -4671,7 +4781,7 @@ async function expireRestartedMembershipSeat(db, target, member, now) {
|
|
|
4671
4781
|
}
|
|
4672
4782
|
|
|
4673
4783
|
// src/membership-restart-authority.ts
|
|
4674
|
-
import { stripeRequest as
|
|
4784
|
+
import { stripeRequest as stripeRequest6 } from "@odla-ai/stripe";
|
|
4675
4785
|
|
|
4676
4786
|
// src/stripe-event-digest.ts
|
|
4677
4787
|
async function stripePayloadDigest(value) {
|
|
@@ -4686,14 +4796,14 @@ function membershipCanRestart(member, now) {
|
|
|
4686
4796
|
}
|
|
4687
4797
|
async function stopPreviousSubscription(member, target, secret) {
|
|
4688
4798
|
const path = `/v1/subscriptions/${encodeURIComponent(String(member.providerSubscriptionId))}`;
|
|
4689
|
-
const result = await
|
|
4799
|
+
const result = await stripeRequest6({ secretKey: secret }, "GET", path);
|
|
4690
4800
|
const sub = result.body;
|
|
4691
4801
|
if (!result.ok || sub.id !== member.providerSubscriptionId || sub.customer !== member.providerCustomerId || sub.livemode !== (target.environment === "prod") || !Object.entries({
|
|
4692
4802
|
...stripePartitionMetadata(target),
|
|
4693
4803
|
applicationId: target.applicationId
|
|
4694
4804
|
}).every(([key3, value]) => sub.metadata?.[key3] === value)) membershipFail("membership_restart_provider_mismatch");
|
|
4695
4805
|
if (sub.status === "canceled") return;
|
|
4696
|
-
const canceled = await
|
|
4806
|
+
const canceled = await stripeRequest6(
|
|
4697
4807
|
{ secretKey: secret },
|
|
4698
4808
|
"DELETE",
|
|
4699
4809
|
path,
|
|
@@ -4794,16 +4904,16 @@ async function verifyMembershipAccessReceipt(value, input, now) {
|
|
|
4794
4904
|
}
|
|
4795
4905
|
|
|
4796
4906
|
// src/membership-seat-stripe.ts
|
|
4797
|
-
import { stripeRequest as
|
|
4907
|
+
import { stripeRequest as stripeRequest8 } from "@odla-ai/stripe";
|
|
4798
4908
|
|
|
4799
4909
|
// src/subscription-provider-contract.ts
|
|
4800
|
-
import { stripeRequest as
|
|
4910
|
+
import { stripeRequest as stripeRequest7 } from "@odla-ai/stripe";
|
|
4801
4911
|
function subscriptionContractMismatch() {
|
|
4802
4912
|
throw Object.assign(new Error("Stripe subscription does not match the checkout contract"), { code: "stripe_contract_mismatch" });
|
|
4803
4913
|
}
|
|
4804
4914
|
async function verifySubscriptionPrice(secretKey, input, resume = false) {
|
|
4805
4915
|
if (!/^price_[A-Za-z0-9]+$/.test(input.priceId) || !Number.isSafeInteger(input.priceCents) || input.priceCents <= 0 || !/^[a-z]{3}$/.test(input.currency) || !["month", "year"].includes(input.interval)) subscriptionContractMismatch();
|
|
4806
|
-
const result = await
|
|
4916
|
+
const result = await stripeRequest7({ secretKey }, "GET", `/v1/prices/${encodeURIComponent(input.priceId)}`);
|
|
4807
4917
|
const price = result.body;
|
|
4808
4918
|
if (!result.ok || !price || price.id !== input.priceId || !resume && price.active !== true || price.unit_amount !== input.priceCents || price.currency !== input.currency || price.type !== "recurring" || price.billing_scheme !== "per_unit" || price.custom_unit_amount || price.transform_quantity || price.recurring?.interval !== input.interval || price.recurring?.interval_count !== 1 || price.recurring?.usage_type !== "licensed" || price.livemode !== (input.environment === "prod")) subscriptionContractMismatch();
|
|
4809
4919
|
return typeof price.product === "string" ? price.product : void 0;
|
|
@@ -4821,7 +4931,7 @@ var metadataFor = (seat) => ({
|
|
|
4821
4931
|
});
|
|
4822
4932
|
var matches = (value, expected) => value && Object.entries(expected).every(([key3, item]) => value[key3] === item);
|
|
4823
4933
|
async function read(sk, path, params) {
|
|
4824
|
-
const response = await
|
|
4934
|
+
const response = await stripeRequest8({ secretKey: sk }, "GET", path, params);
|
|
4825
4935
|
if (!response.ok) return reject3();
|
|
4826
4936
|
return response.body;
|
|
4827
4937
|
}
|
|
@@ -4858,11 +4968,11 @@ async function createNamedSeatStripePayment(sk, seat, now) {
|
|
|
4858
4968
|
}
|
|
4859
4969
|
await subscriptionForSeat(sk, seat);
|
|
4860
4970
|
const metadata = metadataFor(seat);
|
|
4861
|
-
const result = seat.providerPaymentIntentId ? await
|
|
4971
|
+
const result = seat.providerPaymentIntentId ? await stripeRequest8(
|
|
4862
4972
|
{ secretKey: sk },
|
|
4863
4973
|
"GET",
|
|
4864
4974
|
`/v1/payment_intents/${encodeURIComponent(seat.providerPaymentIntentId)}`
|
|
4865
|
-
) : await
|
|
4975
|
+
) : await stripeRequest8({ secretKey: sk }, "POST", "/v1/payment_intents", {
|
|
4866
4976
|
amount: seat.amountCents,
|
|
4867
4977
|
currency: seat.currency,
|
|
4868
4978
|
customer: seat.providerCustomerId,
|
|
@@ -4884,7 +4994,7 @@ async function attachNamedSeatStripeRenewal(sk, seat) {
|
|
|
4884
4994
|
return String(existing.id);
|
|
4885
4995
|
}
|
|
4886
4996
|
if (seat.combinedInvoice) reject3();
|
|
4887
|
-
const result = await
|
|
4997
|
+
const result = await stripeRequest8({ secretKey: sk }, "POST", "/v1/subscription_items", {
|
|
4888
4998
|
subscription: seat.providerSubscriptionId,
|
|
4889
4999
|
price: seat.priceId,
|
|
4890
5000
|
quantity: 1,
|
|
@@ -5159,10 +5269,10 @@ async function recordRecipient(db, input, now, approve) {
|
|
|
5159
5269
|
}
|
|
5160
5270
|
|
|
5161
5271
|
// src/membership-seat-refund-stripe.ts
|
|
5162
|
-
import { stripeRequest as
|
|
5272
|
+
import { stripeRequest as stripeRequest10 } from "@odla-ai/stripe";
|
|
5163
5273
|
|
|
5164
5274
|
// src/membership-checkout-seat-refund.ts
|
|
5165
|
-
import { stripeRequest as
|
|
5275
|
+
import { stripeRequest as stripeRequest9 } from "@odla-ai/stripe";
|
|
5166
5276
|
var fail4 = (code = "named_seat_refund_mismatch") => {
|
|
5167
5277
|
throw Object.assign(new Error(code), { code });
|
|
5168
5278
|
};
|
|
@@ -5171,7 +5281,7 @@ async function refundCheckoutNamedSeat(secretKey, seat) {
|
|
|
5171
5281
|
const invoiceRef = seat.combinedInvoice;
|
|
5172
5282
|
if (!invoiceRef || !/^in_[A-Za-z0-9_]+$/.test(invoiceRef.invoiceId) || !Number.isSafeInteger(invoiceRef.totalCents) || invoiceRef.totalCents <= seat.amountCents || seat.amountCents <= 0 || seat.status !== "refund_pending") fail4();
|
|
5173
5283
|
const call = async (method, path, params, key3) => {
|
|
5174
|
-
const result = await
|
|
5284
|
+
const result = await stripeRequest9({ secretKey }, method, path, params, key3 ? { idempotencyKey: key3 } : {});
|
|
5175
5285
|
if (!result.ok || !result.body) return fail4("named_seat_refund_provider_unavailable");
|
|
5176
5286
|
return result.body;
|
|
5177
5287
|
};
|
|
@@ -5244,7 +5354,7 @@ async function refundNamedSeatStripePayment(secretKey, seat) {
|
|
|
5244
5354
|
if (seat.combinedInvoice) return refundCheckoutNamedSeat(secretKey, seat);
|
|
5245
5355
|
if (!seat.providerPaymentIntentId || seat.status !== "refund_pending") fail5();
|
|
5246
5356
|
const call = async (method, path, params, idempotencyKey) => {
|
|
5247
|
-
const result = await
|
|
5357
|
+
const result = await stripeRequest10({ secretKey }, method, path, params, idempotencyKey ? { idempotencyKey } : {});
|
|
5248
5358
|
if (!result.ok || !result.body) return fail5("named_seat_refund_provider_unavailable");
|
|
5249
5359
|
return result.body;
|
|
5250
5360
|
};
|
|
@@ -5946,7 +6056,7 @@ async function readBoundedJson(response, signal, maxBytes, label) {
|
|
|
5946
6056
|
};
|
|
5947
6057
|
signal.addEventListener("abort", cancel, { once: true });
|
|
5948
6058
|
let size = 0;
|
|
5949
|
-
let
|
|
6059
|
+
let text7 = "";
|
|
5950
6060
|
const decoder = new TextDecoder();
|
|
5951
6061
|
try {
|
|
5952
6062
|
while (true) {
|
|
@@ -5956,10 +6066,10 @@ async function readBoundedJson(response, signal, maxBytes, label) {
|
|
|
5956
6066
|
if (done) break;
|
|
5957
6067
|
size += value.byteLength;
|
|
5958
6068
|
if (size > maxBytes) throw new TypeError(`${label} exceeds limit`);
|
|
5959
|
-
|
|
6069
|
+
text7 += decoder.decode(value, { stream: true });
|
|
5960
6070
|
}
|
|
5961
6071
|
try {
|
|
5962
|
-
return JSON.parse(
|
|
6072
|
+
return JSON.parse(text7 + decoder.decode());
|
|
5963
6073
|
} catch {
|
|
5964
6074
|
throw new TypeError(`invalid ${label}`);
|
|
5965
6075
|
}
|
|
@@ -6173,7 +6283,7 @@ async function requestMembershipRefund(options) {
|
|
|
6173
6283
|
async function requestMembershipRenewal(options) {
|
|
6174
6284
|
const result = await requestAuthority(options, "renewals", { applicationId: options.applicationId, skuId: options.skuId, eventId: options.eventId });
|
|
6175
6285
|
const row2 = result.renewal;
|
|
6176
|
-
if (!row2 || row2.membershipId !== await networkMembershipId(options) || !Number.isSafeInteger(row2.termEndsAt) || row2.termEndsAt <= 0 || !Number.isSafeInteger(row2.graceEndsAt) || row2.graceEndsAt < 0 || typeof row2.autoRenew !== "boolean" || !["active", "paid_pending_approval", "cancel_at_term_end", "payment_grace", "lapsed", "refund_pending", "refunded", "canceled", "declined", "restarting"].includes(row2.status)) {
|
|
6286
|
+
if (!row2 || row2.membershipId !== await networkMembershipId(options) || !Number.isSafeInteger(row2.termEndsAt) || row2.termEndsAt <= 0 || !Number.isSafeInteger(row2.graceEndsAt) || row2.graceEndsAt < 0 || typeof row2.autoRenew !== "boolean" || !["active", "paid_pending_approval", "cancel_at_term_end", "payment_grace", "lapsed", "refund_pending", "refunded", "canceled", "declined", "restarting", "payment_disputed"].includes(row2.status)) {
|
|
6177
6287
|
throw new Error("invalid membership renewal receipt");
|
|
6178
6288
|
}
|
|
6179
6289
|
return { membershipId: row2.membershipId, status: row2.status, termEndsAt: row2.termEndsAt, graceEndsAt: row2.graceEndsAt, autoRenew: row2.autoRenew };
|
|
@@ -6233,7 +6343,7 @@ async function requestMembershipCancellation(options) {
|
|
|
6233
6343
|
}
|
|
6234
6344
|
|
|
6235
6345
|
// src/membership-seat-cancel.ts
|
|
6236
|
-
import { initStripe, stripeRequest as
|
|
6346
|
+
import { initStripe, stripeRequest as stripeRequest11 } from "@odla-ai/stripe";
|
|
6237
6347
|
async function cancelUnpaidNamedSeat(db, target, secretKey, now) {
|
|
6238
6348
|
membershipClock(now);
|
|
6239
6349
|
const state = await loadMembershipAuthorityState(db, target);
|
|
@@ -6261,7 +6371,7 @@ async function cancelUnpaidNamedSeat(db, target, secretKey, now) {
|
|
|
6261
6371
|
}
|
|
6262
6372
|
const metadata = { ...stripePartitionMetadata(target), applicationId: target.applicationId, membershipId: seat.primaryMembershipId, seatId: seat.id };
|
|
6263
6373
|
const request = async (method, path, params) => {
|
|
6264
|
-
const result = await
|
|
6374
|
+
const result = await stripeRequest11(
|
|
6265
6375
|
{ secretKey },
|
|
6266
6376
|
method,
|
|
6267
6377
|
path,
|
|
@@ -6485,7 +6595,20 @@ var membershipAuthorityEntities = {
|
|
|
6485
6595
|
graceEndsAt: attr9("number", true),
|
|
6486
6596
|
...times(),
|
|
6487
6597
|
refundRequestedAt: attr9("number", true),
|
|
6488
|
-
restartId: key2(true)
|
|
6598
|
+
restartId: key2(true),
|
|
6599
|
+
// A card-network dispute on this membership's payment: Stripe's dispute id and status, the reason the cardholder gave,
|
|
6600
|
+
// the status the membership held before it was suspended, and the re-issued charge once an operator collected again.
|
|
6601
|
+
disputeId: key2(true),
|
|
6602
|
+
disputeStatus: attr9("string", true),
|
|
6603
|
+
disputeReason: attr9("string", true),
|
|
6604
|
+
disputeAmountCents: attr9("number", true),
|
|
6605
|
+
disputedAt: attr9("number", true),
|
|
6606
|
+
disputeClosedAt: attr9("number", true),
|
|
6607
|
+
disputePriorStatus: attr9("string", true),
|
|
6608
|
+
disputeEventId: key2(true),
|
|
6609
|
+
rechargePaymentIntentId: key2(true),
|
|
6610
|
+
rechargedAt: attr9("number", true),
|
|
6611
|
+
rechargeAttempt: attr9("json", true)
|
|
6489
6612
|
} },
|
|
6490
6613
|
membershipRestart: { attrs: {
|
|
6491
6614
|
id: id10(),
|
|
@@ -6921,7 +7044,7 @@ var record2 = (value) => value !== null && typeof value === "object" && !Array.i
|
|
|
6921
7044
|
function unknownKeys2(value, allowed2, path, errors) {
|
|
6922
7045
|
for (const key3 of Object.keys(value)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
|
|
6923
7046
|
}
|
|
6924
|
-
function
|
|
7047
|
+
function text4(value, path, errors, max = MAX_TEXT2, required = true) {
|
|
6925
7048
|
if (value === void 0 && !required) return void 0;
|
|
6926
7049
|
if (typeof value !== "string" || required && !value.trim()) {
|
|
6927
7050
|
errors.push(`${path} must be a non-empty string`);
|
|
@@ -6947,7 +7070,7 @@ function scopeValue(value, path, errors) {
|
|
|
6947
7070
|
return { kind: raw2.kind };
|
|
6948
7071
|
}
|
|
6949
7072
|
if (raw2.kind === "chapter") {
|
|
6950
|
-
const chapterId =
|
|
7073
|
+
const chapterId = text4(raw2.chapterId, `${path}.chapterId`, errors, 63) ?? "";
|
|
6951
7074
|
if (chapterId && !APP_ID.test(chapterId)) errors.push(`${path}.chapterId must be a lowercase app slug`);
|
|
6952
7075
|
return { kind: "chapter", chapterId };
|
|
6953
7076
|
}
|
|
@@ -6961,22 +7084,22 @@ function resourceValue(value, path, errors) {
|
|
|
6961
7084
|
return null;
|
|
6962
7085
|
}
|
|
6963
7086
|
unknownKeys2(raw2, RESOURCE_KEYS, path, errors);
|
|
6964
|
-
const id11 =
|
|
7087
|
+
const id11 = text4(raw2.id, `${path}.id`, errors, 80) ?? "";
|
|
6965
7088
|
if (id11 && !ID2.test(id11)) errors.push(`${path}.id must be lowercase kebab-case`);
|
|
6966
|
-
const kind =
|
|
7089
|
+
const kind = text4(raw2.kind, `${path}.kind`, errors) ?? "";
|
|
6967
7090
|
if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
|
|
6968
|
-
const title =
|
|
6969
|
-
const summary =
|
|
6970
|
-
const status2 =
|
|
7091
|
+
const title = text4(raw2.title, `${path}.title`, errors, 240) ?? "";
|
|
7092
|
+
const summary = text4(raw2.summary, `${path}.summary`, errors, 1e3, false);
|
|
7093
|
+
const status2 = text4(raw2.status, `${path}.status`, errors) ?? "";
|
|
6971
7094
|
if (!STATUSES.has(status2)) errors.push(`${path}.status must be draft, published, or archived`);
|
|
6972
7095
|
const revision = integer3(raw2.revision, `${path}.revision`, errors);
|
|
6973
7096
|
const scope2 = scopeValue(raw2.scope, `${path}.scope`, errors) ?? { kind: "global" };
|
|
6974
|
-
const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) =>
|
|
7097
|
+
const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) => text4(tag, `${path}.tags[${index2}]`, errors, 80) ?? "") : [];
|
|
6975
7098
|
if (!Array.isArray(raw2.tags)) errors.push(`${path}.tags must be an array`);
|
|
6976
7099
|
if (tags.length > MAX_TAGS) errors.push(`${path}.tags may contain at most ${MAX_TAGS} items`);
|
|
6977
7100
|
if (new Set(tags).size !== tags.length) errors.push(`${path}.tags must be unique`);
|
|
6978
|
-
const bodyMarkdown =
|
|
6979
|
-
const url =
|
|
7101
|
+
const bodyMarkdown = text4(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, false);
|
|
7102
|
+
const url = text4(raw2.url, `${path}.url`, errors, 2048, false);
|
|
6980
7103
|
if (url) {
|
|
6981
7104
|
try {
|
|
6982
7105
|
if (new URL(url).protocol !== "https:") errors.push(`${path}.url must use https`);
|
|
@@ -6988,9 +7111,9 @@ function resourceValue(value, path, errors) {
|
|
|
6988
7111
|
if (raw2.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
|
|
6989
7112
|
if (asset) unknownKeys2(asset, ASSET_KEYS, `${path}.brandAsset`, errors);
|
|
6990
7113
|
const brandAsset = asset ? {
|
|
6991
|
-
bookId:
|
|
6992
|
-
assetId:
|
|
6993
|
-
...asset.filename === void 0 ? {} : { filename:
|
|
7114
|
+
bookId: text4(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
|
|
7115
|
+
assetId: text4(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
|
|
7116
|
+
...asset.filename === void 0 ? {} : { filename: text4(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
|
|
6994
7117
|
} : void 0;
|
|
6995
7118
|
if ((kind === "document" || kind === "note") && !bodyMarkdown) errors.push(`${path}.bodyMarkdown is required for ${kind}`);
|
|
6996
7119
|
if ((kind === "link" || kind === "inspiration") && !url) errors.push(`${path}.url is required for ${kind}`);
|
|
@@ -7005,17 +7128,17 @@ function resourceValue(value, path, errors) {
|
|
|
7005
7128
|
if (provenanceRaw?.promotedFrom !== void 0 && !promotedRaw) errors.push(`${path}.provenance.promotedFrom must be an object`);
|
|
7006
7129
|
if (promotedRaw) unknownKeys2(promotedRaw, PROMOTION_KEYS, `${path}.provenance.promotedFrom`, errors);
|
|
7007
7130
|
const promotedFrom = promotedRaw ? {
|
|
7008
|
-
chapterId:
|
|
7009
|
-
resourceId:
|
|
7131
|
+
chapterId: text4(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
|
|
7132
|
+
resourceId: text4(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
|
|
7010
7133
|
resourceRevision: integer3(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
|
|
7011
7134
|
documentRevision: integer3(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
|
|
7012
|
-
promotedBy:
|
|
7135
|
+
promotedBy: text4(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
|
|
7013
7136
|
promotedAt: integer3(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
|
|
7014
7137
|
} : void 0;
|
|
7015
7138
|
const provenance = {
|
|
7016
|
-
createdBy:
|
|
7139
|
+
createdBy: text4(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
|
|
7017
7140
|
createdAt: integer3(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
|
|
7018
|
-
updatedBy:
|
|
7141
|
+
updatedBy: text4(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
|
|
7019
7142
|
updatedAt: integer3(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
|
|
7020
7143
|
...promotedFrom ? { promotedFrom } : {}
|
|
7021
7144
|
};
|
|
@@ -7034,7 +7157,7 @@ function validateBuilderLibraryDocument(value, options = {}) {
|
|
|
7034
7157
|
if (sourceRaw) unknownKeys2(sourceRaw, SOURCE_KEYS2, "document.source", errors);
|
|
7035
7158
|
const sourceType = sourceRaw?.type;
|
|
7036
7159
|
if (sourceType !== "chapter" && sourceType !== "built_not_found") errors.push("document.source.type must be chapter or built_not_found");
|
|
7037
|
-
const sourceAppId =
|
|
7160
|
+
const sourceAppId = text4(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
|
|
7038
7161
|
if (sourceAppId && !APP_ID.test(sourceAppId)) errors.push("document.source.appId must be a lowercase app slug");
|
|
7039
7162
|
if (options.sourceType && sourceType !== options.sourceType) errors.push(`document.source.type must be ${options.sourceType}`);
|
|
7040
7163
|
if (options.sourceAppId && sourceAppId !== options.sourceAppId) errors.push(`document.source.appId must be ${options.sourceAppId}`);
|
|
@@ -7042,8 +7165,8 @@ function validateBuilderLibraryDocument(value, options = {}) {
|
|
|
7042
7165
|
if (raw2.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
|
|
7043
7166
|
if (targetRaw) unknownKeys2(targetRaw, TARGET_KEYS2, "document.target", errors);
|
|
7044
7167
|
const target = targetRaw ? {
|
|
7045
|
-
appId:
|
|
7046
|
-
environment:
|
|
7168
|
+
appId: text4(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
|
|
7169
|
+
environment: text4(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
|
|
7047
7170
|
} : void 0;
|
|
7048
7171
|
if (options.targetAppId && target?.appId !== options.targetAppId) errors.push(`document.target.appId must be ${options.targetAppId}`);
|
|
7049
7172
|
if (!Array.isArray(raw2.resources)) errors.push("document.resources must be an array");
|
|
@@ -7138,9 +7261,9 @@ async function createBuilderLibraryEnvelope(document) {
|
|
|
7138
7261
|
}
|
|
7139
7262
|
|
|
7140
7263
|
// src/email-retirement.ts
|
|
7141
|
-
function isRetiredEmailCopy(subject,
|
|
7264
|
+
function isRetiredEmailCopy(subject, text7) {
|
|
7142
7265
|
const subjectCopy = subject.trimStart().replace(/^\[dev\]\s*/i, "");
|
|
7143
|
-
const bodyCopy =
|
|
7266
|
+
const bodyCopy = text7.trimStart().replace(/^\(dev redirect; original recipient: [^\r\n]*\)\r?\n\r?\n/, "");
|
|
7144
7267
|
return /^\(\s*unused\s*:/i.test(subjectCopy) || /^Unused\.\s*Switched off\b/i.test(bodyCopy);
|
|
7145
7268
|
}
|
|
7146
7269
|
|
|
@@ -7218,10 +7341,10 @@ function planDelivery(input) {
|
|
|
7218
7341
|
if (isRetiredEmailCopy(renderedSubject, renderedText)) return { deliver: false, reason: "template-retired" };
|
|
7219
7342
|
if (validateEmailCopy({ subject: renderedSubject, text: renderedText }, { allowedVariables: [] }).length) return { deliver: false, reason: "template-invalid" };
|
|
7220
7343
|
const subject = (redirect ? "[dev] " : "") + renderedSubject;
|
|
7221
|
-
const
|
|
7344
|
+
const text7 = redirect ? `(dev redirect; original recipient: ${input.to})
|
|
7222
7345
|
|
|
7223
7346
|
` + renderedText : renderedText;
|
|
7224
|
-
return { deliver: true, transport, to, subject, text:
|
|
7347
|
+
return { deliver: true, transport, to, subject, text: text7, redirected: redirect };
|
|
7225
7348
|
}
|
|
7226
7349
|
|
|
7227
7350
|
// src/email-readiness.ts
|
|
@@ -7556,6 +7679,17 @@ function findGiftRef(obj) {
|
|
|
7556
7679
|
}
|
|
7557
7680
|
function normalizeWebhookEvent(event2, options = {}) {
|
|
7558
7681
|
const obj = event2.data?.object ?? {};
|
|
7682
|
+
if (event2.type.startsWith("charge.dispute.")) {
|
|
7683
|
+
const chargeId = typeof obj.charge === "string" ? obj.charge : String(obj.charge?.id ?? "");
|
|
7684
|
+
if (!/^ch_[A-Za-z0-9]+$/.test(chargeId) || typeof obj.id !== "string" || !obj.id) return { kind: "ignored", type: event2.type };
|
|
7685
|
+
return {
|
|
7686
|
+
kind: "disputed",
|
|
7687
|
+
chargeId,
|
|
7688
|
+
disputeId: obj.id,
|
|
7689
|
+
disputeStatus: typeof obj.status === "string" ? obj.status : "unknown",
|
|
7690
|
+
...typeof obj.payment_intent === "string" ? { paymentIntentId: obj.payment_intent } : {}
|
|
7691
|
+
};
|
|
7692
|
+
}
|
|
7559
7693
|
if (event2.type === "charge.refunded" && (!Number.isSafeInteger(obj.amount) || Number(obj.amount) <= 0 || obj.amount_refunded !== obj.amount || obj.refunded !== true)) return { kind: "ignored", type: event2.type };
|
|
7560
7694
|
const gift = findGiftRef(obj);
|
|
7561
7695
|
if (gift.giftIntentId) {
|
|
@@ -7866,7 +8000,7 @@ var ID4 = /^[a-z][a-z0-9-]{1,79}$/;
|
|
|
7866
8000
|
var PRICE = /^price_[A-Za-z0-9]{12,}$/;
|
|
7867
8001
|
var CURRENCY = /^[a-z]{3}$/;
|
|
7868
8002
|
var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
7869
|
-
var
|
|
8003
|
+
var text5 = (value, path, errors, max) => {
|
|
7870
8004
|
if (typeof value !== "string" || !value.trim()) {
|
|
7871
8005
|
errors.push(`${path} is required`);
|
|
7872
8006
|
return "";
|
|
@@ -7894,23 +8028,23 @@ function validateGiftOfferCatalog(input, tierIds) {
|
|
|
7894
8028
|
}
|
|
7895
8029
|
const allowed2 = /* @__PURE__ */ new Set(["id", "target", "billingMode", "stripePriceId", "priceCents", "currency", "redemptionLifetimeHours", "activeFrom", "activeUntil", "title", "description", "ctaLabel"]);
|
|
7896
8030
|
for (const key3 of Object.keys(item)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
|
|
7897
|
-
const id11 =
|
|
8031
|
+
const id11 = text5(item.id, `${path}.id`, errors, 80);
|
|
7898
8032
|
if (id11 && !ID4.test(id11)) errors.push(`${path}.id must be a lowercase kebab-case id`);
|
|
7899
8033
|
if (ids2.has(id11)) errors.push(`${path}.id duplicates "${id11}"`);
|
|
7900
8034
|
ids2.add(id11);
|
|
7901
8035
|
const targetRaw = record3(item.target);
|
|
7902
8036
|
let target = { type: "entitlement", entitlementKey: "invalid" };
|
|
7903
8037
|
if (targetRaw?.type === "tier") {
|
|
7904
|
-
const tierId =
|
|
8038
|
+
const tierId = text5(targetRaw.tierId, `${path}.target.tierId`, errors, 80);
|
|
7905
8039
|
if (!tierIds.has(tierId)) errors.push(`${path}.target.tierId must reference a declared tier`);
|
|
7906
8040
|
target = { type: "tier", tierId };
|
|
7907
8041
|
} else if (targetRaw?.type === "entitlement") {
|
|
7908
|
-
const entitlementKey =
|
|
8042
|
+
const entitlementKey = text5(targetRaw.entitlementKey, `${path}.target.entitlementKey`, errors, 80);
|
|
7909
8043
|
if (entitlementKey && !ID4.test(entitlementKey)) errors.push(`${path}.target.entitlementKey must be a lowercase kebab-case id`);
|
|
7910
8044
|
target = { type: "entitlement", entitlementKey };
|
|
7911
8045
|
} else errors.push(`${path}.target.type must be tier or entitlement`);
|
|
7912
8046
|
if (item.billingMode !== "one_time" && item.billingMode !== "subscription") errors.push(`${path}.billingMode must be one_time or subscription`);
|
|
7913
|
-
const stripePriceId =
|
|
8047
|
+
const stripePriceId = text5(item.stripePriceId, `${path}.stripePriceId`, errors, 128);
|
|
7914
8048
|
if (stripePriceId && !PRICE.test(stripePriceId)) errors.push(`${path}.stripePriceId must be a real Stripe Price id`);
|
|
7915
8049
|
if (!Number.isSafeInteger(item.priceCents) || Number(item.priceCents) < 1 || Number(item.priceCents) > 1e8) errors.push(`${path}.priceCents must be an integer from 1 to 100,000,000`);
|
|
7916
8050
|
const currency = typeof item.currency === "string" ? item.currency.trim().toLowerCase() : "";
|
|
@@ -7932,9 +8066,9 @@ function validateGiftOfferCatalog(input, tierIds) {
|
|
|
7932
8066
|
redemptionLifetimeHours: lifetime,
|
|
7933
8067
|
...activeFrom !== void 0 ? { activeFrom } : {},
|
|
7934
8068
|
...activeUntil !== void 0 ? { activeUntil } : {},
|
|
7935
|
-
title:
|
|
7936
|
-
description:
|
|
7937
|
-
ctaLabel:
|
|
8069
|
+
title: text5(item.title, `${path}.title`, errors, 160),
|
|
8070
|
+
description: text5(item.description, `${path}.description`, errors, 2e3),
|
|
8071
|
+
ctaLabel: text5(item.ctaLabel, `${path}.ctaLabel`, errors, 120)
|
|
7938
8072
|
});
|
|
7939
8073
|
}
|
|
7940
8074
|
return errors.length ? { ok: false, errors } : { ok: true, value: { schemaVersion: 1, offers } };
|
|
@@ -8398,9 +8532,9 @@ function subAnnualCents(sub) {
|
|
|
8398
8532
|
}
|
|
8399
8533
|
|
|
8400
8534
|
// src/payments-stripe.ts
|
|
8401
|
-
import { stripeRequest as
|
|
8535
|
+
import { stripeRequest as stripeRequest12, verifyStripeSignature as verifyStripeSignature2 } from "@odla-ai/stripe";
|
|
8402
8536
|
async function stripeCall(sk, method, path, params, idempotencyKey) {
|
|
8403
|
-
return
|
|
8537
|
+
return stripeRequest12(
|
|
8404
8538
|
{ secretKey: sk },
|
|
8405
8539
|
method,
|
|
8406
8540
|
path,
|
|
@@ -9022,10 +9156,10 @@ function boundedString(value, path, errors, max, required = false) {
|
|
|
9022
9156
|
errors.push(`${path} must be a string`);
|
|
9023
9157
|
return void 0;
|
|
9024
9158
|
}
|
|
9025
|
-
const
|
|
9026
|
-
if (required && !
|
|
9027
|
-
if (
|
|
9028
|
-
return
|
|
9159
|
+
const text7 = value.trim();
|
|
9160
|
+
if (required && !text7) errors.push(`${path} is required`);
|
|
9161
|
+
if (text7.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
|
|
9162
|
+
return text7;
|
|
9029
9163
|
}
|
|
9030
9164
|
function validateMembershipPageDocument(input, options = {}) {
|
|
9031
9165
|
const errors = [];
|
|
@@ -9074,8 +9208,8 @@ function validateMembershipPageDocument(input, options = {}) {
|
|
|
9074
9208
|
else {
|
|
9075
9209
|
if (item.benefits.length > MAX_BENEFITS) errors.push(`${path}.benefits supports at most ${MAX_BENEFITS} entries`);
|
|
9076
9210
|
benefits = item.benefits.slice(0, MAX_BENEFITS).flatMap((benefit, benefitIndex) => {
|
|
9077
|
-
const
|
|
9078
|
-
return
|
|
9211
|
+
const text7 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
|
|
9212
|
+
return text7 ? [text7] : [];
|
|
9079
9213
|
});
|
|
9080
9214
|
}
|
|
9081
9215
|
}
|
|
@@ -9174,7 +9308,7 @@ var recordOf = (value) => value !== null && typeof value === "object" && !Array.
|
|
|
9174
9308
|
function unknownKeys3(value, allowed2, path, errors) {
|
|
9175
9309
|
for (const key3 of Object.keys(value)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
|
|
9176
9310
|
}
|
|
9177
|
-
function
|
|
9311
|
+
function text6(value, path, errors, max = 65536) {
|
|
9178
9312
|
if (typeof value !== "string" || !value.trim()) {
|
|
9179
9313
|
errors.push(`${path} is required`);
|
|
9180
9314
|
return "";
|
|
@@ -9197,11 +9331,11 @@ function tiersOf(value, errors) {
|
|
|
9197
9331
|
return [];
|
|
9198
9332
|
}
|
|
9199
9333
|
unknownKeys3(item, TIER_KEYS2, path, errors);
|
|
9200
|
-
const id11 =
|
|
9334
|
+
const id11 = text6(item.id, `${path}.id`, errors, 80);
|
|
9201
9335
|
if (id11 && !TIER.test(id11)) errors.push(`${path}.id must be a lowercase kebab-case id`);
|
|
9202
9336
|
if (seen.has(id11)) errors.push(`${path}.id duplicates "${id11}"`);
|
|
9203
9337
|
seen.add(id11);
|
|
9204
|
-
const name =
|
|
9338
|
+
const name = text6(item.name, `${path}.name`, errors, 160);
|
|
9205
9339
|
if (typeof item.available !== "boolean") errors.push(`${path}.available must be boolean`);
|
|
9206
9340
|
if (!Number.isInteger(item.sortOrder) || Number(item.sortOrder) < 0) errors.push(`${path}.sortOrder must be a non-negative integer`);
|
|
9207
9341
|
if (!Number.isInteger(item.priceCents) || Number(item.priceCents) < 0 || Number(item.priceCents) > 1e8) {
|
|
@@ -9219,7 +9353,7 @@ function tiersOf(value, errors) {
|
|
|
9219
9353
|
if (item.interval !== void 0 && item.interval !== "year" && item.interval !== "month") {
|
|
9220
9354
|
errors.push(`${path}.interval must be year or month`);
|
|
9221
9355
|
}
|
|
9222
|
-
const blurb = item.blurb === void 0 ? void 0 :
|
|
9356
|
+
const blurb = item.blurb === void 0 ? void 0 : text6(item.blurb, `${path}.blurb`, errors, 4e3);
|
|
9223
9357
|
return [{
|
|
9224
9358
|
id: id11,
|
|
9225
9359
|
name,
|
|
@@ -9277,7 +9411,7 @@ function policyOf(value, errors, tiers2) {
|
|
|
9277
9411
|
const result = {};
|
|
9278
9412
|
for (const key3 of allowed2) {
|
|
9279
9413
|
if (input[key3] === void 0 && key3 !== "disclaimerText" && key3 !== "refundPolicyText") continue;
|
|
9280
|
-
result[key3] =
|
|
9414
|
+
result[key3] = text6(input[key3], `document.policy.${key3}`, errors);
|
|
9281
9415
|
}
|
|
9282
9416
|
const tierPolicies = {};
|
|
9283
9417
|
if (input.tierPolicies !== void 0) {
|
|
@@ -9292,8 +9426,8 @@ function policyOf(value, errors, tiers2) {
|
|
|
9292
9426
|
}
|
|
9293
9427
|
unknownKeys3(policy2, /* @__PURE__ */ new Set(["refundPolicyText", "trustCopy"]), path, errors);
|
|
9294
9428
|
tierPolicies[id11] = {
|
|
9295
|
-
refundPolicyText:
|
|
9296
|
-
...policy2.trustCopy !== void 0 ? { trustCopy:
|
|
9429
|
+
refundPolicyText: text6(policy2.refundPolicyText, `${path}.refundPolicyText`, errors),
|
|
9430
|
+
...policy2.trustCopy !== void 0 ? { trustCopy: text6(policy2.trustCopy, `${path}.trustCopy`, errors) } : {}
|
|
9297
9431
|
};
|
|
9298
9432
|
}
|
|
9299
9433
|
}
|
|
@@ -9333,9 +9467,9 @@ function validateSignupControlDocument(input) {
|
|
|
9333
9467
|
if (!Number.isSafeInteger(root.generatedAt) || Number(root.generatedAt) < 1) errors.push("document.generatedAt must be epoch milliseconds");
|
|
9334
9468
|
const source = recordOf(root.source);
|
|
9335
9469
|
const target = recordOf(root.target);
|
|
9336
|
-
const appId =
|
|
9337
|
-
const chapterId =
|
|
9338
|
-
const environment =
|
|
9470
|
+
const appId = text6(source?.appId, "document.source.appId", errors, 63);
|
|
9471
|
+
const chapterId = text6(target?.chapterId, "document.target.chapterId", errors, 63);
|
|
9472
|
+
const environment = text6(target?.environment, "document.target.environment", errors, 63);
|
|
9339
9473
|
if (appId && !SLUG9.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
|
|
9340
9474
|
if (chapterId && !SLUG9.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
|
|
9341
9475
|
if (environment && !ENVIRONMENT.test(environment)) errors.push("document.target.environment is invalid");
|
|
@@ -9512,9 +9646,9 @@ function markdownBenefits(lines, path, errors) {
|
|
|
9512
9646
|
errors.push(`${path} line ${index2 + 1} must be a Markdown bullet`);
|
|
9513
9647
|
continue;
|
|
9514
9648
|
}
|
|
9515
|
-
const
|
|
9516
|
-
if (
|
|
9517
|
-
if (
|
|
9649
|
+
const text7 = (match[1] ?? "").trim();
|
|
9650
|
+
if (text7.length > 2e3) errors.push(`${path} line ${index2 + 1} must be at most 2,000 characters`);
|
|
9651
|
+
if (text7) benefits.push(text7);
|
|
9518
9652
|
}
|
|
9519
9653
|
if (benefits.length > MAX_BENEFITS2) errors.push(`${path} supports at most ${MAX_BENEFITS2} entries`);
|
|
9520
9654
|
return benefits.slice(0, MAX_BENEFITS2);
|