@odla-ai/chapter 0.59.0 → 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.
- package/README.md +14 -2
- package/dist/{chunk-WFTWT3V7.js → chunk-P6X3W2XP.js} +11 -5
- package/dist/{chunk-WFTWT3V7.js.map → chunk-P6X3W2XP.js.map} +1 -1
- package/dist/{chunk-Y2H4VURI.js → chunk-SDGVPIXF.js} +3 -2
- package/dist/chunk-SDGVPIXF.js.map +1 -0
- package/dist/index.cjs +161 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +161 -95
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +4 -0
- package/dist/ui/admin/index.js +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/member/index.d.ts +1 -1
- package/dist/ui/member/index.js +1 -1
- package/dist/worker/index.cjs +264 -181
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +122 -14
- package/dist/worker/index.d.ts +122 -14
- package/dist/worker/index.js +264 -181
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-Y2H4VURI.js.map +0 -1
package/dist/worker/index.cjs
CHANGED
|
@@ -42,6 +42,7 @@ __export(worker_exports, {
|
|
|
42
42
|
deliverMemberContent: () => deliverMemberContent,
|
|
43
43
|
deliverMembershipChapterEffect: () => deliverMembershipChapterEffect,
|
|
44
44
|
deliverSignupControl: () => deliverSignupControl,
|
|
45
|
+
disputedChargeCoversSeat: () => disputedChargeCoversSeat,
|
|
45
46
|
drainMembershipEffects: () => drainMembershipEffects,
|
|
46
47
|
grantIncludedNamedSeat: () => grantIncludedNamedSeat,
|
|
47
48
|
liftSeatPurchaseWindow: () => liftSeatPurchaseWindow,
|
|
@@ -53,7 +54,9 @@ __export(worker_exports, {
|
|
|
53
54
|
readDisputedCharge: () => readDisputedCharge,
|
|
54
55
|
readNamedSeatOffer: () => readNamedSeatOffer,
|
|
55
56
|
rechargeDisputedMembership: () => rechargeDisputedMembership,
|
|
57
|
+
rechargeDisputedSeat: () => rechargeDisputedSeat,
|
|
56
58
|
reconcileMembershipDispute: () => reconcileMembershipDispute,
|
|
59
|
+
reconcileNamedSeatDispute: () => reconcileNamedSeatDispute,
|
|
57
60
|
refundNamedSeatByOperator: () => refundNamedSeatByOperator,
|
|
58
61
|
regiftNamedSeat: () => regiftNamedSeat,
|
|
59
62
|
releaseNamedSeatRecipientApplications: () => releaseNamedSeatRecipientApplications,
|
|
@@ -1697,9 +1700,9 @@ async function backfillCrm(deps) {
|
|
|
1697
1700
|
}
|
|
1698
1701
|
|
|
1699
1702
|
// src/email-retirement.ts
|
|
1700
|
-
function isRetiredEmailCopy(subject,
|
|
1703
|
+
function isRetiredEmailCopy(subject, text10) {
|
|
1701
1704
|
const subjectCopy = subject.trimStart().replace(/^\[dev\]\s*/i, "");
|
|
1702
|
-
const bodyCopy =
|
|
1705
|
+
const bodyCopy = text10.trimStart().replace(/^\(dev redirect; original recipient: [^\r\n]*\)\r?\n\r?\n/, "");
|
|
1703
1706
|
return /^\(\s*unused\s*:/i.test(subjectCopy) || /^Unused\.\s*Switched off\b/i.test(bodyCopy);
|
|
1704
1707
|
}
|
|
1705
1708
|
|
|
@@ -1777,10 +1780,10 @@ function planDelivery(input) {
|
|
|
1777
1780
|
if (isRetiredEmailCopy(renderedSubject, renderedText)) return { deliver: false, reason: "template-retired" };
|
|
1778
1781
|
if ((0, import_crm5.validateEmailCopy)({ subject: renderedSubject, text: renderedText }, { allowedVariables: [] }).length) return { deliver: false, reason: "template-invalid" };
|
|
1779
1782
|
const subject = (redirect ? "[dev] " : "") + renderedSubject;
|
|
1780
|
-
const
|
|
1783
|
+
const text10 = redirect ? `(dev redirect; original recipient: ${input.to})
|
|
1781
1784
|
|
|
1782
1785
|
` + renderedText : renderedText;
|
|
1783
|
-
return { deliver: true, transport, to, subject, text:
|
|
1786
|
+
return { deliver: true, transport, to, subject, text: text10, redirected: redirect };
|
|
1784
1787
|
}
|
|
1785
1788
|
|
|
1786
1789
|
// src/notify.ts
|
|
@@ -1928,8 +1931,8 @@ function formatInstant(at2, timeZone) {
|
|
|
1928
1931
|
var adminUrlFor = (origin) => `${origin}/admin/`;
|
|
1929
1932
|
var membersUrlFor = (origin) => `${origin}/members/`;
|
|
1930
1933
|
function applicantVariables(app) {
|
|
1931
|
-
const
|
|
1932
|
-
return { firstName:
|
|
1934
|
+
const text10 = (v) => typeof v === "string" ? v : "";
|
|
1935
|
+
return { firstName: text10(app.firstName), lastName: text10(app.lastName), email: text10(app.email), phone: text10(app.phone), state: text10(app.state) };
|
|
1933
1936
|
}
|
|
1934
1937
|
function sampleVariables(origin, timeZone, now = Date.now()) {
|
|
1935
1938
|
const startAt = formatInstant(now + 864e5, timeZone);
|
|
@@ -3663,12 +3666,87 @@ function paymentLineItems(group, tier) {
|
|
|
3663
3666
|
|
|
3664
3667
|
// src/membership-dispute-authority.ts
|
|
3665
3668
|
var import_stripe6 = require("@odla-ai/stripe");
|
|
3669
|
+
|
|
3670
|
+
// src/membership-dispute-transition.ts
|
|
3666
3671
|
var MEMBERSHIP_DISPUTED_STATUS = "payment_disputed";
|
|
3667
3672
|
var DISPUTE_WON_STATUSES = ["won", "warning_closed"];
|
|
3668
3673
|
var CLOSED_STATUSES = [...DISPUTE_WON_STATUSES, "lost"];
|
|
3669
|
-
|
|
3674
|
+
function disputeTransition(row2, fact, eventId, now, terminal) {
|
|
3675
|
+
const status2 = String(fact.status ?? "unknown"), closed = CLOSED_STATUSES.includes(status2);
|
|
3676
|
+
const sameDispute = row2.disputeId === fact.objectId;
|
|
3677
|
+
if (sameDispute && Number(row2.disputeClosedAt ?? 0) > 0 && !closed) return null;
|
|
3678
|
+
const won = DISPUTE_WON_STATUSES.includes(status2);
|
|
3679
|
+
const suspended = row2.status === MEMBERSHIP_DISPUTED_STATUS, done = terminal.includes(String(row2.status));
|
|
3680
|
+
const restored = String(row2.disputePriorStatus ?? (row2.journeyApprovedAt ? "active" : "paid_pending_approval"));
|
|
3681
|
+
const next = done ? String(row2.status) : won ? suspended ? restored : String(row2.status) : MEMBERSHIP_DISPUTED_STATUS;
|
|
3682
|
+
return {
|
|
3683
|
+
status: next,
|
|
3684
|
+
disputeId: fact.objectId,
|
|
3685
|
+
disputeStatus: status2,
|
|
3686
|
+
disputeEventId: eventId,
|
|
3687
|
+
disputeClosedAt: closed ? now : 0,
|
|
3688
|
+
...fact.disputeReason ? { disputeReason: fact.disputeReason } : {},
|
|
3689
|
+
...fact.amountCents !== void 0 ? { disputeAmountCents: fact.amountCents } : {},
|
|
3690
|
+
...sameDispute ? {} : { disputedAt: now },
|
|
3691
|
+
...next === MEMBERSHIP_DISPUTED_STATUS && !suspended ? { disputePriorStatus: String(row2.status) } : {},
|
|
3692
|
+
revision: Number(row2.revision) + 1,
|
|
3693
|
+
updatedAt: now
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
// src/membership-seat-dispute.ts
|
|
3670
3698
|
var text2 = (value) => typeof value === "string" && value ? value : void 0;
|
|
3671
|
-
var
|
|
3699
|
+
var TERMINAL = ["payment_pending", "payment_processing", "payment_canceling", "refund_pending", "refunded", "canceled"];
|
|
3700
|
+
var namedSeatDisputeStanding = (seat, applied) => ({ seatId: String(seat.id), status: String(seat.status), ...text2(seat.disputeStatus) ? { disputeStatus: text2(seat.disputeStatus) } : {}, applied });
|
|
3701
|
+
function disputedChargeCoversSeat(charge, seat) {
|
|
3702
|
+
return charge.seatId === seat.id && charge.customerId === seat.providerCustomerId && charge.paymentIntentId !== void 0 && (charge.paymentIntentId === seat.providerPaymentIntentId || charge.paymentIntentId === seat.rechargePaymentIntentId);
|
|
3703
|
+
}
|
|
3704
|
+
async function reconcileNamedSeatDispute(db, input, now, charge, evidence) {
|
|
3705
|
+
if (!charge.seatId) membershipFail("membership_dispute_scope_mismatch");
|
|
3706
|
+
const membership = async (applied, seat) => {
|
|
3707
|
+
const id7 = await networkMembershipId(input), member = await membershipLoad(db, "networkMembership", id7);
|
|
3708
|
+
requireMembershipScope(member, input);
|
|
3709
|
+
return {
|
|
3710
|
+
membershipId: id7,
|
|
3711
|
+
status: String(member.status),
|
|
3712
|
+
termEndsAt: Number(member.termEndsAt),
|
|
3713
|
+
graceEndsAt: Number(member.graceEndsAt ?? 0),
|
|
3714
|
+
autoRenew: member.autoRenew === true,
|
|
3715
|
+
applied,
|
|
3716
|
+
...text2(member.disputeStatus) ? { disputeStatus: text2(member.disputeStatus) } : {},
|
|
3717
|
+
...seat ? { seat: namedSeatDisputeStanding(seat, applied) } : {}
|
|
3718
|
+
};
|
|
3719
|
+
};
|
|
3720
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
3721
|
+
const state = await loadMembershipAuthorityState(db, input), seat = await membershipLoad(db, "networkSeat", charge.seatId);
|
|
3722
|
+
if (!seat) return membership(false);
|
|
3723
|
+
requireMembershipScope(seat, input);
|
|
3724
|
+
if (charge.customerId !== seat.providerCustomerId) membershipFail("membership_dispute_scope_mismatch");
|
|
3725
|
+
if (!disputedChargeCoversSeat(charge, seat) || seat.disputeEventId === input.eventId) return membership(seat.disputeEventId === input.eventId, seat);
|
|
3726
|
+
const attrs = disputeTransition(seat, evidence.fact, input.eventId, now, TERMINAL);
|
|
3727
|
+
if (!attrs) return membership(false, seat);
|
|
3728
|
+
try {
|
|
3729
|
+
await db.transact([membershipUpdate("networkSeat", String(seat.id), attrs)], { mutationId: `seat-dispute:${seat.id}:${input.eventId}`, guards: [
|
|
3730
|
+
{ ns: "networkSeat", id: String(seat.id), exists: true, equals: { revision: seat.revision } },
|
|
3731
|
+
{ ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } },
|
|
3732
|
+
{ ns: "networkCommercialEvents", id: String(evidence.row.id), exists: true, equals: { digest: evidence.fact.digest } },
|
|
3733
|
+
evidence.guard
|
|
3734
|
+
] });
|
|
3735
|
+
} catch (error) {
|
|
3736
|
+
if (error.code !== "transact_guard_failed") throw error;
|
|
3737
|
+
continue;
|
|
3738
|
+
}
|
|
3739
|
+
const saved = await membershipLoad(db, "networkSeat", String(seat.id));
|
|
3740
|
+
requireMembershipScope(saved, input);
|
|
3741
|
+
return membership(true, saved);
|
|
3742
|
+
}
|
|
3743
|
+
return membershipFail("membership_dispute_retry_required");
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
// src/membership-dispute-authority.ts
|
|
3747
|
+
var TERMINAL2 = ["refunded", "refund_pending", "canceled", "declined", "restarting", "superseded"];
|
|
3748
|
+
var text3 = (value) => typeof value === "string" && value ? value : void 0;
|
|
3749
|
+
var refOf = (value) => typeof value === "string" ? text3(value) : text3(value?.id);
|
|
3672
3750
|
async function readDisputedCharge(secretKey, chargeId, fetch2) {
|
|
3673
3751
|
if (!/^ch_[A-Za-z0-9]+$/.test(chargeId)) membershipFail("invalid_dispute_charge");
|
|
3674
3752
|
const config = { secretKey, ...fetch2 ? { fetch: fetch2 } : {} };
|
|
@@ -3682,7 +3760,7 @@ async function readDisputedCharge(secretKey, chargeId, fetch2) {
|
|
|
3682
3760
|
if (!invoice.ok || invoice.body.id !== invoiceId) membershipFail("membership_dispute_provider_unavailable");
|
|
3683
3761
|
subscriptionId = refOf(invoice.body.subscription) ?? refOf(invoice.body.parent?.subscription_details?.subscription);
|
|
3684
3762
|
}
|
|
3685
|
-
const seatId =
|
|
3763
|
+
const seatId = text3(charge.body.metadata?.seatId);
|
|
3686
3764
|
return {
|
|
3687
3765
|
chargeId,
|
|
3688
3766
|
customerId,
|
|
@@ -3708,37 +3786,23 @@ async function reconcileMembershipDispute(db, input, now, secretKey, resolved) {
|
|
|
3708
3786
|
termEndsAt: Number(value.termEndsAt),
|
|
3709
3787
|
graceEndsAt: Number(value.graceEndsAt ?? 0),
|
|
3710
3788
|
autoRenew: value.autoRenew === true,
|
|
3711
|
-
...
|
|
3789
|
+
...text3(value.disputeStatus) ? { disputeStatus: text3(value.disputeStatus) } : {},
|
|
3712
3790
|
applied
|
|
3713
3791
|
});
|
|
3714
3792
|
const known = await membershipLoad(db, "networkMembership", id7);
|
|
3715
3793
|
requireMembershipScope(known, input);
|
|
3716
3794
|
if (known.disputeEventId === input.eventId) return receipt(known);
|
|
3795
|
+
const seatKnown = known.namedSeatId ? await membershipLoad(db, "networkSeat", String(known.namedSeatId)) : void 0;
|
|
3796
|
+
if (seatKnown?.disputeEventId === input.eventId) return { ...receipt(known, true), seat: namedSeatDisputeStanding(seatKnown, true) };
|
|
3717
3797
|
const charge = resolved && resolved.chargeId === fact.chargeId ? resolved : await readDisputedCharge(secretKey, fact.chargeId);
|
|
3798
|
+
if (charge.customerId !== known.providerCustomerId) membershipFail("membership_dispute_scope_mismatch");
|
|
3799
|
+
if (charge.seatId) return reconcileNamedSeatDispute(db, input, now, charge, { row: row2, fact, guard: evidenceGuard });
|
|
3718
3800
|
for (let attempt = 0; attempt < 8; attempt++) {
|
|
3719
3801
|
const state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id7);
|
|
3720
3802
|
requireMembershipScope(member, input);
|
|
3721
|
-
if (charge.customerId !== member.providerCustomerId) membershipFail("membership_dispute_scope_mismatch");
|
|
3722
3803
|
if (!disputedChargeCoversMembership(charge, member) || member.disputeEventId === input.eventId) return receipt(member);
|
|
3723
|
-
const
|
|
3724
|
-
|
|
3725
|
-
if (sameDispute && Number(member.disputeClosedAt ?? 0) > 0 && !closed) return receipt(member, false);
|
|
3726
|
-
const won = DISPUTE_WON_STATUSES.includes(status2);
|
|
3727
|
-
const suspended = member.status === MEMBERSHIP_DISPUTED_STATUS, terminal = TERMINAL.includes(String(member.status));
|
|
3728
|
-
const next = terminal ? String(member.status) : won ? suspended ? String(member.disputePriorStatus ?? (member.journeyApprovedAt ? "active" : "paid_pending_approval")) : String(member.status) : MEMBERSHIP_DISPUTED_STATUS;
|
|
3729
|
-
const attrs = {
|
|
3730
|
-
status: next,
|
|
3731
|
-
disputeId: fact.objectId,
|
|
3732
|
-
disputeStatus: status2,
|
|
3733
|
-
disputeEventId: input.eventId,
|
|
3734
|
-
disputeClosedAt: closed ? now : 0,
|
|
3735
|
-
...fact.disputeReason ? { disputeReason: fact.disputeReason } : {},
|
|
3736
|
-
...fact.amountCents !== void 0 ? { disputeAmountCents: fact.amountCents } : {},
|
|
3737
|
-
...sameDispute ? {} : { disputedAt: now },
|
|
3738
|
-
...next === MEMBERSHIP_DISPUTED_STATUS && !suspended ? { disputePriorStatus: String(member.status) } : {},
|
|
3739
|
-
revision: Number(member.revision) + 1,
|
|
3740
|
-
updatedAt: now
|
|
3741
|
-
};
|
|
3804
|
+
const attrs = disputeTransition(member, fact, input.eventId, now, TERMINAL2);
|
|
3805
|
+
if (!attrs) return receipt(member, false);
|
|
3742
3806
|
try {
|
|
3743
3807
|
await db.transact([membershipUpdate("networkMembership", id7, attrs)], { mutationId: `membership-dispute:${id7}:${input.eventId}`, guards: [
|
|
3744
3808
|
{ ns: "networkMembership", id: id7, exists: true, equals: { revision: member.revision } },
|
|
@@ -4860,6 +4924,9 @@ function namedSeatRecipientState(seat) {
|
|
|
4860
4924
|
return seat.refundReason === "declined" ? "declined" : "refunded";
|
|
4861
4925
|
case "canceled":
|
|
4862
4926
|
return "canceled";
|
|
4927
|
+
// A card-network dispute on the seat's own payment pauses the recipient until it is won or re-charged.
|
|
4928
|
+
case "payment_disputed":
|
|
4929
|
+
return "disputed";
|
|
4863
4930
|
default:
|
|
4864
4931
|
return "payment_pending";
|
|
4865
4932
|
}
|
|
@@ -5188,6 +5255,7 @@ var handleAdminMembershipSeats = async (req, url, env, ctx) => {
|
|
|
5188
5255
|
|
|
5189
5256
|
// src/membership-dispute-recharge.ts
|
|
5190
5257
|
var import_stripe9 = require("@odla-ai/stripe");
|
|
5258
|
+
var NS = { membership: "networkMembership", seat: "networkSeat" };
|
|
5191
5259
|
var fail6 = (code, extra = {}) => {
|
|
5192
5260
|
throw Object.assign(new Error(code), { code, ...extra });
|
|
5193
5261
|
};
|
|
@@ -5201,65 +5269,64 @@ async function defaultPaymentMethod(config, subscriptionId, customerId) {
|
|
|
5201
5269
|
if (!customer.ok) fail6("membership_dispute_provider_unavailable");
|
|
5202
5270
|
return refOf2(customer.body.invoice_settings?.default_payment_method);
|
|
5203
5271
|
}
|
|
5204
|
-
async function
|
|
5272
|
+
async function rechargeDisputed(db, kind, id7, secretKey, now) {
|
|
5205
5273
|
membershipClock(now);
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
const
|
|
5274
|
+
const ns = NS[kind];
|
|
5275
|
+
let row2 = await membershipLoad(db, ns, id7);
|
|
5276
|
+
if (!row2) fail6("membership_not_found");
|
|
5277
|
+
const scope2 = { chapterId: String(row2.chapterId), environment: String(row2.environment) };
|
|
5278
|
+
const membershipId = kind === "membership" ? id7 : String(row2.primaryMembershipId);
|
|
5279
|
+
const amountCents = Number(row2.disputeAmountCents ?? row2.amountCents), currency = String(row2.currency);
|
|
5210
5280
|
const done = (value, replayed) => ({
|
|
5211
5281
|
membershipId,
|
|
5282
|
+
...kind === "seat" ? { seatId: id7 } : {},
|
|
5212
5283
|
status: String(value.status),
|
|
5213
5284
|
paymentIntentId: String(value.rechargePaymentIntentId),
|
|
5214
5285
|
amountCents,
|
|
5215
5286
|
currency,
|
|
5216
5287
|
...replayed ? { replayed } : {}
|
|
5217
5288
|
});
|
|
5218
|
-
if (
|
|
5219
|
-
if (
|
|
5289
|
+
if (row2.status !== MEMBERSHIP_DISPUTED_STATUS) {
|
|
5290
|
+
if (row2.disputeStatus === "recharged" && row2.rechargePaymentIntentId) return done(row2, true);
|
|
5220
5291
|
fail6("membership_not_disputed");
|
|
5221
5292
|
}
|
|
5222
|
-
if (
|
|
5293
|
+
if (row2.disputeStatus !== "lost") fail6("membership_dispute_open");
|
|
5223
5294
|
if (!Number.isSafeInteger(amountCents) || amountCents <= 0 || !/^[a-z]{3}$/.test(currency)) fail6("membership_dispute_amount_invalid");
|
|
5224
|
-
let attempt =
|
|
5295
|
+
let attempt = row2.rechargeAttempt;
|
|
5225
5296
|
if (!attempt || attempt.finishedAt !== void 0) {
|
|
5226
|
-
attempt = { key:
|
|
5297
|
+
attempt = { key: `${kind}-recharge:${id7}:${String(row2.disputeId)}:${now}`, startedAt: now };
|
|
5227
5298
|
await db.transact(
|
|
5228
|
-
[membershipUpdate(
|
|
5229
|
-
{ mutationId: `membership-recharge-attempt:${attempt.key}`, guards: [{
|
|
5230
|
-
ns: "networkMembership",
|
|
5231
|
-
id: membershipId,
|
|
5232
|
-
exists: true,
|
|
5233
|
-
equals: { revision: member.revision, status: MEMBERSHIP_DISPUTED_STATUS }
|
|
5234
|
-
}] }
|
|
5299
|
+
[membershipUpdate(ns, id7, { rechargeAttempt: attempt, revision: Number(row2.revision) + 1, updatedAt: now })],
|
|
5300
|
+
{ mutationId: `membership-recharge-attempt:${attempt.key}`, guards: [{ ns, id: id7, exists: true, equals: { revision: row2.revision, status: MEMBERSHIP_DISPUTED_STATUS } }] }
|
|
5235
5301
|
);
|
|
5236
|
-
|
|
5237
|
-
if (!
|
|
5302
|
+
row2 = await membershipLoad(db, ns, id7);
|
|
5303
|
+
if (!row2 || row2.status !== MEMBERSHIP_DISPUTED_STATUS) fail6("membership_dispute_changed");
|
|
5238
5304
|
}
|
|
5239
5305
|
const config = { secretKey };
|
|
5240
|
-
const paymentMethod = await defaultPaymentMethod(config, String(
|
|
5306
|
+
const paymentMethod = await defaultPaymentMethod(config, String(row2.providerSubscriptionId), String(row2.providerCustomerId));
|
|
5241
5307
|
if (!paymentMethod) fail6("recharge_payment_method_unavailable");
|
|
5242
5308
|
const intent = await (0, import_stripe9.stripeRequest)(config, "POST", "/v1/payment_intents", {
|
|
5243
5309
|
amount: amountCents,
|
|
5244
5310
|
currency,
|
|
5245
|
-
customer: String(
|
|
5311
|
+
customer: String(row2.providerCustomerId),
|
|
5246
5312
|
payment_method: paymentMethod,
|
|
5247
5313
|
off_session: "true",
|
|
5248
5314
|
confirm: "true",
|
|
5249
|
-
description:
|
|
5315
|
+
description: `${kind === "seat" ? "Named seat" : "Membership"} payment reissued after dispute ${String(row2.disputeId)}`,
|
|
5250
5316
|
metadata: {
|
|
5251
|
-
...stripePartitionMetadata({ appId: String(
|
|
5252
|
-
applicationId: String(
|
|
5317
|
+
...stripePartitionMetadata({ appId: String(row2.appId), chapterId: scope2.chapterId, environment: scope2.environment, runtime: String(row2.runtime) }),
|
|
5318
|
+
applicationId: String(row2.applicationId),
|
|
5253
5319
|
membershipId,
|
|
5254
|
-
|
|
5320
|
+
...kind === "seat" ? { seatId: id7 } : {},
|
|
5321
|
+
disputeId: String(row2.disputeId),
|
|
5255
5322
|
membershipRecharge: "true"
|
|
5256
5323
|
}
|
|
5257
5324
|
}, { idempotencyKey: attempt.key });
|
|
5258
5325
|
const body = intent.body, error = body.error ?? {}, paymentStatus = String(body.status ?? "");
|
|
5259
5326
|
const finish = async (outcome) => {
|
|
5260
|
-
const current = await membershipLoad(db,
|
|
5327
|
+
const current = await membershipLoad(db, ns, id7);
|
|
5261
5328
|
if (!current) return;
|
|
5262
|
-
await db.transact([membershipUpdate(
|
|
5329
|
+
await db.transact([membershipUpdate(ns, id7, {
|
|
5263
5330
|
rechargeAttempt: { ...attempt, finishedAt: now, outcome },
|
|
5264
5331
|
revision: Number(current.revision) + 1,
|
|
5265
5332
|
updatedAt: now
|
|
@@ -5277,15 +5344,15 @@ async function rechargeDisputedMembership(db, membershipId, secretKey, now) {
|
|
|
5277
5344
|
fail6("recharge_declined", { declineCode: String(body.last_payment_error?.decline_code ?? paymentStatus) });
|
|
5278
5345
|
}
|
|
5279
5346
|
if (paymentStatus !== "succeeded") fail6("recharge_pending", { paymentStatus });
|
|
5280
|
-
if (refOf2(body.customer) !==
|
|
5347
|
+
if (refOf2(body.customer) !== row2.providerCustomerId || body.amount !== amountCents) fail6("recharge_mismatch");
|
|
5281
5348
|
for (let retry = 0; retry < 8; retry++) {
|
|
5282
|
-
const state = await loadMembershipAuthorityState(db, scope2), current = await membershipLoad(db,
|
|
5349
|
+
const state = await loadMembershipAuthorityState(db, scope2), current = await membershipLoad(db, ns, id7);
|
|
5283
5350
|
if (!current) fail6("membership_not_found");
|
|
5284
5351
|
if (current.rechargePaymentIntentId === body.id && current.status !== MEMBERSHIP_DISPUTED_STATUS) return done(current, true);
|
|
5285
|
-
const status2 = String(current.disputePriorStatus ?? (current.journeyApprovedAt ? "active" : "paid_pending_approval"));
|
|
5352
|
+
const status2 = String(current.disputePriorStatus ?? (kind === "seat" ? "active" : current.journeyApprovedAt ? "active" : "paid_pending_approval"));
|
|
5286
5353
|
try {
|
|
5287
5354
|
await db.transact(
|
|
5288
|
-
[membershipUpdate(
|
|
5355
|
+
[membershipUpdate(ns, id7, {
|
|
5289
5356
|
status: status2,
|
|
5290
5357
|
disputeStatus: "recharged",
|
|
5291
5358
|
rechargePaymentIntentId: String(body.id),
|
|
@@ -5295,7 +5362,7 @@ async function rechargeDisputedMembership(db, membershipId, secretKey, now) {
|
|
|
5295
5362
|
updatedAt: now
|
|
5296
5363
|
})],
|
|
5297
5364
|
{ mutationId: `membership-recharged:${attempt.key}`, guards: [
|
|
5298
|
-
{ ns
|
|
5365
|
+
{ ns, id: id7, exists: true, equals: { revision: current.revision, status: MEMBERSHIP_DISPUTED_STATUS } },
|
|
5299
5366
|
{ ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } }
|
|
5300
5367
|
] }
|
|
5301
5368
|
);
|
|
@@ -5303,10 +5370,12 @@ async function rechargeDisputedMembership(db, membershipId, secretKey, now) {
|
|
|
5303
5370
|
if (cause.code !== "transact_guard_failed") throw cause;
|
|
5304
5371
|
continue;
|
|
5305
5372
|
}
|
|
5306
|
-
return done(await membershipLoad(db,
|
|
5373
|
+
return done(await membershipLoad(db, ns, id7));
|
|
5307
5374
|
}
|
|
5308
5375
|
return fail6("membership_dispute_retry_required");
|
|
5309
5376
|
}
|
|
5377
|
+
var rechargeDisputedMembership = (db, membershipId, secretKey, now) => rechargeDisputed(db, "membership", membershipId, secretKey, now);
|
|
5378
|
+
var rechargeDisputedSeat = (db, seatId, secretKey, now) => rechargeDisputed(db, "seat", seatId, secretKey, now);
|
|
5310
5379
|
|
|
5311
5380
|
// src/worker-routes-admin-membership-disputes.ts
|
|
5312
5381
|
var LIST2 = "/api/admin/membership-disputes";
|
|
@@ -5323,6 +5392,9 @@ var PUBLIC2 = [
|
|
|
5323
5392
|
"memberNumber",
|
|
5324
5393
|
"amountCents",
|
|
5325
5394
|
"currency",
|
|
5395
|
+
"recipientName",
|
|
5396
|
+
"primaryMembershipId",
|
|
5397
|
+
"purchasedAt",
|
|
5326
5398
|
"disputeId",
|
|
5327
5399
|
"disputeStatus",
|
|
5328
5400
|
"disputeReason",
|
|
@@ -5370,14 +5442,17 @@ var handleAdminMembershipDisputes = async (req, url, env, ctx) => {
|
|
|
5370
5442
|
return json({ error: "the membership authority is not hosted by this deployment" }, 404);
|
|
5371
5443
|
}
|
|
5372
5444
|
if (!one) {
|
|
5373
|
-
const
|
|
5445
|
+
const page = { $: { where: { environment: env.ODLA_ENV }, order: { updatedAt: "desc" }, limit: 500 } };
|
|
5446
|
+
const found = await db.query({ networkMembership: page, networkSeat: page });
|
|
5447
|
+
const memberships = found.networkMembership ?? [], seats = found.networkSeat ?? [];
|
|
5448
|
+
const rows5 = [...memberships.map((row2) => ({ ...project2(row2), kind: "membership" })), ...seats.map((row2) => ({ ...project2(row2), kind: "seat" }))];
|
|
5374
5449
|
const disputes = rows5.filter((row2) => typeof row2.disputeId === "string").sort((a, b) => Number(b.disputedAt ?? 0) - Number(a.disputedAt ?? 0));
|
|
5375
|
-
return json({ disputes
|
|
5450
|
+
return json({ disputes, truncated: memberships.length >= 500 || seats.length >= 500 });
|
|
5376
5451
|
}
|
|
5377
5452
|
const secret = await getVaultSecret(db, operator.stripeSecretName ?? "stripe_secret_key");
|
|
5378
5453
|
if (!secret) return json({ error: "network payment provider unavailable" }, 503);
|
|
5379
5454
|
try {
|
|
5380
|
-
const receipt = await rechargeDisputedMembership(db, one[1], secret, Date.now());
|
|
5455
|
+
const receipt = await (one[1].startsWith("seat_") ? rechargeDisputedSeat : rechargeDisputedMembership)(db, one[1], secret, Date.now());
|
|
5381
5456
|
return json({ ok: true, ...receipt });
|
|
5382
5457
|
} catch (error) {
|
|
5383
5458
|
const failure = error;
|
|
@@ -5397,13 +5472,13 @@ var handleAdminMembershipDisputes = async (req, url, env, ctx) => {
|
|
|
5397
5472
|
|
|
5398
5473
|
// src/membership-policy-versions.ts
|
|
5399
5474
|
var LIMIT = 200;
|
|
5400
|
-
var
|
|
5475
|
+
var text4 = (value) => typeof value === "string" && value.length > 0 && value.length <= 200 ? value : void 0;
|
|
5401
5476
|
var instant = (value) => {
|
|
5402
5477
|
const at2 = typeof value === "number" ? value : typeof value === "string" ? Date.parse(value) : NaN;
|
|
5403
5478
|
return Number.isSafeInteger(at2) && at2 >= 0 ? at2 : void 0;
|
|
5404
5479
|
};
|
|
5405
5480
|
function provenance(row2) {
|
|
5406
|
-
const source =
|
|
5481
|
+
const source = text4(row2.source), actor = text4(row2.actor ?? row2.$createdBy), recordedAt = instant(row2.recordedAt ?? row2.createdAt ?? row2.$createdAt);
|
|
5407
5482
|
return { ...source ? { source } : {}, ...actor ? { actor } : {}, ...recordedAt !== void 0 ? { recordedAt } : {} };
|
|
5408
5483
|
}
|
|
5409
5484
|
var live = (w, now) => w.from <= now && (w.until === void 0 || now < w.until);
|
|
@@ -6404,8 +6479,8 @@ var APPLICATION_LABELS = {
|
|
|
6404
6479
|
state: "State"
|
|
6405
6480
|
};
|
|
6406
6481
|
function humanize(id7) {
|
|
6407
|
-
const
|
|
6408
|
-
return
|
|
6482
|
+
const text10 = id7.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
6483
|
+
return text10 ? `${text10[0].toUpperCase()}${text10.slice(1)}` : id7;
|
|
6409
6484
|
}
|
|
6410
6485
|
function applicationDefs(chapter) {
|
|
6411
6486
|
try {
|
|
@@ -6472,7 +6547,7 @@ var ID3 = /^[a-z][a-z0-9-]{1,79}$/;
|
|
|
6472
6547
|
var PRICE = /^price_[A-Za-z0-9]{12,}$/;
|
|
6473
6548
|
var CURRENCY = /^[a-z]{3}$/;
|
|
6474
6549
|
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6475
|
-
var
|
|
6550
|
+
var text5 = (value, path, errors, max) => {
|
|
6476
6551
|
if (typeof value !== "string" || !value.trim()) {
|
|
6477
6552
|
errors.push(`${path} is required`);
|
|
6478
6553
|
return "";
|
|
@@ -6500,23 +6575,23 @@ function validateGiftOfferCatalog(input, tierIds) {
|
|
|
6500
6575
|
}
|
|
6501
6576
|
const allowed2 = /* @__PURE__ */ new Set(["id", "target", "billingMode", "stripePriceId", "priceCents", "currency", "redemptionLifetimeHours", "activeFrom", "activeUntil", "title", "description", "ctaLabel"]);
|
|
6502
6577
|
for (const key3 of Object.keys(item)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
|
|
6503
|
-
const id7 =
|
|
6578
|
+
const id7 = text5(item.id, `${path}.id`, errors, 80);
|
|
6504
6579
|
if (id7 && !ID3.test(id7)) errors.push(`${path}.id must be a lowercase kebab-case id`);
|
|
6505
6580
|
if (ids2.has(id7)) errors.push(`${path}.id duplicates "${id7}"`);
|
|
6506
6581
|
ids2.add(id7);
|
|
6507
6582
|
const targetRaw = record(item.target);
|
|
6508
6583
|
let target = { type: "entitlement", entitlementKey: "invalid" };
|
|
6509
6584
|
if (targetRaw?.type === "tier") {
|
|
6510
|
-
const tierId =
|
|
6585
|
+
const tierId = text5(targetRaw.tierId, `${path}.target.tierId`, errors, 80);
|
|
6511
6586
|
if (!tierIds.has(tierId)) errors.push(`${path}.target.tierId must reference a declared tier`);
|
|
6512
6587
|
target = { type: "tier", tierId };
|
|
6513
6588
|
} else if (targetRaw?.type === "entitlement") {
|
|
6514
|
-
const entitlementKey =
|
|
6589
|
+
const entitlementKey = text5(targetRaw.entitlementKey, `${path}.target.entitlementKey`, errors, 80);
|
|
6515
6590
|
if (entitlementKey && !ID3.test(entitlementKey)) errors.push(`${path}.target.entitlementKey must be a lowercase kebab-case id`);
|
|
6516
6591
|
target = { type: "entitlement", entitlementKey };
|
|
6517
6592
|
} else errors.push(`${path}.target.type must be tier or entitlement`);
|
|
6518
6593
|
if (item.billingMode !== "one_time" && item.billingMode !== "subscription") errors.push(`${path}.billingMode must be one_time or subscription`);
|
|
6519
|
-
const stripePriceId =
|
|
6594
|
+
const stripePriceId = text5(item.stripePriceId, `${path}.stripePriceId`, errors, 128);
|
|
6520
6595
|
if (stripePriceId && !PRICE.test(stripePriceId)) errors.push(`${path}.stripePriceId must be a real Stripe Price id`);
|
|
6521
6596
|
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`);
|
|
6522
6597
|
const currency = typeof item.currency === "string" ? item.currency.trim().toLowerCase() : "";
|
|
@@ -6538,9 +6613,9 @@ function validateGiftOfferCatalog(input, tierIds) {
|
|
|
6538
6613
|
redemptionLifetimeHours: lifetime,
|
|
6539
6614
|
...activeFrom !== void 0 ? { activeFrom } : {},
|
|
6540
6615
|
...activeUntil !== void 0 ? { activeUntil } : {},
|
|
6541
|
-
title:
|
|
6542
|
-
description:
|
|
6543
|
-
ctaLabel:
|
|
6616
|
+
title: text5(item.title, `${path}.title`, errors, 160),
|
|
6617
|
+
description: text5(item.description, `${path}.description`, errors, 2e3),
|
|
6618
|
+
ctaLabel: text5(item.ctaLabel, `${path}.ctaLabel`, errors, 120)
|
|
6544
6619
|
});
|
|
6545
6620
|
}
|
|
6546
6621
|
return errors.length ? { ok: false, errors } : { ok: true, value: { schemaVersion: 1, offers } };
|
|
@@ -6956,17 +7031,17 @@ async function readBoundedRequestText(req, maxBytes) {
|
|
|
6956
7031
|
if (!reader) return "";
|
|
6957
7032
|
const decoder = new TextDecoder();
|
|
6958
7033
|
let bytes = 0;
|
|
6959
|
-
let
|
|
7034
|
+
let text10 = "";
|
|
6960
7035
|
for (; ; ) {
|
|
6961
7036
|
const { done, value } = await reader.read();
|
|
6962
7037
|
if (req.signal.aborted) return invalid2();
|
|
6963
7038
|
if (done) {
|
|
6964
7039
|
complete = true;
|
|
6965
|
-
return
|
|
7040
|
+
return text10 + decoder.decode();
|
|
6966
7041
|
}
|
|
6967
7042
|
bytes += value.byteLength;
|
|
6968
7043
|
if (bytes > maxBytes) return json({ error: "request body too large" }, 413);
|
|
6969
|
-
|
|
7044
|
+
text10 += decoder.decode(value, { stream: true });
|
|
6970
7045
|
}
|
|
6971
7046
|
} catch {
|
|
6972
7047
|
return invalid2();
|
|
@@ -7070,9 +7145,9 @@ async function paymentQuoteRoute(url, env, ctx, prepare = false, request) {
|
|
|
7070
7145
|
let selection;
|
|
7071
7146
|
if (request?.body) {
|
|
7072
7147
|
try {
|
|
7073
|
-
const
|
|
7074
|
-
if (
|
|
7075
|
-
const body = JSON.parse(
|
|
7148
|
+
const text10 = await readBoundedRequestText(request, 2048);
|
|
7149
|
+
if (text10 instanceof Response) return text10;
|
|
7150
|
+
const body = JSON.parse(text10);
|
|
7076
7151
|
if (!body || typeof body !== "object" || Array.isArray(body) || Object.keys(body).some((key3) => key3 !== "namedSeat")) throw new Error("invalid body");
|
|
7077
7152
|
selection = body.namedSeat;
|
|
7078
7153
|
} catch {
|
|
@@ -7954,10 +8029,10 @@ function boundedString(value, path, errors, max, required = false) {
|
|
|
7954
8029
|
errors.push(`${path} must be a string`);
|
|
7955
8030
|
return void 0;
|
|
7956
8031
|
}
|
|
7957
|
-
const
|
|
7958
|
-
if (required && !
|
|
7959
|
-
if (
|
|
7960
|
-
return
|
|
8032
|
+
const text10 = value.trim();
|
|
8033
|
+
if (required && !text10) errors.push(`${path} is required`);
|
|
8034
|
+
if (text10.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
|
|
8035
|
+
return text10;
|
|
7961
8036
|
}
|
|
7962
8037
|
function validateMembershipPageDocument(input, options = {}) {
|
|
7963
8038
|
const errors = [];
|
|
@@ -8006,8 +8081,8 @@ function validateMembershipPageDocument(input, options = {}) {
|
|
|
8006
8081
|
else {
|
|
8007
8082
|
if (item.benefits.length > MAX_BENEFITS) errors.push(`${path}.benefits supports at most ${MAX_BENEFITS} entries`);
|
|
8008
8083
|
benefits = item.benefits.slice(0, MAX_BENEFITS).flatMap((benefit, benefitIndex) => {
|
|
8009
|
-
const
|
|
8010
|
-
return
|
|
8084
|
+
const text10 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
|
|
8085
|
+
return text10 ? [text10] : [];
|
|
8011
8086
|
});
|
|
8012
8087
|
}
|
|
8013
8088
|
}
|
|
@@ -8101,7 +8176,7 @@ var recordOf = (value) => value !== null && typeof value === "object" && !Array.
|
|
|
8101
8176
|
function unknownKeys(value, allowed2, path, errors) {
|
|
8102
8177
|
for (const key3 of Object.keys(value)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
|
|
8103
8178
|
}
|
|
8104
|
-
function
|
|
8179
|
+
function text6(value, path, errors, max = 65536) {
|
|
8105
8180
|
if (typeof value !== "string" || !value.trim()) {
|
|
8106
8181
|
errors.push(`${path} is required`);
|
|
8107
8182
|
return "";
|
|
@@ -8124,11 +8199,11 @@ function tiersOf(value, errors) {
|
|
|
8124
8199
|
return [];
|
|
8125
8200
|
}
|
|
8126
8201
|
unknownKeys(item, TIER_KEYS2, path, errors);
|
|
8127
|
-
const id7 =
|
|
8202
|
+
const id7 = text6(item.id, `${path}.id`, errors, 80);
|
|
8128
8203
|
if (id7 && !TIER.test(id7)) errors.push(`${path}.id must be a lowercase kebab-case id`);
|
|
8129
8204
|
if (seen.has(id7)) errors.push(`${path}.id duplicates "${id7}"`);
|
|
8130
8205
|
seen.add(id7);
|
|
8131
|
-
const name =
|
|
8206
|
+
const name = text6(item.name, `${path}.name`, errors, 160);
|
|
8132
8207
|
if (typeof item.available !== "boolean") errors.push(`${path}.available must be boolean`);
|
|
8133
8208
|
if (!Number.isInteger(item.sortOrder) || Number(item.sortOrder) < 0) errors.push(`${path}.sortOrder must be a non-negative integer`);
|
|
8134
8209
|
if (!Number.isInteger(item.priceCents) || Number(item.priceCents) < 0 || Number(item.priceCents) > 1e8) {
|
|
@@ -8146,7 +8221,7 @@ function tiersOf(value, errors) {
|
|
|
8146
8221
|
if (item.interval !== void 0 && item.interval !== "year" && item.interval !== "month") {
|
|
8147
8222
|
errors.push(`${path}.interval must be year or month`);
|
|
8148
8223
|
}
|
|
8149
|
-
const blurb = item.blurb === void 0 ? void 0 :
|
|
8224
|
+
const blurb = item.blurb === void 0 ? void 0 : text6(item.blurb, `${path}.blurb`, errors, 4e3);
|
|
8150
8225
|
return [{
|
|
8151
8226
|
id: id7,
|
|
8152
8227
|
name,
|
|
@@ -8204,7 +8279,7 @@ function policyOf(value, errors, tiers) {
|
|
|
8204
8279
|
const result = {};
|
|
8205
8280
|
for (const key3 of allowed2) {
|
|
8206
8281
|
if (input[key3] === void 0 && key3 !== "disclaimerText" && key3 !== "refundPolicyText") continue;
|
|
8207
|
-
result[key3] =
|
|
8282
|
+
result[key3] = text6(input[key3], `document.policy.${key3}`, errors);
|
|
8208
8283
|
}
|
|
8209
8284
|
const tierPolicies = {};
|
|
8210
8285
|
if (input.tierPolicies !== void 0) {
|
|
@@ -8219,8 +8294,8 @@ function policyOf(value, errors, tiers) {
|
|
|
8219
8294
|
}
|
|
8220
8295
|
unknownKeys(policy2, /* @__PURE__ */ new Set(["refundPolicyText", "trustCopy"]), path, errors);
|
|
8221
8296
|
tierPolicies[id7] = {
|
|
8222
|
-
refundPolicyText:
|
|
8223
|
-
...policy2.trustCopy !== void 0 ? { trustCopy:
|
|
8297
|
+
refundPolicyText: text6(policy2.refundPolicyText, `${path}.refundPolicyText`, errors),
|
|
8298
|
+
...policy2.trustCopy !== void 0 ? { trustCopy: text6(policy2.trustCopy, `${path}.trustCopy`, errors) } : {}
|
|
8224
8299
|
};
|
|
8225
8300
|
}
|
|
8226
8301
|
}
|
|
@@ -8260,9 +8335,9 @@ function validateSignupControlDocument(input) {
|
|
|
8260
8335
|
if (!Number.isSafeInteger(root.generatedAt) || Number(root.generatedAt) < 1) errors.push("document.generatedAt must be epoch milliseconds");
|
|
8261
8336
|
const source = recordOf(root.source);
|
|
8262
8337
|
const target = recordOf(root.target);
|
|
8263
|
-
const appId =
|
|
8264
|
-
const chapterId =
|
|
8265
|
-
const environment =
|
|
8338
|
+
const appId = text6(source?.appId, "document.source.appId", errors, 63);
|
|
8339
|
+
const chapterId = text6(target?.chapterId, "document.target.chapterId", errors, 63);
|
|
8340
|
+
const environment = text6(target?.environment, "document.target.environment", errors, 63);
|
|
8266
8341
|
if (appId && !SLUG.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
|
|
8267
8342
|
if (chapterId && !SLUG.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
|
|
8268
8343
|
if (environment && !ENVIRONMENT.test(environment)) errors.push("document.target.environment is invalid");
|
|
@@ -10533,7 +10608,7 @@ async function startSubscription(req, env, ctx) {
|
|
|
10533
10608
|
|
|
10534
10609
|
// src/worker-payment-receipt.ts
|
|
10535
10610
|
var json2 = (value, status2 = 200) => Response.json(value, { status: status2, headers: { "cache-control": "no-store", "referrer-policy": "no-referrer" } });
|
|
10536
|
-
var escape = (
|
|
10611
|
+
var escape = (text10) => text10.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
10537
10612
|
async function paymentReceiptRoute(req, url, env, ctx) {
|
|
10538
10613
|
const id7 = url.searchParams.get("application");
|
|
10539
10614
|
if (!id7 || id7.length > 160) return json2({ error: "application required" }, 400);
|
|
@@ -10544,7 +10619,7 @@ async function paymentReceiptRoute(req, url, env, ctx) {
|
|
|
10544
10619
|
if (!app || app.groupId !== ctx.chapter.id || !intent || intent.applicationId !== id7 || intent.appId !== env.ODLA_APP_ID || intent.chapterId !== ctx.chapter.id || intent.environment !== env.ODLA_ENV || intent.runtime !== stripeRuntimeIdentity(env.ODLA_RUNTIME) || app.stripeRuntime !== intent.runtime || app.stripeSubscriptionId !== intent.providerSubscriptionId) return json2({ error: "receipt not found" }, 404);
|
|
10545
10620
|
const receipt = intent.paymentReceipt;
|
|
10546
10621
|
if (!receipt) return json2({ pending: true }, 202);
|
|
10547
|
-
const
|
|
10622
|
+
const text10 = paymentReceiptText(receipt, ctx.chapter.name);
|
|
10548
10623
|
if (req.method === "POST") {
|
|
10549
10624
|
if (!env.SEND_EMAIL || !env.EMAIL_FROM) return json2({ error: "Email is unavailable. You can print or save your receipt." }, 503);
|
|
10550
10625
|
const group = (await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups?.[0];
|
|
@@ -10562,7 +10637,7 @@ async function paymentReceiptRoute(req, url, env, ctx) {
|
|
|
10562
10637
|
template: "paymentReceipt",
|
|
10563
10638
|
to: String(intent.purchaserEmailNormalized),
|
|
10564
10639
|
applicationId: id7,
|
|
10565
|
-
vars: { chapterName: ctx.chapter.name, receiptText:
|
|
10640
|
+
vars: { chapterName: ctx.chapter.name, receiptText: text10 },
|
|
10566
10641
|
dedupeKey: `requested-payment-receipt:${receipt.invoiceId}`,
|
|
10567
10642
|
event: { label: "Payment receipt", trigger: "Purchaser requests their receipt", audience: "Purchaser", variables: ["chapterName", "receiptText"] }
|
|
10568
10643
|
});
|
|
@@ -10572,7 +10647,7 @@ async function paymentReceiptRoute(req, url, env, ctx) {
|
|
|
10572
10647
|
const nonce = crypto.randomUUID();
|
|
10573
10648
|
return new Response(`<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Payment receipt</title>
|
|
10574
10649
|
<style>body{font:16px/1.6 system-ui;max-width:680px;margin:48px auto;padding:24px;color:#17252d}pre{font:inherit;white-space:pre-wrap}button{font:inherit;padding:10px 16px}@media print{button{display:none}body{margin:0}}</style>
|
|
10575
|
-
<h1>Payment receipt</h1><pre>${escape(
|
|
10650
|
+
<h1>Payment receipt</h1><pre>${escape(text10)}</pre><button id="print">Print / save PDF</button>
|
|
10576
10651
|
<script nonce="${nonce}">document.getElementById('print').addEventListener('click',()=>window.print())</script></html>`, {
|
|
10577
10652
|
headers: {
|
|
10578
10653
|
"content-type": "text/html; charset=utf-8",
|
|
@@ -11042,16 +11117,16 @@ var handleNamedSeat = async (req, url, env, ctx) => {
|
|
|
11042
11117
|
};
|
|
11043
11118
|
|
|
11044
11119
|
// src/named-seat-accepted-notice.ts
|
|
11045
|
-
var
|
|
11120
|
+
var text7 = (value) => typeof value === "string" ? value : "";
|
|
11046
11121
|
async function notifyNamedSeatAccepted(db, env, ctx, input) {
|
|
11047
11122
|
const purchaser = await membershipLoad(db, "applications", input.primaryApplicationId);
|
|
11048
|
-
if (!purchaser || !chapterBillingApplication(purchaser, ctx.chapter.id, env.ODLA_RUNTIME) || !
|
|
11123
|
+
if (!purchaser || !chapterBillingApplication(purchaser, ctx.chapter.id, env.ODLA_RUNTIME) || !text7(purchaser.email)) {
|
|
11049
11124
|
return { sent: false, reason: "purchaser-missing" };
|
|
11050
11125
|
}
|
|
11051
11126
|
const group = await membershipLoad(db, "groups", ctx.chapter.id);
|
|
11052
11127
|
if (!group) return { sent: false, reason: "group-missing" };
|
|
11053
11128
|
const config = emailGroupFrom(group);
|
|
11054
|
-
const purchaserName = [purchaser.firstName, purchaser.lastName].map(
|
|
11129
|
+
const purchaserName = [purchaser.firstName, purchaser.lastName].map(text7).join(" ").replace(/\s+/g, " ").trim();
|
|
11055
11130
|
return sendTemplated({
|
|
11056
11131
|
db,
|
|
11057
11132
|
envName: env.ODLA_ENV,
|
|
@@ -11063,12 +11138,12 @@ async function notifyNamedSeatAccepted(db, env, ctx, input) {
|
|
|
11063
11138
|
}, {
|
|
11064
11139
|
group: config,
|
|
11065
11140
|
template: "seatAccepted",
|
|
11066
|
-
to:
|
|
11141
|
+
to: text7(purchaser.email),
|
|
11067
11142
|
dedupeKey: `named-seat-accepted:${input.seatId}`,
|
|
11068
11143
|
applicationId: input.primaryApplicationId,
|
|
11069
11144
|
tierId: input.skuId,
|
|
11070
11145
|
vars: {
|
|
11071
|
-
firstName:
|
|
11146
|
+
firstName: text7(purchaser.firstName),
|
|
11072
11147
|
purchaserName,
|
|
11073
11148
|
recipientName: input.recipientName,
|
|
11074
11149
|
tierName: await tierName(db, group, input.skuId),
|
|
@@ -12535,12 +12610,12 @@ async function updateEmailSettings(db, ctx, env, body) {
|
|
|
12535
12610
|
if (!value || typeof value !== "object" || Array.isArray(value)) return json({ error: `Invalid template: ${key3}` }, 400);
|
|
12536
12611
|
const t = value;
|
|
12537
12612
|
if (typeof t.enabled !== "boolean") return json({ error: `Choose whether ${key3} is enabled.` }, 400);
|
|
12538
|
-
const subject = str2(t.subject).trim(),
|
|
12539
|
-
const issues = (0, import_crm16.validateEmailCopy)({ subject, text:
|
|
12613
|
+
const subject = str2(t.subject).trim(), text10 = str2(t.text);
|
|
12614
|
+
const issues = (0, import_crm16.validateEmailCopy)({ subject, text: text10 }, { allowedVariables: events[key3].variables });
|
|
12540
12615
|
if (t.enabled && issues.length || issues.some((issue) => ["subject-invalid", "body-invalid"].includes(issue.code))) {
|
|
12541
12616
|
return json({ error: `${events[key3].label} is incomplete.`, errors: issues.map((issue) => issue.message) }, 400);
|
|
12542
12617
|
}
|
|
12543
|
-
clean[key3] = { subject, text:
|
|
12618
|
+
clean[key3] = { subject, text: text10, enabled: t.enabled };
|
|
12544
12619
|
}
|
|
12545
12620
|
const notificationEmail = str2(body.notificationEmail).trim(), replyTo = str2(body.replyTo).trim(), debugEmail = str2(body.debugEmail).trim();
|
|
12546
12621
|
const emailish = /^[^\s@,;<>]+@[^\s@,;<>]+\.[^\s@,;<>]+$/;
|
|
@@ -14744,9 +14819,9 @@ var handleAdminNetworkPersonRegistry = async (req, url, env, ctx) => {
|
|
|
14744
14819
|
if (req.method === "POST") {
|
|
14745
14820
|
let body;
|
|
14746
14821
|
try {
|
|
14747
|
-
const
|
|
14748
|
-
if (new TextEncoder().encode(
|
|
14749
|
-
const parsed = JSON.parse(
|
|
14822
|
+
const text10 = await req.text();
|
|
14823
|
+
if (new TextEncoder().encode(text10).byteLength > MAX_BODY2) return json({ error: "request is too large" }, 413);
|
|
14824
|
+
const parsed = JSON.parse(text10);
|
|
14750
14825
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("invalid");
|
|
14751
14826
|
body = parsed;
|
|
14752
14827
|
} catch {
|
|
@@ -14915,7 +14990,7 @@ var import_db13 = require("@odla-ai/db");
|
|
|
14915
14990
|
// src/network-commercial-parity.ts
|
|
14916
14991
|
var DIGEST = /^[0-9a-f]{64}$/;
|
|
14917
14992
|
var MAX_RECORDS = 500;
|
|
14918
|
-
var
|
|
14993
|
+
var text8 = (value, max = 255) => typeof value === "string" && value.length > 0 && value.length <= max ? value : null;
|
|
14919
14994
|
function parityMessage(partition, kind, value) {
|
|
14920
14995
|
return [
|
|
14921
14996
|
"chapter-commercial-parity",
|
|
@@ -14947,18 +15022,18 @@ async function digestWithKey(key3, partition, kind, value) {
|
|
|
14947
15022
|
async function buildChapterCommercialParitySnapshot(input) {
|
|
14948
15023
|
const requestedMax = input.maxRecords ?? MAX_RECORDS;
|
|
14949
15024
|
const maxRecords = Number.isSafeInteger(requestedMax) ? Math.max(0, Math.min(requestedMax, MAX_RECORDS)) : MAX_RECORDS;
|
|
14950
|
-
const eligible = input.rows.filter((row2) =>
|
|
15025
|
+
const eligible = input.rows.filter((row2) => text8(row2.id) && (text8(row2.stripeCustomerId) || text8(row2.stripeSubscriptionId)));
|
|
14951
15026
|
const selected2 = eligible.slice(0, maxRecords);
|
|
14952
15027
|
const key3 = await parityKey(input.secret);
|
|
14953
15028
|
const records = await Promise.all(selected2.map(async (row2) => {
|
|
14954
|
-
const applicationId =
|
|
14955
|
-
const customerId =
|
|
14956
|
-
const subscriptionId =
|
|
15029
|
+
const applicationId = text8(row2.id);
|
|
15030
|
+
const customerId = text8(row2.stripeCustomerId);
|
|
15031
|
+
const subscriptionId = text8(row2.stripeSubscriptionId);
|
|
14957
15032
|
return {
|
|
14958
15033
|
applicationDigest: await digestWithKey(key3, input.partition, "application", applicationId),
|
|
14959
15034
|
customerDigest: customerId ? await digestWithKey(key3, input.partition, "customer", customerId) : null,
|
|
14960
15035
|
subscriptionDigest: subscriptionId ? await digestWithKey(key3, input.partition, "subscription", subscriptionId) : null,
|
|
14961
|
-
status:
|
|
15036
|
+
status: text8(row2.status, 64),
|
|
14962
15037
|
canceled: row2.canceled === true
|
|
14963
15038
|
};
|
|
14964
15039
|
}));
|
|
@@ -14984,7 +15059,7 @@ function validSnapshot(snapshot, source) {
|
|
|
14984
15059
|
if (snapshot?.version !== 1 || snapshot.site?.id !== source.chapterId || !partition || !Array.isArray(records) || records.length > MAX_RECORDS || partition.appId !== source.appId || partition.chapterId !== source.chapterId || partition.environment !== source.environment || partition.dataEnvironment !== source.dataEnvironment || partition.runtime !== source.runtime || typeof snapshot.complete !== "boolean" || typeof snapshot.truncated !== "boolean" || !snapshot.counts) return false;
|
|
14985
15060
|
const seen = /* @__PURE__ */ new Set();
|
|
14986
15061
|
for (const record4 of records) {
|
|
14987
|
-
if (!DIGEST.test(record4.applicationDigest) || record4.customerDigest !== null && !DIGEST.test(record4.customerDigest) || record4.subscriptionDigest !== null && !DIGEST.test(record4.subscriptionDigest) || record4.status !== null &&
|
|
15062
|
+
if (!DIGEST.test(record4.applicationDigest) || record4.customerDigest !== null && !DIGEST.test(record4.customerDigest) || record4.subscriptionDigest !== null && !DIGEST.test(record4.subscriptionDigest) || record4.status !== null && text8(record4.status, 64) === null || typeof record4.canceled !== "boolean") return false;
|
|
14988
15063
|
const key3 = recordKey(record4);
|
|
14989
15064
|
if (seen.has(key3)) return false;
|
|
14990
15065
|
seen.add(key3);
|
|
@@ -14995,16 +15070,16 @@ async function reconcileCommercialParitySnapshot(input) {
|
|
|
14995
15070
|
if (!validSnapshot(input.snapshot, input.source)) throw new Error("invalid chapter commercial parity snapshot");
|
|
14996
15071
|
const sourceKey = networkCommercialSourceKey(input.source);
|
|
14997
15072
|
const key3 = await parityKey(input.secret);
|
|
14998
|
-
const shadow = await Promise.all(input.projection.subscriptions.filter((row2) => row2.sourceKey === sourceKey &&
|
|
14999
|
-
const customerId =
|
|
15000
|
-
const subscriptionId =
|
|
15073
|
+
const shadow = await Promise.all(input.projection.subscriptions.filter((row2) => row2.sourceKey === sourceKey && text8(row2.applicationId) && text8(row2.objectId)).map(async (row2) => {
|
|
15074
|
+
const customerId = text8(row2.customerId);
|
|
15075
|
+
const subscriptionId = text8(row2.subscriptionId) ?? text8(row2.objectId);
|
|
15001
15076
|
return {
|
|
15002
15077
|
applicationDigest: await digestWithKey(key3, input.snapshot.partition, "application", row2.applicationId),
|
|
15003
15078
|
customerDigest: customerId ? await digestWithKey(key3, input.snapshot.partition, "customer", customerId) : null,
|
|
15004
15079
|
subscriptionDigest: subscriptionId ? await digestWithKey(key3, input.snapshot.partition, "subscription", subscriptionId) : null,
|
|
15005
15080
|
status: null,
|
|
15006
15081
|
canceled: false,
|
|
15007
|
-
stripeStatus:
|
|
15082
|
+
stripeStatus: text8(row2.status, 64),
|
|
15008
15083
|
stripeCanceled: row2.status === "canceled"
|
|
15009
15084
|
};
|
|
15010
15085
|
}));
|
|
@@ -15214,18 +15289,24 @@ async function applyHostedMembershipDispute(env, ctx, fact, now) {
|
|
|
15214
15289
|
const secret = await getVaultSecret(db, operator.stripeSecretName ?? "stripe_secret_key");
|
|
15215
15290
|
if (!secret) throw Object.assign(new Error("membership_dispute_provider_unavailable"), { code: "membership_dispute_provider_unavailable" });
|
|
15216
15291
|
const charge = await readDisputedCharge(secret, fact.chargeId);
|
|
15292
|
+
const scopeOf = (row2) => ({
|
|
15293
|
+
appId: String(row2.appId),
|
|
15294
|
+
chapterId: String(row2.chapterId),
|
|
15295
|
+
environment: String(row2.environment),
|
|
15296
|
+
runtime: String(row2.runtime),
|
|
15297
|
+
applicationId: String(row2.applicationId),
|
|
15298
|
+
skuId: String(row2.skuId)
|
|
15299
|
+
});
|
|
15300
|
+
if (charge.seatId) {
|
|
15301
|
+
const seat = await membershipLoad(db, "networkSeat", charge.seatId);
|
|
15302
|
+
if (!seat || seat.environment !== env.ODLA_ENV || !disputedChargeCoversSeat(charge, seat)) return { disposition: "not_applicable" };
|
|
15303
|
+
const receipt2 = await reconcileMembershipDispute(db, { ...scopeOf(seat), eventId: fact.eventId }, now, secret, charge);
|
|
15304
|
+
return { disposition: "applied", membershipId: receipt2.membershipId, seatId: charge.seatId };
|
|
15305
|
+
}
|
|
15217
15306
|
const rows5 = (await db.query({ networkMembership: { $: { where: { providerCustomerId: charge.customerId, environment: env.ODLA_ENV }, limit: 5 } } })).networkMembership ?? [];
|
|
15218
15307
|
const member = rows5.find((row2) => disputedChargeCoversMembership(charge, row2));
|
|
15219
15308
|
if (!member) return { disposition: "not_applicable" };
|
|
15220
|
-
const
|
|
15221
|
-
appId: String(member.appId),
|
|
15222
|
-
chapterId: String(member.chapterId),
|
|
15223
|
-
environment: String(member.environment),
|
|
15224
|
-
runtime: String(member.runtime),
|
|
15225
|
-
applicationId: String(member.applicationId),
|
|
15226
|
-
skuId: String(member.skuId)
|
|
15227
|
-
};
|
|
15228
|
-
const receipt = await reconcileMembershipDispute(db, { ...target, eventId: fact.eventId }, now, secret, charge);
|
|
15309
|
+
const receipt = await reconcileMembershipDispute(db, { ...scopeOf(member), eventId: fact.eventId }, now, secret, charge);
|
|
15229
15310
|
return { disposition: "applied", membershipId: receipt.membershipId };
|
|
15230
15311
|
}
|
|
15231
15312
|
|
|
@@ -15293,11 +15374,11 @@ function safeProjection(projection) {
|
|
|
15293
15374
|
amountReceivedCents: seat.amountReceivedCents,
|
|
15294
15375
|
refundedCents: seat.refundedCents ?? 0
|
|
15295
15376
|
})),
|
|
15296
|
-
disputes: (projection.disputes ?? []).map((
|
|
15297
|
-
...row2(
|
|
15298
|
-
chargeId:
|
|
15299
|
-
paymentIntentId:
|
|
15300
|
-
reason:
|
|
15377
|
+
disputes: (projection.disputes ?? []).map((dispute2) => ({
|
|
15378
|
+
...row2(dispute2),
|
|
15379
|
+
chargeId: dispute2.chargeId ?? null,
|
|
15380
|
+
paymentIntentId: dispute2.paymentIntentId ?? null,
|
|
15381
|
+
reason: dispute2.disputeReason ?? null
|
|
15301
15382
|
})),
|
|
15302
15383
|
exceptions: projection.exceptions
|
|
15303
15384
|
};
|
|
@@ -15331,10 +15412,10 @@ var handleNetworkCommercialStripeWebhook = async (req, url, env, ctx) => {
|
|
|
15331
15412
|
if (result.status === "collision") {
|
|
15332
15413
|
console.error("network commercial event collision", JSON.stringify({ eventId: fact.eventId, eventType: fact.eventType }));
|
|
15333
15414
|
}
|
|
15334
|
-
let
|
|
15415
|
+
let dispute2;
|
|
15335
15416
|
if (fact.objectKind === "dispute" && result.status !== "collision") {
|
|
15336
15417
|
try {
|
|
15337
|
-
|
|
15418
|
+
dispute2 = await applyHostedMembershipDispute(env, ctx, fact, Date.now());
|
|
15338
15419
|
} catch (error) {
|
|
15339
15420
|
console.warn("membership dispute pending", JSON.stringify({ eventId: fact.eventId, code: error.code ?? "unknown" }));
|
|
15340
15421
|
return json({ error: "membership dispute reconciliation pending", eventId: fact.eventId }, 503);
|
|
@@ -15344,7 +15425,7 @@ var handleNetworkCommercialStripeWebhook = async (req, url, env, ctx) => {
|
|
|
15344
15425
|
ok: true,
|
|
15345
15426
|
status: result.status,
|
|
15346
15427
|
eventId: fact.eventId,
|
|
15347
|
-
...
|
|
15428
|
+
...dispute2 ? { dispute: dispute2.disposition } : {},
|
|
15348
15429
|
...result.status === "replayed" && result.resolvedCollision ? { resolvedCollision: true } : {}
|
|
15349
15430
|
});
|
|
15350
15431
|
} catch (error) {
|
|
@@ -15542,7 +15623,7 @@ var record3 = (value) => value !== null && typeof value === "object" && !Array.i
|
|
|
15542
15623
|
function unknownKeys3(value, allowed2, path, errors) {
|
|
15543
15624
|
for (const key3 of Object.keys(value)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
|
|
15544
15625
|
}
|
|
15545
|
-
function
|
|
15626
|
+
function text9(value, path, errors, max = MAX_TEXT2, required = true) {
|
|
15546
15627
|
if (value === void 0 && !required) return void 0;
|
|
15547
15628
|
if (typeof value !== "string" || required && !value.trim()) {
|
|
15548
15629
|
errors.push(`${path} must be a non-empty string`);
|
|
@@ -15568,7 +15649,7 @@ function scopeValue(value, path, errors) {
|
|
|
15568
15649
|
return { kind: raw2.kind };
|
|
15569
15650
|
}
|
|
15570
15651
|
if (raw2.kind === "chapter") {
|
|
15571
|
-
const chapterId =
|
|
15652
|
+
const chapterId = text9(raw2.chapterId, `${path}.chapterId`, errors, 63) ?? "";
|
|
15572
15653
|
if (chapterId && !APP_ID.test(chapterId)) errors.push(`${path}.chapterId must be a lowercase app slug`);
|
|
15573
15654
|
return { kind: "chapter", chapterId };
|
|
15574
15655
|
}
|
|
@@ -15582,22 +15663,22 @@ function resourceValue(value, path, errors) {
|
|
|
15582
15663
|
return null;
|
|
15583
15664
|
}
|
|
15584
15665
|
unknownKeys3(raw2, RESOURCE_KEYS, path, errors);
|
|
15585
|
-
const id7 =
|
|
15666
|
+
const id7 = text9(raw2.id, `${path}.id`, errors, 80) ?? "";
|
|
15586
15667
|
if (id7 && !ID5.test(id7)) errors.push(`${path}.id must be lowercase kebab-case`);
|
|
15587
|
-
const kind =
|
|
15668
|
+
const kind = text9(raw2.kind, `${path}.kind`, errors) ?? "";
|
|
15588
15669
|
if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
|
|
15589
|
-
const title =
|
|
15590
|
-
const summary =
|
|
15591
|
-
const status2 =
|
|
15670
|
+
const title = text9(raw2.title, `${path}.title`, errors, 240) ?? "";
|
|
15671
|
+
const summary = text9(raw2.summary, `${path}.summary`, errors, 1e3, false);
|
|
15672
|
+
const status2 = text9(raw2.status, `${path}.status`, errors) ?? "";
|
|
15592
15673
|
if (!STATUSES.has(status2)) errors.push(`${path}.status must be draft, published, or archived`);
|
|
15593
15674
|
const revision = integer4(raw2.revision, `${path}.revision`, errors);
|
|
15594
15675
|
const scope2 = scopeValue(raw2.scope, `${path}.scope`, errors) ?? { kind: "global" };
|
|
15595
|
-
const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) =>
|
|
15676
|
+
const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) => text9(tag, `${path}.tags[${index2}]`, errors, 80) ?? "") : [];
|
|
15596
15677
|
if (!Array.isArray(raw2.tags)) errors.push(`${path}.tags must be an array`);
|
|
15597
15678
|
if (tags.length > MAX_TAGS) errors.push(`${path}.tags may contain at most ${MAX_TAGS} items`);
|
|
15598
15679
|
if (new Set(tags).size !== tags.length) errors.push(`${path}.tags must be unique`);
|
|
15599
|
-
const bodyMarkdown =
|
|
15600
|
-
const url =
|
|
15680
|
+
const bodyMarkdown = text9(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN3, false);
|
|
15681
|
+
const url = text9(raw2.url, `${path}.url`, errors, 2048, false);
|
|
15601
15682
|
if (url) {
|
|
15602
15683
|
try {
|
|
15603
15684
|
if (new URL(url).protocol !== "https:") errors.push(`${path}.url must use https`);
|
|
@@ -15609,9 +15690,9 @@ function resourceValue(value, path, errors) {
|
|
|
15609
15690
|
if (raw2.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
|
|
15610
15691
|
if (asset) unknownKeys3(asset, ASSET_KEYS, `${path}.brandAsset`, errors);
|
|
15611
15692
|
const brandAsset = asset ? {
|
|
15612
|
-
bookId:
|
|
15613
|
-
assetId:
|
|
15614
|
-
...asset.filename === void 0 ? {} : { filename:
|
|
15693
|
+
bookId: text9(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
|
|
15694
|
+
assetId: text9(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
|
|
15695
|
+
...asset.filename === void 0 ? {} : { filename: text9(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
|
|
15615
15696
|
} : void 0;
|
|
15616
15697
|
if ((kind === "document" || kind === "note") && !bodyMarkdown) errors.push(`${path}.bodyMarkdown is required for ${kind}`);
|
|
15617
15698
|
if ((kind === "link" || kind === "inspiration") && !url) errors.push(`${path}.url is required for ${kind}`);
|
|
@@ -15626,17 +15707,17 @@ function resourceValue(value, path, errors) {
|
|
|
15626
15707
|
if (provenanceRaw?.promotedFrom !== void 0 && !promotedRaw) errors.push(`${path}.provenance.promotedFrom must be an object`);
|
|
15627
15708
|
if (promotedRaw) unknownKeys3(promotedRaw, PROMOTION_KEYS, `${path}.provenance.promotedFrom`, errors);
|
|
15628
15709
|
const promotedFrom = promotedRaw ? {
|
|
15629
|
-
chapterId:
|
|
15630
|
-
resourceId:
|
|
15710
|
+
chapterId: text9(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
|
|
15711
|
+
resourceId: text9(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
|
|
15631
15712
|
resourceRevision: integer4(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
|
|
15632
15713
|
documentRevision: integer4(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
|
|
15633
|
-
promotedBy:
|
|
15714
|
+
promotedBy: text9(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
|
|
15634
15715
|
promotedAt: integer4(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
|
|
15635
15716
|
} : void 0;
|
|
15636
15717
|
const provenance2 = {
|
|
15637
|
-
createdBy:
|
|
15718
|
+
createdBy: text9(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
|
|
15638
15719
|
createdAt: integer4(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
|
|
15639
|
-
updatedBy:
|
|
15720
|
+
updatedBy: text9(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
|
|
15640
15721
|
updatedAt: integer4(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
|
|
15641
15722
|
...promotedFrom ? { promotedFrom } : {}
|
|
15642
15723
|
};
|
|
@@ -15655,7 +15736,7 @@ function validateBuilderLibraryDocument(value, options = {}) {
|
|
|
15655
15736
|
if (sourceRaw) unknownKeys3(sourceRaw, SOURCE_KEYS2, "document.source", errors);
|
|
15656
15737
|
const sourceType = sourceRaw?.type;
|
|
15657
15738
|
if (sourceType !== "chapter" && sourceType !== "built_not_found") errors.push("document.source.type must be chapter or built_not_found");
|
|
15658
|
-
const sourceAppId =
|
|
15739
|
+
const sourceAppId = text9(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
|
|
15659
15740
|
if (sourceAppId && !APP_ID.test(sourceAppId)) errors.push("document.source.appId must be a lowercase app slug");
|
|
15660
15741
|
if (options.sourceType && sourceType !== options.sourceType) errors.push(`document.source.type must be ${options.sourceType}`);
|
|
15661
15742
|
if (options.sourceAppId && sourceAppId !== options.sourceAppId) errors.push(`document.source.appId must be ${options.sourceAppId}`);
|
|
@@ -15663,8 +15744,8 @@ function validateBuilderLibraryDocument(value, options = {}) {
|
|
|
15663
15744
|
if (raw2.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
|
|
15664
15745
|
if (targetRaw) unknownKeys3(targetRaw, TARGET_KEYS2, "document.target", errors);
|
|
15665
15746
|
const target = targetRaw ? {
|
|
15666
|
-
appId:
|
|
15667
|
-
environment:
|
|
15747
|
+
appId: text9(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
|
|
15748
|
+
environment: text9(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
|
|
15668
15749
|
} : void 0;
|
|
15669
15750
|
if (options.targetAppId && target?.appId !== options.targetAppId) errors.push(`document.target.appId must be ${options.targetAppId}`);
|
|
15670
15751
|
if (!Array.isArray(raw2.resources)) errors.push("document.resources must be an array");
|
|
@@ -17189,6 +17270,19 @@ var id5 = () => attr("string", false, true, true);
|
|
|
17189
17270
|
var key = (optional = false) => attr("string", optional, false, true);
|
|
17190
17271
|
var scope = () => ({ appId: key(), chapterId: key(), environment: key(), runtime: key(), applicationId: key(), skuId: key() });
|
|
17191
17272
|
var times = () => ({ revision: attr("number"), createdAt: attr("number"), updatedAt: attr("number") });
|
|
17273
|
+
var dispute = () => ({
|
|
17274
|
+
disputeId: key(true),
|
|
17275
|
+
disputeStatus: attr("string", true),
|
|
17276
|
+
disputeReason: attr("string", true),
|
|
17277
|
+
disputeAmountCents: attr("number", true),
|
|
17278
|
+
disputedAt: attr("number", true),
|
|
17279
|
+
disputeClosedAt: attr("number", true),
|
|
17280
|
+
disputePriorStatus: attr("string", true),
|
|
17281
|
+
disputeEventId: key(true),
|
|
17282
|
+
rechargePaymentIntentId: key(true),
|
|
17283
|
+
rechargedAt: attr("number", true),
|
|
17284
|
+
rechargeAttempt: attr("json", true)
|
|
17285
|
+
});
|
|
17192
17286
|
var membershipAuthorityEntities = {
|
|
17193
17287
|
networkMembership: { attrs: {
|
|
17194
17288
|
id: id5(),
|
|
@@ -17229,19 +17323,7 @@ var membershipAuthorityEntities = {
|
|
|
17229
17323
|
...times(),
|
|
17230
17324
|
refundRequestedAt: attr("number", true),
|
|
17231
17325
|
restartId: key(true),
|
|
17232
|
-
|
|
17233
|
-
// the status the membership held before it was suspended, and the re-issued charge once an operator collected again.
|
|
17234
|
-
disputeId: key(true),
|
|
17235
|
-
disputeStatus: attr("string", true),
|
|
17236
|
-
disputeReason: attr("string", true),
|
|
17237
|
-
disputeAmountCents: attr("number", true),
|
|
17238
|
-
disputedAt: attr("number", true),
|
|
17239
|
-
disputeClosedAt: attr("number", true),
|
|
17240
|
-
disputePriorStatus: attr("string", true),
|
|
17241
|
-
disputeEventId: key(true),
|
|
17242
|
-
rechargePaymentIntentId: key(true),
|
|
17243
|
-
rechargedAt: attr("number", true),
|
|
17244
|
-
rechargeAttempt: attr("json", true)
|
|
17326
|
+
...dispute()
|
|
17245
17327
|
} },
|
|
17246
17328
|
membershipRestart: { attrs: {
|
|
17247
17329
|
id: id5(),
|
|
@@ -17317,7 +17399,8 @@ var membershipAuthorityEntities = {
|
|
|
17317
17399
|
refundedCents: attr("number", true),
|
|
17318
17400
|
providerRefundIds: attr("json", true),
|
|
17319
17401
|
regiftedAt: attr("number", true),
|
|
17320
|
-
formerRecipients: attr("json", true)
|
|
17402
|
+
formerRecipients: attr("json", true),
|
|
17403
|
+
...dispute()
|
|
17321
17404
|
} },
|
|
17322
17405
|
networkSeatRecipient: { attrs: { id: id5(), appId: key(), chapterId: key(), environment: key(), runtime: key(), seatId: key(), recipientEmailNormalized: key(), createdAt: attr("number") } },
|
|
17323
17406
|
membershipEffect: { attrs: {
|
|
@@ -17654,7 +17737,7 @@ async function readBoundedJson(response, signal, maxBytes, label) {
|
|
|
17654
17737
|
};
|
|
17655
17738
|
signal.addEventListener("abort", cancel, { once: true });
|
|
17656
17739
|
let size = 0;
|
|
17657
|
-
let
|
|
17740
|
+
let text10 = "";
|
|
17658
17741
|
const decoder = new TextDecoder();
|
|
17659
17742
|
try {
|
|
17660
17743
|
while (true) {
|
|
@@ -17664,10 +17747,10 @@ async function readBoundedJson(response, signal, maxBytes, label) {
|
|
|
17664
17747
|
if (done) break;
|
|
17665
17748
|
size += value.byteLength;
|
|
17666
17749
|
if (size > maxBytes) throw new TypeError(`${label} exceeds limit`);
|
|
17667
|
-
|
|
17750
|
+
text10 += decoder.decode(value, { stream: true });
|
|
17668
17751
|
}
|
|
17669
17752
|
try {
|
|
17670
|
-
return JSON.parse(
|
|
17753
|
+
return JSON.parse(text10 + decoder.decode());
|
|
17671
17754
|
} catch {
|
|
17672
17755
|
throw new TypeError(`invalid ${label}`);
|
|
17673
17756
|
}
|