@odla-ai/chapter 0.54.2 → 0.56.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/dist/index.js CHANGED
@@ -122,8 +122,8 @@ async function submitApplication(db, chapter, fields, opts) {
122
122
  if (app.requireDisclaimerAck && !acked) {
123
123
  return { ok: false, error: "disclaimerAck is required" };
124
124
  }
125
- const id10 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
126
- const row2 = { id: id10, status: chapter.pipeline.initial, createdAt: opts.now };
125
+ const id11 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
126
+ const row2 = { id: id11, status: chapter.pipeline.initial, createdAt: opts.now };
127
127
  for (const f of [...app.required, ...app.optional]) {
128
128
  if (fieldStates[f]?.visible === false) continue;
129
129
  if (typeof fields[f] === "string") row2[f] = fields[f].trim();
@@ -157,12 +157,12 @@ async function submitApplication(db, chapter, fields, opts) {
157
157
  }
158
158
  if (acked) row2.disclaimerAckAt = opts.now;
159
159
  const ops = [
160
- { t: "update", ns: "applications", id: id10, attrs: row2 }
160
+ { t: "update", ns: "applications", id: id11, attrs: row2 }
161
161
  ];
162
162
  if (opts.admissionGrant?.decision === "waive_interview") {
163
163
  ops.push({ t: "update", ns: "admissionGrants", id: opts.admissionGrant.id, attrs: {
164
164
  consumedAt: opts.now,
165
- consumedApplicationId: id10
165
+ consumedApplicationId: id11
166
166
  } });
167
167
  }
168
168
  try {
@@ -170,7 +170,7 @@ async function submitApplication(db, chapter, fields, opts) {
170
170
  ops,
171
171
  opts.submissionId ? { mutationId: `join:${opts.submissionId}` } : void 0
172
172
  );
173
- return { ok: true, id: id10, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
173
+ return { ok: true, id: id11, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
174
174
  } catch (error) {
175
175
  if (error.code === "unique_violation") return {
176
176
  ok: false,
@@ -559,6 +559,7 @@ var subscriptionCheckoutIntentsEntity = {
559
559
  interval: attr5("string", { indexed: true }),
560
560
  billingTerms: attr5("json", { optional: true }),
561
561
  paymentQuote: attr5("json", { optional: true }),
562
+ paymentReceipt: attr5("json", { optional: true }),
562
563
  membershipOffer: attr5("json", { optional: true }),
563
564
  purchaserEmailNormalized: attr5("string", { indexed: true }),
564
565
  requestedCustomerId: attr5("string", { indexed: true, optional: true }),
@@ -1705,9 +1706,9 @@ var APPLICATION_LABELS = {
1705
1706
  phone: "Phone",
1706
1707
  state: "State"
1707
1708
  };
1708
- function humanize(id10) {
1709
- const text6 = id10.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1710
- return text6 ? `${text6[0].toUpperCase()}${text6.slice(1)}` : id10;
1709
+ function humanize(id11) {
1710
+ const text6 = id11.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1711
+ return text6 ? `${text6[0].toUpperCase()}${text6.slice(1)}` : id11;
1711
1712
  }
1712
1713
  function applicationDefs(chapter) {
1713
1714
  try {
@@ -1716,23 +1717,23 @@ function applicationDefs(chapter) {
1716
1717
  return {};
1717
1718
  }
1718
1719
  }
1719
- function applicationType(id10, def) {
1720
+ function applicationType(id11, def) {
1720
1721
  if (def && ["string", "email", "url", "enum"].includes(def.type)) return def.type;
1721
- return id10 === "email" ? "email" : id10 === "linkedin" ? "url" : "string";
1722
+ return id11 === "email" ? "email" : id11 === "linkedin" ? "url" : "string";
1722
1723
  }
1723
1724
  function applicationFlowFields(chapter, application = chapter.application) {
1724
1725
  const defs = applicationDefs(chapter);
1725
- return [...application.required, ...application.optional].map((id10, order2) => {
1726
- const def = defs[id10];
1727
- const conditions = application.conditions[id10];
1728
- const type = applicationType(id10, def);
1726
+ return [...application.required, ...application.optional].map((id11, order2) => {
1727
+ const def = defs[id11];
1728
+ const conditions = application.conditions[id11];
1729
+ const type = applicationType(id11, def);
1729
1730
  return {
1730
- id: id10,
1731
- label: def?.label ?? APPLICATION_LABELS[id10] ?? humanize(id10),
1731
+ id: id11,
1732
+ label: def?.label ?? APPLICATION_LABELS[id11] ?? humanize(id11),
1732
1733
  type,
1733
- required: application.required.includes(id10),
1734
+ required: application.required.includes(id11),
1734
1735
  order: order2,
1735
- maxLength: application.maxLen[id10] ?? application.defaultMaxLen,
1736
+ maxLength: application.maxLen[id11] ?? application.defaultMaxLen,
1736
1737
  ...type === "enum" && def?.options ? { options: [...def.options] } : {},
1737
1738
  ...conditions ? { conditions: { ...conditions } } : {},
1738
1739
  ...type === "email" ? { emailIdentity: "literal" } : {}
@@ -1742,17 +1743,17 @@ function applicationFlowFields(chapter, application = chapter.application) {
1742
1743
  function resolveFormationFlowFields(crm, formation) {
1743
1744
  if (!formation.enabled) return [];
1744
1745
  const def = crm.type(formation.type);
1745
- return [...formation.required, ...formation.optional].map((id10, order2) => {
1746
- const field2 = def.fields[id10];
1746
+ return [...formation.required, ...formation.optional].map((id11, order2) => {
1747
+ const field2 = def.fields[id11];
1747
1748
  return {
1748
- id: id10,
1749
- label: field2.label ?? humanize(id10),
1749
+ id: id11,
1750
+ label: field2.label ?? humanize(id11),
1750
1751
  type: field2.type,
1751
- required: formation.required.includes(id10),
1752
+ required: formation.required.includes(id11),
1752
1753
  order: order2,
1753
- maxLength: formation.maxLen[id10] ?? formation.defaultMaxLen,
1754
+ maxLength: formation.maxLen[id11] ?? formation.defaultMaxLen,
1754
1755
  ...field2.options ? { options: [...field2.options] } : {},
1755
- ...id10 === def.emailField || field2.type === "email" ? { emailIdentity: "literal" } : {}
1756
+ ...id11 === def.emailField || field2.type === "email" ? { emailIdentity: "literal" } : {}
1756
1757
  };
1757
1758
  });
1758
1759
  }
@@ -2056,12 +2057,12 @@ function resolveNetworkPersonRegistry(mode, input, targetIds) {
2056
2057
  if (input.mode !== "shadow") throw new Error('defineChapter.networkPersonRegistry.mode: must be "shadow"');
2057
2058
  const chapterIds = input.chapterIds ? [...input.chapterIds] : [...targetIds];
2058
2059
  if (chapterIds.length === 0) throw new Error("defineChapter.networkPersonRegistry.chapterIds: at least one chapter is required");
2059
- if (new Set(chapterIds).size !== chapterIds.length || chapterIds.some((id10) => !SLUG2.test(id10))) {
2060
+ if (new Set(chapterIds).size !== chapterIds.length || chapterIds.some((id11) => !SLUG2.test(id11))) {
2060
2061
  throw new Error("defineChapter.networkPersonRegistry.chapterIds: expected distinct lowercase slugs");
2061
2062
  }
2062
- const unknown = chapterIds.filter((id10) => !targetIds.includes(id10));
2063
+ const unknown = chapterIds.filter((id11) => !targetIds.includes(id11));
2063
2064
  if (unknown.length) {
2064
- throw new Error(`defineChapter.networkPersonRegistry.chapterIds: ${unknown.map((id10) => `"${id10}"`).join(", ")} are not configured network targets`);
2065
+ throw new Error(`defineChapter.networkPersonRegistry.chapterIds: ${unknown.map((id11) => `"${id11}"`).join(", ")} are not configured network targets`);
2065
2066
  }
2066
2067
  return { mode: "shadow", chapterIds };
2067
2068
  }
@@ -2184,9 +2185,9 @@ function isResolvedCrm(x) {
2184
2185
  }
2185
2186
  function defineChapter(config) {
2186
2187
  if (!config || typeof config !== "object") throw new Error("defineChapter: a config object is required");
2187
- const { id: id10, name } = config;
2188
- if (typeof id10 !== "string" || !SLUG7.test(id10)) {
2189
- throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id10)}`);
2188
+ const { id: id11, name } = config;
2189
+ if (typeof id11 !== "string" || !SLUG7.test(id11)) {
2190
+ throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id11)}`);
2190
2191
  }
2191
2192
  if (typeof name !== "string" || name.trim() === "") throw new Error("defineChapter.name: a non-empty name is required");
2192
2193
  const mode = config.mode ?? "chapter";
@@ -2293,7 +2294,7 @@ function defineChapter(config) {
2293
2294
  };
2294
2295
  const chapter = {
2295
2296
  config,
2296
- id: id10,
2297
+ id: id11,
2297
2298
  name,
2298
2299
  mode,
2299
2300
  brand,
@@ -2708,14 +2709,14 @@ function facts(snapshots) {
2708
2709
  ["payment_intent.updated", snapshots.payments?.objects ?? []]
2709
2710
  ];
2710
2711
  return groups2.flatMap(([eventType, objects]) => objects.map((object2) => {
2711
- const id10 = typeof object2.id === "string" ? object2.id : "invalid";
2712
+ const id11 = typeof object2.id === "string" ? object2.id : "invalid";
2712
2713
  return normalizeNetworkCommercialStripeEvent({
2713
- id: `evt_snapshot_${eventType.replaceAll(".", "_")}_${id10}`,
2714
+ id: `evt_snapshot_${eventType.replaceAll(".", "_")}_${id11}`,
2714
2715
  type: eventType,
2715
2716
  created: typeof object2.created === "number" ? object2.created : 0,
2716
2717
  livemode: object2.livemode === true,
2717
2718
  data: { object: object2 }
2718
- }, `stripe-snapshot:${eventType}:${id10}`);
2719
+ }, `stripe-snapshot:${eventType}:${id11}`);
2719
2720
  }));
2720
2721
  }
2721
2722
  var selected = (row2) => ({
@@ -3128,7 +3129,7 @@ function findTier(tiers2, tierId) {
3128
3129
  if (!tierId) return tiers2.length === 1 ? tiers2[0] : null;
3129
3130
  return tiers2.find((tier) => tier.id === tierId) ?? null;
3130
3131
  }
3131
- function joinConfigTiers(tiers2, defaults = {}, founding = /* @__PURE__ */ new Map()) {
3132
+ function joinConfigTiers(tiers2, defaults = {}, founding = /* @__PURE__ */ new Map(), seats = /* @__PURE__ */ new Map()) {
3132
3133
  return tiers2.map((tier) => ({
3133
3134
  id: tier.id,
3134
3135
  name: tier.name,
@@ -3137,7 +3138,8 @@ function joinConfigTiers(tiers2, defaults = {}, founding = /* @__PURE__ */ new M
3137
3138
  interval: tier.interval ?? defaults.interval ?? "year",
3138
3139
  blurb: tier.blurb ?? "",
3139
3140
  free: tierIsFree(tier),
3140
- ...founding.has(tier.id) ? { founding: founding.get(tier.id) } : {}
3141
+ ...founding.has(tier.id) ? { founding: founding.get(tier.id) } : {},
3142
+ ...seats.has(tier.id) ? { seatOffer: seats.get(tier.id) } : {}
3141
3143
  }));
3142
3144
  }
3143
3145
 
@@ -3424,8 +3426,8 @@ async function hash(value) {
3424
3426
  async function membershipDiscountCouponId(input, policyId) {
3425
3427
  return policyId ? `odla_m_${await hash([input.appId, input.chapterId, input.environment, input.runtime, input.skuId, policyId])}` : void 0;
3426
3428
  }
3427
- async function load(db, ns, id10) {
3428
- return (await db.query({ [ns]: { $: { where: { id: id10 }, limit: 1 } } }))[ns]?.[0];
3429
+ async function load(db, ns, id11) {
3430
+ return (await db.query({ [ns]: { $: { where: { id: id11 }, limit: 1 } } }))[ns]?.[0];
3429
3431
  }
3430
3432
  async function membershipOfferIdForRequest(input) {
3431
3433
  return `offer_${await hash([input.appId, input.chapterId, input.environment, input.runtime, input.applicationId, input.requestId])}`;
@@ -3491,6 +3493,7 @@ async function previewMembershipCommercialOffer(db, input) {
3491
3493
  ...contract.founding && contract.discountPolicy ? { cohortLabel: contract.discountPolicy.cohortLabel } : {},
3492
3494
  dueTodayCents: contract.dueTodayCents,
3493
3495
  renewalCents: contract.renewalCents,
3496
+ ...offering.seatPriceCents !== void 0 ? { seatPriceCents: offering.seatPriceCents } : {},
3494
3497
  ...discount ? { discountDuration: discount.duration === "once" ? "first_term" : "while_active" } : {},
3495
3498
  ...contract.initialTermEndsAt !== void 0 ? { initialTermEndsAt: contract.initialTermEndsAt } : {}
3496
3499
  };
@@ -3562,20 +3565,20 @@ import { stripeRequest as stripeRequest2 } from "@odla-ai/stripe";
3562
3565
 
3563
3566
  // src/commercial-evidence-guard.ts
3564
3567
  async function commercialEvidenceGuard(db, eventId, digest, code = "membership_payment_evidence_quarantined") {
3565
- const ns = "networkCommercialExceptions", id10 = `network-commercial-exception:${eventId}:collision`;
3566
- const row2 = (await db.query({ [ns]: { $: { where: { id: id10 }, limit: 1 } } }))[ns]?.[0];
3567
- if (!row2) return { ns, id: id10, exists: false };
3568
+ const ns = "networkCommercialExceptions", id11 = `network-commercial-exception:${eventId}:collision`;
3569
+ const row2 = (await db.query({ [ns]: { $: { where: { id: id11 }, limit: 1 } } }))[ns]?.[0];
3570
+ if (!row2) return { ns, id: id11, exists: false };
3568
3571
  if (row2.status !== "resolved" || row2.resolvedDigest !== digest || typeof digest !== "string") {
3569
3572
  throw Object.assign(new Error(code), { code });
3570
3573
  }
3571
- return { ns, id: id10, exists: true, equals: { status: "resolved", resolvedDigest: digest, updatedAt: row2.updatedAt } };
3574
+ return { ns, id: id11, exists: true, equals: { status: "resolved", resolvedDigest: digest, updatedAt: row2.updatedAt } };
3572
3575
  }
3573
3576
 
3574
3577
  // src/membership-authority-state.ts
3575
3578
  var membershipFail = (code) => {
3576
3579
  throw Object.assign(new Error(code), { code });
3577
3580
  };
3578
- var membershipLoad = async (db, ns, id10) => (await db.query({ [ns]: { $: { where: { id: id10 }, limit: 1 } } }))[ns]?.[0];
3581
+ var membershipLoad = async (db, ns, id11) => (await db.query({ [ns]: { $: { where: { id: id11 }, limit: 1 } } }))[ns]?.[0];
3579
3582
  var membershipScopeKeys = ["appId", "chapterId", "environment", "runtime", "applicationId", "skuId"];
3580
3583
  var membershipScope = (input) => Object.fromEntries(membershipScopeKeys.map((key3) => [key3, input[key3]]));
3581
3584
  function requireMembershipScope(row2, input) {
@@ -3586,18 +3589,18 @@ async function loadMembershipAuthorityState(db, input) {
3586
3589
  if (!row2 || row2.mode !== "authoritative" || row2.chapterId !== input.chapterId || row2.environment !== input.environment || !Number.isSafeInteger(row2.revision) || Number(row2.revision) < 1) return membershipFail("membership_authority_unavailable");
3587
3590
  return row2;
3588
3591
  }
3589
- var membershipUpdate = (ns, id10, attrs) => ({ t: "update", ns, id: id10, attrs });
3592
+ var membershipUpdate = (ns, id11, attrs) => ({ t: "update", ns, id: id11, attrs });
3590
3593
  var membershipClock = (now) => {
3591
3594
  if (!Number.isSafeInteger(now) || now < 0) membershipFail("invalid_membership_clock");
3592
3595
  };
3593
3596
 
3594
3597
  // src/membership-restart-payment.ts
3595
3598
  async function membershipRestartPayment(db, target, member, now) {
3596
- const id10 = String(member.id), restart = await membershipLoad(db, "membershipRestart", id10);
3599
+ const id11 = String(member.id), restart = await membershipLoad(db, "membershipRestart", id11);
3597
3600
  requireMembershipScope(restart, target);
3598
- const entitlement = await membershipLoad(db, "networkEntitlement", `${id10}:primary`);
3601
+ const entitlement = await membershipLoad(db, "networkEntitlement", `${id11}:primary`);
3599
3602
  requireMembershipScope(entitlement, target);
3600
- if (entitlement.membershipId !== id10 || entitlement.kind !== "primary") membershipFail("membership_restart_conflict");
3603
+ if (entitlement.membershipId !== id11 || entitlement.kind !== "primary") membershipFail("membership_restart_conflict");
3601
3604
  if (restart.status !== "ready" || member.status !== "restarting" || member.restartId !== restart.restartId || member.memberNumber !== restart.memberNumber || !Number.isSafeInteger(member.journeyApprovedAt)) membershipFail("membership_restart_conflict");
3602
3605
  return {
3603
3606
  restartId: String(restart.restartId),
@@ -3614,49 +3617,117 @@ async function membershipRestartPayment(db, target, member, now) {
3614
3617
  graceEndsAt: 0
3615
3618
  },
3616
3619
  ops: [
3617
- membershipUpdate("membershipRestart", id10, { status: "complete" }),
3618
- { t: "retract", ns: "networkMembership", id: id10, attrs: ["refundRequestedAt", "cancellationRequestedAt", "discountPolicyVersionId", "discountSnapshot"] }
3620
+ membershipUpdate("membershipRestart", id11, { status: "complete" }),
3621
+ { t: "retract", ns: "networkMembership", id: id11, attrs: ["refundRequestedAt", "cancellationRequestedAt", "discountPolicyVersionId", "discountSnapshot"] }
3619
3622
  ],
3620
3623
  guards: [
3621
- { ns: "networkMembership", id: id10, exists: true, equals: { status: "restarting", revision: member.revision, restartId: restart.restartId } },
3622
- { ns: "membershipRestart", id: id10, exists: true, equals: { status: "ready", restartId: restart.restartId } },
3624
+ { ns: "networkMembership", id: id11, exists: true, equals: { status: "restarting", revision: member.revision, restartId: restart.restartId } },
3625
+ { ns: "membershipRestart", id: id11, exists: true, equals: { status: "ready", restartId: restart.restartId } },
3623
3626
  { ns: "networkEntitlement", id: String(entitlement.id), exists: true, equals: { revision: entitlement.revision, status: entitlement.status } }
3624
3627
  ]
3625
3628
  };
3626
3629
  }
3627
3630
 
3631
+ // src/checkout-named-seat.ts
3632
+ function checkoutNamedSeatSelection(value, purchaserEmail) {
3633
+ const row2 = value;
3634
+ const name = typeof row2?.recipientName === "string" ? row2.recipientName.trim().replace(/\s+/g, " ") : "";
3635
+ const email = normalizeChapterEmail(typeof row2?.recipientEmail === "string" ? row2.recipientEmail : "");
3636
+ const purchaser = normalizeChapterEmail(purchaserEmail);
3637
+ if (!row2 || typeof row2 !== "object" || Array.isArray(row2) || name.length < 2 || name.length > 160 || /[\u0000-\u001f\u007f]/.test(row2.recipientName) || email.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(purchaser) || email === purchaser) {
3638
+ throw Object.assign(new Error("Enter a different recipient's name and email."), { code: "invalid_named_seat_recipient" });
3639
+ }
3640
+ return { recipientName: name, recipientEmail: email };
3641
+ }
3642
+ async function checkoutNamedSeatIdentity(target, email) {
3643
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify([
3644
+ target.appId,
3645
+ target.chapterId,
3646
+ target.environment,
3647
+ target.runtime,
3648
+ email
3649
+ ])));
3650
+ return {
3651
+ seatId: (await membershipOfferIdForRequest({ ...target, requestId: "named_seat" })).replace(/^offer_/, "seat_"),
3652
+ membershipId: (await membershipOfferIdForRequest({ ...target, requestId: "checkout_binding" })).replace(/^offer_/, "membership_"),
3653
+ recipientClaimId: `seat-recipient:${[...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`
3654
+ };
3655
+ }
3656
+
3628
3657
  // src/membership-checkout-binding.ts
3629
3658
  var keys = ["appId", "chapterId", "environment", "runtime", "applicationId", "skuId", "offerId", "offerDigest", "quoteDigest", "priceId"];
3630
3659
  var reject2 = (code) => {
3631
3660
  throw Object.assign(new Error(code), { code });
3632
3661
  };
3633
- var load2 = async (db, ns, id10) => (await db.query({ [ns]: { $: { where: { id: id10 }, limit: 1 } } }))[ns]?.[0];
3662
+ var load2 = async (db, ns, id11) => (await db.query({ [ns]: { $: { where: { id: id11 }, limit: 1 } } }))[ns]?.[0];
3634
3663
  async function membershipCheckoutBindingId(input) {
3635
3664
  return (await membershipOfferIdForRequest({ ...input, requestId: "checkout_binding" })).replace(/^offer_/, "checkout_");
3636
3665
  }
3637
3666
  async function verifyMembershipCheckoutBinding(value, input) {
3638
3667
  const row2 = value;
3639
3668
  if (!row2 || typeof row2 !== "object" || row2.id !== await membershipCheckoutBindingId(input) || keys.some((key3) => row2[key3] !== input[key3]) || !Number.isSafeInteger(row2.boundAt) || row2.boundAt < 0) return reject2("membership_checkout_conflict");
3640
- return { id: row2.id, ...Object.fromEntries(keys.map((key3) => [key3, input[key3]])), boundAt: row2.boundAt };
3669
+ if (Boolean(row2.namedSeat) !== Boolean(input.namedSeat)) return reject2("membership_checkout_conflict");
3670
+ if (input.namedSeat) {
3671
+ const expected = {
3672
+ ...checkoutNamedSeatSelection(input.namedSeat, input.namedSeat.purchaserEmail),
3673
+ purchaserEmail: normalizeChapterEmail(input.namedSeat.purchaserEmail)
3674
+ };
3675
+ const identity2 = await checkoutNamedSeatIdentity(input, expected.recipientEmail);
3676
+ if (!row2.namedSeat || Object.entries({ ...expected, ...identity2 }).some(([key3, value2]) => row2.namedSeat[key3] !== value2) || !/^price_[A-Za-z0-9_]+$/.test(row2.namedSeat.priceId) || row2.namedSeat.priceId === input.priceId || !Number.isSafeInteger(row2.namedSeat.amountCents) || row2.namedSeat.amountCents < 0) return reject2("membership_checkout_conflict");
3677
+ }
3678
+ return {
3679
+ id: row2.id,
3680
+ ...Object.fromEntries(keys.map((key3) => [key3, input[key3]])),
3681
+ boundAt: row2.boundAt,
3682
+ ...row2.namedSeat ? { namedSeat: row2.namedSeat } : {}
3683
+ };
3641
3684
  }
3642
- async function bindMembershipCheckout(db, input, now) {
3685
+ async function bindMembershipCheckout(db, input, now, seatPriceId) {
3643
3686
  if (!Number.isSafeInteger(now) || now < 0 || !/^[a-f0-9]{64}$/.test(input.offerDigest) || !/^[a-f0-9]{64}$/.test(input.quoteDigest) || !/^price_[A-Za-z0-9_]+$/.test(input.priceId)) return reject2("invalid_membership_checkout");
3644
3687
  const stateId = `${input.chapterId}:${input.environment}`;
3645
3688
  const state = await load2(db, "membershipAuthorityState", stateId);
3646
3689
  if (!state || state.mode !== "authoritative" || state.chapterId !== input.chapterId || state.environment !== input.environment || !Number.isSafeInteger(state.revision) || Number(state.revision) < 1) return reject2("membership_authority_unavailable");
3647
- const id10 = await membershipCheckoutBindingId(input);
3648
- const existing = await load2(db, "membershipOfferCheckout", id10);
3690
+ const id11 = await membershipCheckoutBindingId(input);
3691
+ const existing = await load2(db, "membershipOfferCheckout", id11);
3649
3692
  if (existing) return verifyMembershipCheckoutBinding(existing, input);
3650
3693
  const row2 = await load2(db, "membershipCheckoutOffer", input.offerId);
3651
3694
  const offer = await verifyIssuedMembershipOffer(row2, { ...input, now, lifetimeMs: 6e4 });
3652
3695
  if (offer.digest !== input.offerDigest || offer.expiresAt <= now || offer.issuedAt > now) return reject2("membership_offer_expired");
3653
- const restart = await load2(db, "membershipRestart", id10.replace(/^checkout_/, "membership_"));
3654
- const receipt = { id: id10, ...Object.fromEntries(keys.map((key3) => [key3, input[key3]])), boundAt: now };
3696
+ const restart = await load2(db, "membershipRestart", id11.replace(/^checkout_/, "membership_"));
3697
+ let namedSeat;
3698
+ if (input.namedSeat) {
3699
+ if (!seatPriceId || !/^price_[A-Za-z0-9_]+$/.test(seatPriceId) || seatPriceId === input.priceId || restart) return reject2("named_seat_purchase_unavailable");
3700
+ const selection = checkoutNamedSeatSelection(input.namedSeat, input.namedSeat.purchaserEmail);
3701
+ namedSeat = {
3702
+ ...selection,
3703
+ purchaserEmail: normalizeChapterEmail(input.namedSeat.purchaserEmail),
3704
+ ...await checkoutNamedSeatIdentity(input, selection.recipientEmail),
3705
+ priceId: seatPriceId,
3706
+ amountCents: offer.contract.offering.seatPriceCents
3707
+ };
3708
+ }
3709
+ const receipt = { id: id11, ...Object.fromEntries(keys.map((key3) => [key3, input[key3]])), boundAt: now, ...namedSeat ? { namedSeat } : {} };
3655
3710
  try {
3656
- await db.transact([{ t: "update", ns: "membershipOfferCheckout", id: id10, attrs: receipt }], {
3657
- mutationId: `membership-checkout:${id10}${restart?.restartId ? `:${restart.restartId}` : ""}`,
3711
+ await db.transact([
3712
+ { t: "update", ns: "membershipOfferCheckout", id: id11, attrs: receipt },
3713
+ ...namedSeat ? [{ t: "update", ns: "networkSeatRecipient", id: namedSeat.recipientClaimId, attrs: {
3714
+ id: namedSeat.recipientClaimId,
3715
+ appId: input.appId,
3716
+ chapterId: input.chapterId,
3717
+ environment: input.environment,
3718
+ runtime: input.runtime,
3719
+ seatId: namedSeat.seatId,
3720
+ recipientEmailNormalized: namedSeat.recipientEmail,
3721
+ createdAt: now
3722
+ } }] : []
3723
+ ], {
3724
+ mutationId: `membership-checkout:${id11}${restart?.restartId ? `:${restart.restartId}` : ""}`,
3658
3725
  guards: [
3659
- { ns: "membershipOfferCheckout", id: id10, exists: false },
3726
+ { ns: "membershipOfferCheckout", id: id11, exists: false },
3727
+ ...namedSeat ? [
3728
+ { ns: "networkSeat", id: namedSeat.seatId, exists: false },
3729
+ { ns: "networkSeatRecipient", id: namedSeat.recipientClaimId, exists: false }
3730
+ ] : [],
3660
3731
  { ns: "membershipCheckoutOffer", id: input.offerId, exists: true, equals: { digest: input.offerDigest } },
3661
3732
  { ns: "membershipAuthorityState", id: stateId, exists: true, equals: { mode: "authoritative", revision: state.revision } }
3662
3733
  ]
@@ -3664,11 +3735,78 @@ async function bindMembershipCheckout(db, input, now) {
3664
3735
  } catch (error) {
3665
3736
  if (error.code !== "transact_guard_failed") throw error;
3666
3737
  }
3667
- const saved = await load2(db, "membershipOfferCheckout", id10);
3738
+ const saved = await load2(db, "membershipOfferCheckout", id11);
3668
3739
  if (!saved) return reject2("membership_binding_unconfirmed");
3669
3740
  return verifyMembershipCheckoutBinding(saved, input);
3670
3741
  }
3671
3742
 
3743
+ // src/membership-checkout-seat-payment.ts
3744
+ function checkoutSeatPaymentOperations(target, seat, input) {
3745
+ const { now, paidAt, endsAt } = input;
3746
+ const included = seat.amountCents === 0, entitlementId = `${seat.seatId}:entitlement`;
3747
+ const effectId = `${seat.seatId}:${included ? "invite" : "attach-renewal"}`;
3748
+ return [
3749
+ membershipUpdate("networkSeat", seat.seatId, {
3750
+ id: seat.seatId,
3751
+ ...membershipScope(target),
3752
+ primaryMembershipId: seat.membershipId,
3753
+ purchaserEmailNormalized: seat.purchaserEmail,
3754
+ recipientName: seat.recipientName,
3755
+ recipientEmailNormalized: seat.recipientEmail,
3756
+ recipientClaimId: seat.recipientClaimId,
3757
+ priceId: seat.priceId,
3758
+ primaryPriceId: input.primaryPriceId,
3759
+ amountCents: seat.amountCents,
3760
+ currency: input.offering.currency,
3761
+ interval: input.offering.interval,
3762
+ providerCustomerId: input.customerId,
3763
+ providerSubscriptionId: input.subscriptionId,
3764
+ combinedInvoice: { invoiceId: input.invoiceId, totalCents: input.invoiceTotalCents },
3765
+ status: included ? "pending_acceptance" : "renewal_pending",
3766
+ preparedAt: input.boundAt,
3767
+ purchasedAt: paidAt,
3768
+ termStartedAt: paidAt,
3769
+ termEndsAt: endsAt,
3770
+ acceptanceEndsAt: endsAt,
3771
+ seatPurchaseEndsAt: seatPurchaseDeadline(input.offering, { initialTermStartedAt: paidAt, termEndsAt: endsAt }),
3772
+ offeringSnapshot: input.offering,
3773
+ paymentEventId: input.eventId,
3774
+ ...included ? { entitlementId } : {},
3775
+ revision: 1,
3776
+ createdAt: now,
3777
+ updatedAt: now
3778
+ }),
3779
+ ...included ? [membershipUpdate("networkEntitlement", entitlementId, {
3780
+ id: entitlementId,
3781
+ ...membershipScope(target),
3782
+ membershipId: seat.membershipId,
3783
+ seatId: seat.seatId,
3784
+ kind: "named_seat",
3785
+ status: "pending_acceptance",
3786
+ termStartedAt: paidAt,
3787
+ termEndsAt: endsAt,
3788
+ offeringSnapshot: input.offering,
3789
+ sourceEventId: input.eventId,
3790
+ revision: 1,
3791
+ createdAt: now,
3792
+ updatedAt: now
3793
+ })] : [],
3794
+ membershipUpdate("membershipEffect", effectId, {
3795
+ id: effectId,
3796
+ chapterId: target.chapterId,
3797
+ environment: target.environment,
3798
+ runtime: target.runtime,
3799
+ membershipId: seat.membershipId,
3800
+ seatId: seat.seatId,
3801
+ kind: included ? "invite_named_recipient" : "attach_seat_renewal",
3802
+ status: "pending",
3803
+ attempts: 0,
3804
+ createdAt: now,
3805
+ updatedAt: now
3806
+ })
3807
+ ];
3808
+ }
3809
+
3672
3810
  // src/membership-payment-authority.ts
3673
3811
  async function networkMembershipId(input) {
3674
3812
  return (await membershipCheckoutBindingId(input)).replace(/^checkout_/, "membership_");
@@ -3684,11 +3822,14 @@ async function confirmMembershipPayment(db, input, now) {
3684
3822
  { ...input, offerId: String(binding.offerId), now, lifetimeMs: 6e4 }
3685
3823
  );
3686
3824
  if (binding.offerDigest !== offer.digest) membershipFail("membership_checkout_conflict");
3825
+ const { namedSeat } = await verifyMembershipCheckoutBinding(binding, binding);
3826
+ if (namedSeat && namedSeat.amountCents !== offer.contract.offering.seatPriceCents) membershipFail("membership_checkout_conflict");
3827
+ const totalCents = offer.contract.dueTodayCents + (namedSeat?.amountCents ?? 0);
3687
3828
  const eventRow = await membershipLoad(db, "networkCommercialEvents", `network-commercial-event:${input.eventId}`);
3688
3829
  if (!eventRow) membershipFail("membership_payment_evidence_pending");
3689
3830
  const evidenceGuard = await commercialEvidenceGuard(db, input.eventId, eventRow.digest);
3690
3831
  const fact = eventRow.fact;
3691
- if (!fact || !fact.identity || !/^[a-f0-9]{64}$/.test(String(eventRow.digest)) || fact.digest !== eventRow.digest || fact.eventId !== input.eventId || fact.eventType !== "invoice.payment_succeeded" || fact.objectKind !== "invoice" || fact.billingReason !== "subscription_create" || fact.livemode !== (input.environment === "prod") || fact.status !== "paid" || fact.applicationId !== input.applicationId || fact.subscriptionId !== input.providerSubscriptionId || fact.customerId !== input.providerCustomerId || !/^in_[A-Za-z0-9_]+$/.test(fact.invoiceId ?? "") || fact.currency !== offer.contract.offering.currency || fact.invoiceTotalCents !== offer.contract.dueTodayCents || fact.amountPaidCents !== offer.contract.dueTodayCents || Object.entries({
3832
+ if (!fact || !fact.identity || !/^[a-f0-9]{64}$/.test(String(eventRow.digest)) || fact.digest !== eventRow.digest || fact.eventId !== input.eventId || fact.eventType !== "invoice.payment_succeeded" || fact.objectKind !== "invoice" || fact.billingReason !== "subscription_create" || fact.livemode !== (input.environment === "prod") || fact.status !== "paid" || fact.applicationId !== input.applicationId || fact.subscriptionId !== input.providerSubscriptionId || fact.customerId !== input.providerCustomerId || !/^in_[A-Za-z0-9_]+$/.test(fact.invoiceId ?? "") || fact.currency !== offer.contract.offering.currency || fact.invoiceTotalCents !== totalCents || fact.amountPaidCents !== totalCents || Object.entries({
3692
3833
  appId: input.appId,
3693
3834
  chapterId: input.chapterId,
3694
3835
  environment: input.environment,
@@ -3700,14 +3841,14 @@ async function confirmMembershipPayment(db, input, now) {
3700
3841
  const paidAt = fact.invoicePaidAt;
3701
3842
  const endsAt = offer.contract.initialTermEndsAt ?? fact.invoicePeriodEndsAt;
3702
3843
  if (!Number.isSafeInteger(paidAt) || Number(paidAt) < Math.floor(Number(binding.boundAt) / 1e3) * 1e3 || Number(paidAt) > now + 3e4 || !Number.isSafeInteger(endsAt) || Number(endsAt) <= Number(paidAt)) membershipFail("membership_payment_period_mismatch");
3703
- const id10 = await networkMembershipId(input), entitlementId = `${id10}:primary`;
3704
- let member = await membershipLoad(db, "networkMembership", id10);
3844
+ const id11 = await networkMembershipId(input), entitlementId = `${id11}:primary`;
3845
+ let member = await membershipLoad(db, "networkMembership", id11);
3705
3846
  if (!member || member.status === "restarting") {
3706
3847
  const offering = offer.contract.offering;
3707
3848
  const restart = member ? await membershipRestartPayment(db, input, member, now) : null;
3708
3849
  const attrs = {
3709
- id: id10,
3710
- membershipId: id10,
3850
+ id: id11,
3851
+ membershipId: id11,
3711
3852
  ...membershipScope(input),
3712
3853
  baseSkuId: input.skuId,
3713
3854
  status: "paid_pending_approval",
@@ -3723,8 +3864,9 @@ async function confirmMembershipPayment(db, input, now) {
3723
3864
  founding: offer.contract.founding,
3724
3865
  cohort: offer.contract.founding ? offer.contract.discountPolicy.cohortLabel : "standard",
3725
3866
  baseAmountCents: offering.basePriceCents,
3726
- amountCents: offer.contract.dueTodayCents,
3867
+ amountCents: totalCents,
3727
3868
  renewalAmountCents: offer.contract.renewalCents,
3869
+ ...namedSeat ? { namedSeatId: namedSeat.seatId } : {},
3728
3870
  currency: offering.currency,
3729
3871
  interval: offering.interval,
3730
3872
  initialTermStartedAt: paidAt,
@@ -3741,11 +3883,11 @@ async function confirmMembershipPayment(db, input, now) {
3741
3883
  };
3742
3884
  try {
3743
3885
  await db.transact([
3744
- membershipUpdate("networkMembership", id10, attrs),
3886
+ membershipUpdate("networkMembership", id11, attrs),
3745
3887
  membershipUpdate("networkEntitlement", entitlementId, {
3746
3888
  id: entitlementId,
3747
3889
  ...membershipScope(input),
3748
- membershipId: id10,
3890
+ membershipId: id11,
3749
3891
  kind: "primary",
3750
3892
  status: restart?.attrs.status === "active" ? "active" : "pending_approval",
3751
3893
  termStartedAt: paidAt,
@@ -3757,23 +3899,45 @@ async function confirmMembershipPayment(db, input, now) {
3757
3899
  updatedAt: now,
3758
3900
  ...restart?.entitlementAttrs
3759
3901
  }),
3760
- ...restart?.ops ?? []
3761
- ], { mutationId: `membership-paid:${id10}${restart ? `:${restart.restartId}` : ""}`, guards: [
3762
- ...restart?.guards ?? [{ ns: "networkMembership", id: id10, exists: false }],
3902
+ ...restart?.ops ?? [],
3903
+ ...namedSeat ? checkoutSeatPaymentOperations(input, namedSeat, {
3904
+ offering,
3905
+ primaryPriceId: String(binding.priceId),
3906
+ invoiceId: fact.invoiceId,
3907
+ invoiceTotalCents: totalCents,
3908
+ subscriptionId: input.providerSubscriptionId,
3909
+ customerId: input.providerCustomerId,
3910
+ eventId: input.eventId,
3911
+ paidAt: Number(paidAt),
3912
+ endsAt: Number(endsAt),
3913
+ boundAt: Number(binding.boundAt),
3914
+ now
3915
+ }) : []
3916
+ ], { mutationId: `membership-paid:${id11}${restart ? `:${restart.restartId}` : ""}`, guards: [
3917
+ ...restart?.guards ?? [{ ns: "networkMembership", id: id11, exists: false }],
3918
+ ...namedSeat ? [
3919
+ { ns: "networkSeat", id: namedSeat.seatId, exists: false },
3920
+ {
3921
+ ns: "networkSeatRecipient",
3922
+ id: namedSeat.recipientClaimId,
3923
+ exists: true,
3924
+ equals: { seatId: namedSeat.seatId, recipientEmailNormalized: namedSeat.recipientEmail }
3925
+ }
3926
+ ] : [],
3763
3927
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } },
3764
- { ns: "membershipOfferCheckout", id: String(binding.id), exists: true, equals: { offerDigest: offer.digest } },
3928
+ { ns: "membershipOfferCheckout", id: String(binding.id), exists: true, equals: { offerDigest: offer.digest, quoteDigest: binding.quoteDigest } },
3765
3929
  { ns: "networkCommercialEvents", id: String(eventRow.id), exists: true, equals: { digest: eventRow.digest } },
3766
3930
  evidenceGuard
3767
3931
  ] });
3768
3932
  } catch (error) {
3769
3933
  if (error.code !== "transact_guard_failed") throw error;
3770
3934
  }
3771
- member = await membershipLoad(db, "networkMembership", id10);
3935
+ member = await membershipLoad(db, "networkMembership", id11);
3772
3936
  }
3773
3937
  requireMembershipScope(member, input);
3774
3938
  if (member.purchaseOfferDigest !== offer.digest || member.providerInitialInvoiceId !== fact.invoiceId || member.providerSubscriptionId !== fact.subscriptionId || member.providerCustomerId !== fact.customerId) membershipFail("membership_purchase_conflict");
3775
3939
  return {
3776
- membershipId: id10,
3940
+ membershipId: id11,
3777
3941
  status: String(member.status),
3778
3942
  cohort: String(member.cohort),
3779
3943
  initialTermStartedAt: Number(member.initialTermStartedAt),
@@ -3785,9 +3949,9 @@ async function confirmMembershipPayment(db, input, now) {
3785
3949
  // src/membership-cancel-authority.ts
3786
3950
  async function cancelMembershipAtTermEnd(db, target, now, stripeSecret) {
3787
3951
  membershipClock(now);
3788
- const id10 = await networkMembershipId(target);
3952
+ const id11 = await networkMembershipId(target);
3789
3953
  const state = await loadMembershipAuthorityState(db, target);
3790
- const member = await membershipLoad(db, "networkMembership", id10);
3954
+ const member = await membershipLoad(db, "networkMembership", id11);
3791
3955
  requireMembershipScope(member, target);
3792
3956
  if (!["active", "cancel_at_term_end", "payment_grace", "paid_pending_approval", "lapsed"].includes(String(member.status))) {
3793
3957
  membershipFail("membership_cancellation_unavailable");
@@ -3805,7 +3969,7 @@ async function cancelMembershipAtTermEnd(db, target, now, stripeSecret) {
3805
3969
  "POST",
3806
3970
  path,
3807
3971
  { cancel_at_period_end: true, proration_behavior: "none" },
3808
- { idempotencyKey: `membership-cancel-at-term:${id10}:${member.providerSubscriptionId}` }
3972
+ { idempotencyKey: `membership-cancel-at-term:${id11}:${member.providerSubscriptionId}` }
3809
3973
  );
3810
3974
  if (!write.ok || write.body.id !== member.providerSubscriptionId || write.body.cancel_at_period_end !== true) {
3811
3975
  membershipFail("membership_cancellation_pending");
@@ -3813,7 +3977,7 @@ async function cancelMembershipAtTermEnd(db, target, now, stripeSecret) {
3813
3977
  }
3814
3978
  const status2 = Number(member.termEndsAt) <= now ? "lapsed" : member.journeyApprovedAt ? "cancel_at_term_end" : "paid_pending_approval";
3815
3979
  if (member.status !== status2 || member.autoRenew !== false || Number(member.graceEndsAt ?? 0) !== 0) {
3816
- await db.transact([membershipUpdate("networkMembership", id10, {
3980
+ await db.transact([membershipUpdate("networkMembership", id11, {
3817
3981
  status: status2,
3818
3982
  autoRenew: false,
3819
3983
  graceEndsAt: 0,
@@ -3822,12 +3986,12 @@ async function cancelMembershipAtTermEnd(db, target, now, stripeSecret) {
3822
3986
  updatedAt: now
3823
3987
  })], {
3824
3988
  guards: [
3825
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: member.revision } },
3989
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: member.revision } },
3826
3990
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { revision: state.revision, mode: "authoritative" } }
3827
3991
  ]
3828
3992
  });
3829
3993
  }
3830
- return { membershipId: id10, status: status2, termEndsAt: Number(member.termEndsAt), graceEndsAt: 0, autoRenew: false };
3994
+ return { membershipId: id11, status: status2, termEndsAt: Number(member.termEndsAt), graceEndsAt: 0, autoRenew: false };
3831
3995
  }
3832
3996
 
3833
3997
  // src/membership-authority-handler.ts
@@ -3866,17 +4030,17 @@ async function namedSeatPrimaryActivation(db, member, now) {
3866
4030
  // src/membership-approval-authority.ts
3867
4031
  async function approveNetworkMembership(db, input, now) {
3868
4032
  membershipClock(now);
3869
- const id10 = await networkMembershipId(input), entitlementId = `${id10}:primary`;
4033
+ const id11 = await networkMembershipId(input), entitlementId = `${id11}:primary`;
3870
4034
  for (let attempt = 0; attempt < 8; attempt++) {
3871
4035
  const state = await loadMembershipAuthorityState(db, input);
3872
- const member = await membershipLoad(db, "networkMembership", id10);
4036
+ const member = await membershipLoad(db, "networkMembership", id11);
3873
4037
  requireMembershipScope(member, input);
3874
4038
  const entitlement = await membershipLoad(db, "networkEntitlement", entitlementId);
3875
4039
  requireMembershipScope(entitlement, input);
3876
- if (entitlement.membershipId !== id10 || entitlement.kind !== "primary") membershipFail("membership_entitlement_conflict");
4040
+ if (entitlement.membershipId !== id11 || entitlement.kind !== "primary") membershipFail("membership_entitlement_conflict");
3877
4041
  if (["active", "cancel_at_term_end"].includes(String(member.status)) && entitlement.status === "active" && Number(member.refundedCents) === 0 && Number.isSafeInteger(member.memberNumber) && Number(member.memberNumber) > 0 && Number(member.termEndsAt) > now) {
3878
4042
  return {
3879
- membershipId: id10,
4043
+ membershipId: id11,
3880
4044
  status: String(member.status),
3881
4045
  cohort: String(member.cohort),
3882
4046
  memberNumber: Number(member.memberNumber),
@@ -3896,7 +4060,7 @@ async function approveNetworkMembership(db, input, now) {
3896
4060
  const seatActivation = await namedSeatPrimaryActivation(db, member, now);
3897
4061
  try {
3898
4062
  await db.transact([
3899
- membershipUpdate("networkMembership", id10, {
4063
+ membershipUpdate("networkMembership", id11, {
3900
4064
  status: member.autoRenew ? "active" : "cancel_at_term_end",
3901
4065
  memberNumber,
3902
4066
  journeyApprovedAt: now,
@@ -3911,8 +4075,8 @@ async function approveNetworkMembership(db, input, now) {
3911
4075
  updatedAt: now
3912
4076
  }),
3913
4077
  ...seatActivation.ops
3914
- ], { mutationId: `membership-approve:${id10}`, guards: [
3915
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: member.revision, status: "paid_pending_approval" }, absent: ["memberNumber"] },
4078
+ ], { mutationId: `membership-approve:${id11}`, guards: [
4079
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: member.revision, status: "paid_pending_approval" }, absent: ["memberNumber"] },
3916
4080
  { ns: "networkEntitlement", id: entitlementId, exists: true, equals: { revision: entitlement.revision, status: "pending_approval" } },
3917
4081
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { revision: state.revision, mode: "authoritative" } },
3918
4082
  ...seatActivation.guards
@@ -4011,41 +4175,41 @@ async function refundSubscriptionPayment(secretKey, input) {
4011
4175
  // src/membership-refund-authority.ts
4012
4176
  async function refundNetworkMembership(db, input, secretKey, now) {
4013
4177
  membershipClock(now);
4014
- const id10 = await networkMembershipId(input), entitlementId = `${id10}:primary`;
4015
- let state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id10);
4178
+ const id11 = await networkMembershipId(input), entitlementId = `${id11}:primary`;
4179
+ let state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id11);
4016
4180
  requireMembershipScope(member, input);
4017
- if (member.status === "refunded") return { membershipId: id10, status: "refunded", refundedCents: Number(member.refundedCents), subscriptionCanceled: true };
4181
+ if (member.status === "refunded") return { membershipId: id11, status: "refunded", refundedCents: Number(member.refundedCents), subscriptionCanceled: true };
4018
4182
  if (!["paid_pending_approval", "active", "cancel_at_term_end", "payment_grace", "lapsed", "refund_pending"].includes(String(member.status))) membershipFail("membership_refund_not_eligible");
4019
4183
  if (member.status !== "refund_pending") {
4020
4184
  const entitlement = await membershipLoad(db, "networkEntitlement", entitlementId);
4021
4185
  requireMembershipScope(entitlement, input);
4022
4186
  await db.transact([
4023
- membershipUpdate("networkMembership", id10, {
4187
+ membershipUpdate("networkMembership", id11, {
4024
4188
  status: "refund_pending",
4025
4189
  refundRequestedAt: now,
4026
4190
  revision: Number(member.revision) + 1,
4027
4191
  updatedAt: now
4028
4192
  }),
4029
4193
  membershipUpdate("networkEntitlement", entitlementId, { status: "closed", revision: Number(entitlement.revision) + 1, updatedAt: now }),
4030
- membershipUpdate("membershipEffect", `${id10}:refund`, {
4031
- id: `${id10}:refund`,
4194
+ membershipUpdate("membershipEffect", `${id11}:refund`, {
4195
+ id: `${id11}:refund`,
4032
4196
  chapterId: input.chapterId,
4033
4197
  environment: input.environment,
4034
4198
  runtime: input.runtime,
4035
- membershipId: id10,
4199
+ membershipId: id11,
4036
4200
  kind: "refund_membership",
4037
4201
  status: "pending",
4038
4202
  attempts: 0,
4039
4203
  createdAt: now,
4040
4204
  updatedAt: now
4041
4205
  })
4042
- ], { mutationId: `membership-refund-request:${id10}`, guards: [
4043
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: member.revision } },
4206
+ ], { mutationId: `membership-refund-request:${id11}`, guards: [
4207
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: member.revision } },
4044
4208
  { ns: "networkEntitlement", id: entitlementId, exists: true, equals: { revision: entitlement.revision } },
4045
4209
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { revision: state.revision, mode: "authoritative" } }
4046
4210
  ] });
4047
4211
  }
4048
- member = await membershipLoad(db, "networkMembership", id10);
4212
+ member = await membershipLoad(db, "networkMembership", id11);
4049
4213
  requireMembershipScope(member, input);
4050
4214
  if (member.status !== "refund_pending") membershipFail("membership_refund_retry_required");
4051
4215
  const result = await refundSubscriptionPayment(secretKey, {
@@ -4059,23 +4223,23 @@ async function refundNetworkMembership(db, input, secretKey, now) {
4059
4223
  if (!result.subscriptionCanceled || result.refundedCents !== member.amountCents) membershipFail("membership_refund_unconfirmed");
4060
4224
  for (let attempt = 0; attempt < 8; attempt++) {
4061
4225
  state = await loadMembershipAuthorityState(db, input);
4062
- member = await membershipLoad(db, "networkMembership", id10);
4226
+ member = await membershipLoad(db, "networkMembership", id11);
4063
4227
  requireMembershipScope(member, input);
4064
- if (member.status === "refunded") return { membershipId: id10, status: "refunded", refundedCents: Number(member.refundedCents), subscriptionCanceled: true };
4228
+ if (member.status === "refunded") return { membershipId: id11, status: "refunded", refundedCents: Number(member.refundedCents), subscriptionCanceled: true };
4065
4229
  if (member.status !== "refund_pending") membershipFail("membership_refund_retry_required");
4066
4230
  const counted = member.founding === true && member.memberNumber !== void 0;
4067
4231
  const counts = state.cohortCounts;
4068
4232
  if (!counts || counted && (!Number.isSafeInteger(counts[String(member.cohort)]) || counts[String(member.cohort)] < 1)) membershipFail("invalid_cohort_count");
4069
4233
  try {
4070
4234
  await db.transact([
4071
- membershipUpdate("networkMembership", id10, {
4235
+ membershipUpdate("networkMembership", id11, {
4072
4236
  status: "refunded",
4073
4237
  refundedCents: result.refundedCents,
4074
4238
  autoRenew: false,
4075
4239
  revision: Number(member.revision) + 1,
4076
4240
  updatedAt: now
4077
4241
  }),
4078
- membershipUpdate("membershipEffect", `${id10}:refund`, { status: "complete", updatedAt: now }),
4242
+ membershipUpdate("membershipEffect", `${id11}:refund`, { status: "complete", updatedAt: now }),
4079
4243
  ...counted ? [membershipUpdate("membershipAuthorityState", String(state.id), {
4080
4244
  cohortCounts: { ...counts, [String(member.cohort)]: counts[String(member.cohort)] - 1 },
4081
4245
  revision: Number(state.revision) + 1,
@@ -4086,7 +4250,7 @@ async function refundNetworkMembership(db, input, secretKey, now) {
4086
4250
  chapterId: input.chapterId,
4087
4251
  environment: input.environment,
4088
4252
  runtime: input.runtime,
4089
- membershipId: id10,
4253
+ membershipId: id11,
4090
4254
  seatId: member.namedSeatId,
4091
4255
  kind: "refund_seat_with_primary",
4092
4256
  status: "pending",
@@ -4094,8 +4258,8 @@ async function refundNetworkMembership(db, input, secretKey, now) {
4094
4258
  createdAt: now,
4095
4259
  updatedAt: now
4096
4260
  })] : []
4097
- ], { mutationId: `membership-refunded:${id10}`, guards: [
4098
- { ns: "networkMembership", id: id10, exists: true, equals: { status: "refund_pending", revision: member.revision } },
4261
+ ], { mutationId: `membership-refunded:${id11}`, guards: [
4262
+ { ns: "networkMembership", id: id11, exists: true, equals: { status: "refund_pending", revision: member.revision } },
4099
4263
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } }
4100
4264
  ] });
4101
4265
  } catch (error) {
@@ -4123,8 +4287,8 @@ async function reserveNamedMembershipSeat(db, input, now) {
4123
4287
  const memberId = await networkMembershipId(input), member = await membershipLoad(db, "networkMembership", memberId);
4124
4288
  requireMembershipScope(member, input);
4125
4289
  if (!Number.isSafeInteger(member.revision) || !/^price_[A-Za-z0-9_]+$/.test(String(member.providerPriceId)) || member.providerPriceId === input.priceId) membershipFail("named_seat_purchase_unavailable");
4126
- const id10 = (await membershipOfferIdForRequest({ ...input, requestId: "named_seat" })).replace(/^offer_/, "seat_");
4127
- let seat = await membershipLoad(db, "networkSeat", id10);
4290
+ const id11 = (await membershipOfferIdForRequest({ ...input, requestId: "named_seat" })).replace(/^offer_/, "seat_");
4291
+ let seat = await membershipLoad(db, "networkSeat", id11);
4128
4292
  if (!seat) {
4129
4293
  const offering = membershipOfferingVersion(member.offeringSnapshot), claimId = await recipientKey(input, email);
4130
4294
  const purchaseEndsAt = seatPurchaseDeadline(offering, member);
@@ -4132,7 +4296,7 @@ async function reserveNamedMembershipSeat(db, input, now) {
4132
4296
  membershipFail("named_seat_purchase_unavailable");
4133
4297
  }
4134
4298
  const attrs = {
4135
- id: id10,
4299
+ id: id11,
4136
4300
  ...membershipScope(input),
4137
4301
  primaryMembershipId: memberId,
4138
4302
  recipientName: name,
@@ -4157,20 +4321,20 @@ async function reserveNamedMembershipSeat(db, input, now) {
4157
4321
  };
4158
4322
  try {
4159
4323
  await db.transact([
4160
- membershipUpdate("networkSeat", id10, attrs),
4324
+ membershipUpdate("networkSeat", id11, attrs),
4161
4325
  membershipUpdate("networkSeatRecipient", claimId, {
4162
4326
  id: claimId,
4163
4327
  appId: input.appId,
4164
4328
  chapterId: input.chapterId,
4165
4329
  environment: input.environment,
4166
4330
  runtime: input.runtime,
4167
- seatId: id10,
4331
+ seatId: id11,
4168
4332
  recipientEmailNormalized: email,
4169
4333
  createdAt: now
4170
4334
  }),
4171
- membershipUpdate("networkMembership", memberId, { namedSeatId: id10, revision: Number(member.revision) + 1, updatedAt: now })
4172
- ], { mutationId: `named-seat-reserve:${id10}`, guards: [
4173
- { ns: "networkSeat", id: id10, exists: false },
4335
+ membershipUpdate("networkMembership", memberId, { namedSeatId: id11, revision: Number(member.revision) + 1, updatedAt: now })
4336
+ ], { mutationId: `named-seat-reserve:${id11}`, guards: [
4337
+ { ns: "networkSeat", id: id11, exists: false },
4174
4338
  { ns: "networkSeatRecipient", id: claimId, exists: false },
4175
4339
  { ns: "networkMembership", id: memberId, exists: true, equals: { revision: member.revision }, absent: ["namedSeatId"] },
4176
4340
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } }
@@ -4178,7 +4342,7 @@ async function reserveNamedMembershipSeat(db, input, now) {
4178
4342
  } catch (error) {
4179
4343
  if (error.code !== "transact_guard_failed") throw error;
4180
4344
  }
4181
- seat = await membershipLoad(db, "networkSeat", id10);
4345
+ seat = await membershipLoad(db, "networkSeat", id11);
4182
4346
  if (!seat && await membershipLoad(db, "networkSeatRecipient", claimId)) membershipFail("named_seat_recipient_unavailable");
4183
4347
  }
4184
4348
  requireMembershipScope(seat, input);
@@ -4188,7 +4352,7 @@ async function reserveNamedMembershipSeat(db, input, now) {
4188
4352
 
4189
4353
  // src/membership-seat-included.ts
4190
4354
  var namedSeatIncluded = (seat) => Number(seat.amountCents) === 0;
4191
- var namedSeatSettled = (seat) => namedSeatIncluded(seat) ? seat.purchasedAt !== void 0 : Boolean(seat.providerPaymentIntentId && seat.providerSubscriptionItemId);
4355
+ var namedSeatSettled = (seat) => namedSeatIncluded(seat) ? seat.purchasedAt !== void 0 : Boolean((seat.providerPaymentIntentId || seat.combinedInvoice) && seat.providerSubscriptionItemId);
4192
4356
  function namedSeatRecipientState(seat) {
4193
4357
  switch (String(seat.status)) {
4194
4358
  case "renewal_pending":
@@ -4286,7 +4450,7 @@ async function grantIncludedNamedSeat(db, input, now) {
4286
4450
  // src/membership-renewal-authority.ts
4287
4451
  async function reconcileMembershipRenewal(db, input, now) {
4288
4452
  membershipClock(now);
4289
- const id10 = await networkMembershipId(input), eventRow = await membershipLoad(db, "networkCommercialEvents", `network-commercial-event:${input.eventId}`);
4453
+ const id11 = await networkMembershipId(input), eventRow = await membershipLoad(db, "networkCommercialEvents", `network-commercial-event:${input.eventId}`);
4290
4454
  const evidenceGuard = await commercialEvidenceGuard(db, input.eventId, eventRow?.digest, "membership_renewal_evidence_pending");
4291
4455
  const fact = eventRow?.fact;
4292
4456
  if (!fact || !/^evt_[A-Za-z0-9_]+$/.test(input.eventId) || !/^[a-f0-9]{64}$/.test(String(eventRow.digest)) || fact.digest !== eventRow.digest || fact.eventId !== input.eventId || fact.objectKind !== "invoice" || !["invoice.payment_succeeded", "invoice.payment_failed"].includes(fact.eventType) || fact.billingReason !== "subscription_cycle" || fact.livemode !== (input.environment === "prod") || fact.applicationId !== input.applicationId || Object.entries({
@@ -4299,10 +4463,10 @@ async function reconcileMembershipRenewal(db, input, now) {
4299
4463
  return membershipFail("membership_renewal_evidence_pending");
4300
4464
  }
4301
4465
  for (let attempt = 0; attempt < 8; attempt++) {
4302
- const state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id10);
4466
+ const state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id11);
4303
4467
  requireMembershipScope(member, input);
4304
4468
  const receipt = () => ({
4305
- membershipId: id10,
4469
+ membershipId: id11,
4306
4470
  status: String(member.status),
4307
4471
  termEndsAt: Number(member.termEndsAt),
4308
4472
  graceEndsAt: Number(member.graceEndsAt ?? 0),
@@ -4327,13 +4491,13 @@ async function reconcileMembershipRenewal(db, input, now) {
4327
4491
  const paid = fact.eventType === "invoice.payment_succeeded";
4328
4492
  if (!paid && (fact.status !== "open" || !Number.isSafeInteger(fact.amountPaidCents) || Number(fact.amountPaidCents) < 0 || Number(fact.amountPaidCents) >= amount)) membershipFail("membership_renewal_failure_mismatch");
4329
4493
  if (paid && (fact.status !== "paid" || fact.amountPaidCents !== amount || !Number.isSafeInteger(fact.invoicePaidAt) || Number(fact.invoicePaidAt) < start || Number(fact.invoicePaidAt) > now + 3e4)) membershipFail("membership_renewal_unpaid");
4330
- const entitlement = await membershipLoad(db, "networkEntitlement", `${id10}:primary`);
4494
+ const entitlement = await membershipLoad(db, "networkEntitlement", `${id11}:primary`);
4331
4495
  requireMembershipScope(entitlement, input);
4332
4496
  const graceEndsAt = paid ? 0 : start + 14 * 864e5;
4333
4497
  const status2 = paid ? member.journeyApprovedAt ? member.autoRenew ? "active" : "cancel_at_term_end" : "paid_pending_approval" : now < graceEndsAt ? "payment_grace" : "lapsed";
4334
4498
  const entitlementStatus = member.journeyApprovedAt && (paid || now < graceEndsAt) ? "active" : member.journeyApprovedAt ? "closed" : "pending_approval";
4335
4499
  const ops = [
4336
- membershipUpdate("networkMembership", id10, {
4500
+ membershipUpdate("networkMembership", id11, {
4337
4501
  status: status2,
4338
4502
  graceEndsAt,
4339
4503
  ...paid ? {
@@ -4344,7 +4508,7 @@ async function reconcileMembershipRenewal(db, input, now) {
4344
4508
  revision: Number(member.revision) + 1,
4345
4509
  updatedAt: now
4346
4510
  }),
4347
- membershipUpdate("networkEntitlement", `${id10}:primary`, {
4511
+ membershipUpdate("networkEntitlement", `${id11}:primary`, {
4348
4512
  status: entitlementStatus,
4349
4513
  ...paid ? { termStartedAt: start, termEndsAt: end } : {},
4350
4514
  sourceEventId: input.eventId,
@@ -4353,8 +4517,8 @@ async function reconcileMembershipRenewal(db, input, now) {
4353
4517
  })
4354
4518
  ];
4355
4519
  const guards = [
4356
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: member.revision } },
4357
- { ns: "networkEntitlement", id: `${id10}:primary`, exists: true, equals: { revision: entitlement.revision } },
4520
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: member.revision } },
4521
+ { ns: "networkEntitlement", id: `${id11}:primary`, exists: true, equals: { revision: entitlement.revision } },
4358
4522
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { revision: state.revision, mode: "authoritative" } },
4359
4523
  { ns: "networkCommercialEvents", id: String(eventRow.id), exists: true, equals: { digest: eventRow.digest } },
4360
4524
  evidenceGuard
@@ -4379,15 +4543,15 @@ async function reconcileMembershipRenewal(db, input, now) {
4379
4543
  );
4380
4544
  }
4381
4545
  try {
4382
- await db.transact(ops, { mutationId: `membership-renewal:${id10}:${input.eventId}`, guards });
4546
+ await db.transact(ops, { mutationId: `membership-renewal:${id11}:${input.eventId}`, guards });
4383
4547
  } catch (error) {
4384
4548
  if (error.code !== "transact_guard_failed") throw error;
4385
4549
  continue;
4386
4550
  }
4387
- const saved = await membershipLoad(db, "networkMembership", id10);
4551
+ const saved = await membershipLoad(db, "networkMembership", id11);
4388
4552
  requireMembershipScope(saved, input);
4389
4553
  return {
4390
- membershipId: id10,
4554
+ membershipId: id11,
4391
4555
  status: String(saved.status),
4392
4556
  termEndsAt: Number(saved.termEndsAt),
4393
4557
  graceEndsAt: Number(saved.graceEndsAt ?? 0),
@@ -4413,12 +4577,12 @@ async function reconcileMembershipBilling(db, input, now, stripeSecretName = "st
4413
4577
  }).some(([key3, value]) => fact.identity[key3] !== value)) membershipFail("membership_billing_evidence_pending");
4414
4578
  const sk = await getVaultSecret(db, stripeSecretName);
4415
4579
  if (!sk) membershipFail("membership_billing_provider_unavailable");
4416
- const id10 = await networkMembershipId(input);
4580
+ const id11 = await networkMembershipId(input);
4417
4581
  for (let attempt = 0; attempt < 8; attempt++) {
4418
- const state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id10);
4582
+ const state = await loadMembershipAuthorityState(db, input), member = await membershipLoad(db, "networkMembership", id11);
4419
4583
  requireMembershipScope(member, input);
4420
4584
  const receipt = (value = member) => ({
4421
- membershipId: id10,
4585
+ membershipId: id11,
4422
4586
  status: String(value.status),
4423
4587
  termEndsAt: Number(value.termEndsAt),
4424
4588
  graceEndsAt: Number(value.graceEndsAt ?? 0),
@@ -4439,7 +4603,7 @@ async function reconcileMembershipBilling(db, input, now, stripeSecretName = "st
4439
4603
  });
4440
4604
  await commercialEvidenceGuard(db, input.eventId, row2?.digest, "membership_billing_evidence_pending");
4441
4605
  await refundNetworkMembership(db, input, sk, now);
4442
- const saved2 = await membershipLoad(db, "networkMembership", id10);
4606
+ const saved2 = await membershipLoad(db, "networkMembership", id11);
4443
4607
  requireMembershipScope(saved2, input);
4444
4608
  return receipt(saved2);
4445
4609
  }
@@ -4454,18 +4618,18 @@ async function reconcileMembershipBilling(db, input, now, stripeSecretName = "st
4454
4618
  const status2 = autoRenew ? member.status === "cancel_at_term_end" ? "active" : String(member.status) : now >= Number(member.termEndsAt) ? "lapsed" : member.journeyApprovedAt ? "cancel_at_term_end" : "paid_pending_approval";
4455
4619
  const graceEndsAt = autoRenew ? Number(member.graceEndsAt ?? 0) : 0;
4456
4620
  if (member.autoRenew === autoRenew && member.status === status2 && Number(member.graceEndsAt ?? 0) === graceEndsAt) return receipt();
4457
- const entitlement = await membershipLoad(db, "networkEntitlement", `${id10}:primary`);
4621
+ const entitlement = await membershipLoad(db, "networkEntitlement", `${id11}:primary`);
4458
4622
  requireMembershipScope(entitlement, input);
4459
4623
  try {
4460
4624
  await db.transact([
4461
- membershipUpdate("networkMembership", id10, { status: status2, autoRenew, graceEndsAt, revision: Number(member.revision) + 1, updatedAt: now }),
4625
+ membershipUpdate("networkMembership", id11, { status: status2, autoRenew, graceEndsAt, revision: Number(member.revision) + 1, updatedAt: now }),
4462
4626
  membershipUpdate("networkEntitlement", String(entitlement.id), {
4463
4627
  status: status2 === "lapsed" ? "closed" : entitlement.status,
4464
4628
  revision: Number(entitlement.revision) + 1,
4465
4629
  updatedAt: now
4466
4630
  })
4467
4631
  ], { guards: [
4468
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: member.revision } },
4632
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: member.revision } },
4469
4633
  { ns: "networkEntitlement", id: String(entitlement.id), exists: true, equals: { revision: entitlement.revision } },
4470
4634
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } },
4471
4635
  { ns: "networkCommercialEvents", id: String(row2.id), exists: true, equals: { digest: fact.digest } },
@@ -4475,7 +4639,7 @@ async function reconcileMembershipBilling(db, input, now, stripeSecretName = "st
4475
4639
  if (error.code !== "transact_guard_failed") throw error;
4476
4640
  continue;
4477
4641
  }
4478
- const saved = await membershipLoad(db, "networkMembership", id10);
4642
+ const saved = await membershipLoad(db, "networkMembership", id11);
4479
4643
  requireMembershipScope(saved, input);
4480
4644
  return receipt(saved);
4481
4645
  }
@@ -4536,19 +4700,19 @@ async function stopPreviousSubscription(member, target, secret) {
4536
4700
  async function prepareMembershipRestart(db, input, now, stripeSecret) {
4537
4701
  membershipClock(now);
4538
4702
  if (!/^sub_[A-Za-z0-9_]+$/.test(input.previousSubscriptionId)) membershipFail("membership_restart_conflict");
4539
- const id10 = await networkMembershipId(input), bindingId = await membershipCheckoutBindingId(input);
4540
- const restartId = `restart_${await stripePayloadDigest(JSON.stringify([id10, input.previousSubscriptionId]))}`;
4703
+ const id11 = await networkMembershipId(input), bindingId = await membershipCheckoutBindingId(input);
4704
+ const restartId = `restart_${await stripePayloadDigest(JSON.stringify([id11, input.previousSubscriptionId]))}`;
4541
4705
  const state = await loadMembershipAuthorityState(db, input);
4542
- const previous = await membershipLoad(db, "networkMembership", id10);
4706
+ const previous = await membershipLoad(db, "networkMembership", id11);
4543
4707
  requireMembershipScope(previous, input);
4544
4708
  const receipt = {
4545
4709
  applicationId: input.applicationId,
4546
- membershipId: id10,
4710
+ membershipId: id11,
4547
4711
  restartId,
4548
4712
  memberNumber: Number(previous.memberNumber),
4549
4713
  status: "ready"
4550
4714
  };
4551
- const existing = await membershipLoad(db, "membershipRestart", id10);
4715
+ const existing = await membershipLoad(db, "membershipRestart", id11);
4552
4716
  if (existing?.restartId === restartId && existing.status === "ready") {
4553
4717
  requireMembershipScope(existing, input);
4554
4718
  return receipt;
@@ -4564,33 +4728,33 @@ async function prepareMembershipRestart(db, input, now, stripeSecret) {
4564
4728
  membershipUpdate("membershipPurchaseHistory", restartId, {
4565
4729
  id: restartId,
4566
4730
  ...membershipScope(input),
4567
- membershipId: id10,
4731
+ membershipId: id11,
4568
4732
  providerSubscriptionId: input.previousSubscriptionId,
4569
4733
  memberSnapshot: previous,
4570
4734
  bindingSnapshot: binding2,
4571
4735
  archivedAt: now
4572
4736
  }),
4573
- membershipUpdate("networkMembership", id10, { status: "restarting", restartId, revision: Number(previous.revision) + 1, updatedAt: now })
4737
+ membershipUpdate("networkMembership", id11, { status: "restarting", restartId, revision: Number(previous.revision) + 1, updatedAt: now })
4574
4738
  ], { mutationId: `membership-restart-reserve:${restartId}`, guards: [
4575
4739
  ...seats.guards,
4576
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: previous.revision, status: previous.status } },
4740
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: previous.revision, status: previous.status } },
4577
4741
  { ns: "membershipPurchaseHistory", id: restartId, exists: false },
4578
4742
  { ns: "membershipOfferCheckout", id: bindingId, exists: true, equals: { offerDigest: binding2.offerDigest } },
4579
4743
  { ns: "membershipAuthorityState", id: String(state.id), exists: true, equals: { mode: "authoritative", revision: state.revision } }
4580
4744
  ] });
4581
4745
  } else if (previous.restartId !== restartId) membershipFail("membership_restart_conflict");
4582
- const reserved = await membershipLoad(db, "networkMembership", id10);
4746
+ const reserved = await membershipLoad(db, "networkMembership", id11);
4583
4747
  requireMembershipScope(reserved, input);
4584
4748
  if (reserved.status !== "restarting" || reserved.restartId !== restartId) membershipFail("membership_restart_conflict");
4585
4749
  await stopPreviousSubscription(reserved, input, stripeSecret);
4586
4750
  const binding = await membershipLoad(db, "membershipOfferCheckout", bindingId);
4587
4751
  requireMembershipScope(binding, input);
4588
4752
  await db.transact([
4589
- membershipUpdate("membershipRestart", id10, { id: id10, ...membershipScope(input), ...receipt, previousSubscriptionId: input.previousSubscriptionId, createdAt: now }),
4753
+ membershipUpdate("membershipRestart", id11, { id: id11, ...membershipScope(input), ...receipt, previousSubscriptionId: input.previousSubscriptionId, createdAt: now }),
4590
4754
  { t: "delete", ns: "membershipOfferCheckout", id: bindingId }
4591
4755
  ], { mutationId: `membership-restart-ready:${restartId}`, guards: [
4592
- { ns: "membershipRestart", id: id10, exists: Boolean(existing), ...existing ? { equals: { restartId: existing.restartId, status: existing.status } } : {} },
4593
- { ns: "networkMembership", id: id10, exists: true, equals: { revision: reserved.revision, status: "restarting", restartId } },
4756
+ { ns: "membershipRestart", id: id11, exists: Boolean(existing), ...existing ? { equals: { restartId: existing.restartId, status: existing.status } } : {} },
4757
+ { ns: "networkMembership", id: id11, exists: true, equals: { revision: reserved.revision, status: "restarting", restartId } },
4594
4758
  { ns: "membershipOfferCheckout", id: bindingId, exists: true, equals: { offerDigest: binding.offerDigest } }
4595
4759
  ] });
4596
4760
  return receipt;
@@ -4600,15 +4764,15 @@ async function prepareMembershipRestart(db, input, now, stripeSecret) {
4600
4764
  async function readMembershipAccess(db, input, now) {
4601
4765
  membershipClock(now);
4602
4766
  await loadMembershipAuthorityState(db, input);
4603
- const id10 = await networkMembershipId(input), member = await membershipLoad(db, "networkMembership", id10);
4604
- if (!member) return { membershipId: id10, allowed: false, status: "unpaid", accessEndsAt: 0, revision: 0 };
4767
+ const id11 = await networkMembershipId(input), member = await membershipLoad(db, "networkMembership", id11);
4768
+ if (!member) return { membershipId: id11, allowed: false, status: "unpaid", accessEndsAt: 0, revision: 0 };
4605
4769
  requireMembershipScope(member, input);
4606
- const entitlement = await membershipLoad(db, "networkEntitlement", `${id10}:primary`);
4770
+ const entitlement = await membershipLoad(db, "networkEntitlement", `${id11}:primary`);
4607
4771
  requireMembershipScope(entitlement, input);
4608
4772
  const endsAt = member.status === "payment_grace" ? Number(member.graceEndsAt) : Number(member.termEndsAt);
4609
- const allowed2 = ["active", "cancel_at_term_end", "payment_grace"].includes(String(member.status)) && member.journeyApprovedAt !== void 0 && Number.isSafeInteger(member.memberNumber) && Number(member.memberNumber) > 0 && member.refundedCents === 0 && entitlement.status === "active" && entitlement.membershipId === id10 && entitlement.kind === "primary" && Number.isSafeInteger(endsAt) && endsAt > now && Number(member.termStartedAt) <= now;
4773
+ const allowed2 = ["active", "cancel_at_term_end", "payment_grace"].includes(String(member.status)) && member.journeyApprovedAt !== void 0 && Number.isSafeInteger(member.memberNumber) && Number(member.memberNumber) > 0 && member.refundedCents === 0 && entitlement.status === "active" && entitlement.membershipId === id11 && entitlement.kind === "primary" && Number.isSafeInteger(endsAt) && endsAt > now && Number(member.termStartedAt) <= now;
4610
4774
  return {
4611
- membershipId: id10,
4775
+ membershipId: id11,
4612
4776
  allowed: allowed2,
4613
4777
  status: String(member.status),
4614
4778
  accessEndsAt: Number.isSafeInteger(endsAt) ? endsAt : 0,
@@ -4663,7 +4827,7 @@ async function subscriptionForSeat(sk, seat) {
4663
4827
  currency: seat.currency,
4664
4828
  interval: seat.interval,
4665
4829
  environment: seat.environment
4666
- }, Boolean(seat.providerPaymentIntentId));
4830
+ }, Boolean(seat.providerPaymentIntentId || seat.combinedInvoice));
4667
4831
  if (!productId) reject3();
4668
4832
  const sub = await read(sk, `/v1/subscriptions/${encodeURIComponent(seat.providerSubscriptionId)}?expand[]=discounts`);
4669
4833
  const items = sub.items?.data;
@@ -4707,10 +4871,14 @@ async function createNamedSeatStripePayment(sk, seat, now) {
4707
4871
  return { paymentIntentId: pi.id, clientSecret: pi.client_secret, status: pi.status };
4708
4872
  }
4709
4873
  async function attachNamedSeatStripeRenewal(sk, seat) {
4710
- if (!seat.providerPaymentIntentId || !["renewal_pending", "pending_acceptance", "pending_approval", "active"].includes(seat.status)) reject3();
4874
+ if (!seat.providerPaymentIntentId && !seat.combinedInvoice || !["renewal_pending", "pending_acceptance", "pending_approval", "active"].includes(seat.status)) reject3();
4711
4875
  const sub = await subscriptionForSeat(sk, seat);
4712
4876
  const existing = sub.items.data.find((item2) => item2.metadata?.seatId === seat.id);
4713
- if (existing) return String(existing.id);
4877
+ if (existing) {
4878
+ if (!/^si_[A-Za-z0-9_]+$/.test(existing.id)) reject3();
4879
+ return String(existing.id);
4880
+ }
4881
+ if (seat.combinedInvoice) reject3();
4714
4882
  const result = await stripeRequest7({ secretKey: sk }, "POST", "/v1/subscription_items", {
4715
4883
  subscription: seat.providerSubscriptionId,
4716
4884
  price: seat.priceId,
@@ -4928,7 +5096,7 @@ async function recordRecipient(db, input, now, approve) {
4928
5096
  const state = await loadMembershipAuthorityState(db, input);
4929
5097
  const seat = await membershipLoad(db, "networkSeat", input.seatId);
4930
5098
  requireMembershipScope(seat, input);
4931
- if (!seat.purchaserUserId || seat.purchaserUserId === input.recipientUserId) membershipFail("named_seat_recipient_must_differ_from_purchaser");
5099
+ if (!seat.purchaserUserId && !seat.purchaserEmailNormalized || seat.purchaserUserId === input.recipientUserId || seat.purchaserEmailNormalized === normalizeChapterEmail(input.verifiedRecipientEmail)) membershipFail("named_seat_recipient_must_differ_from_purchaser");
4932
5100
  const member = await membershipLoad(db, "networkMembership", String(seat.primaryMembershipId));
4933
5101
  requireMembershipScope(member, input);
4934
5102
  const entitlement = await membershipLoad(db, "networkEntitlement", String(seat.entitlementId));
@@ -4986,43 +5154,121 @@ async function recordRecipient(db, input, now, approve) {
4986
5154
  }
4987
5155
 
4988
5156
  // src/membership-seat-refund-stripe.ts
5157
+ import { stripeRequest as stripeRequest9 } from "@odla-ai/stripe";
5158
+
5159
+ // src/membership-checkout-seat-refund.ts
4989
5160
  import { stripeRequest as stripeRequest8 } from "@odla-ai/stripe";
4990
5161
  var fail4 = (code = "named_seat_refund_mismatch") => {
4991
5162
  throw Object.assign(new Error(code), { code });
4992
5163
  };
5164
+ var id8 = (value) => typeof value === "string" ? value : value?.id ?? "";
5165
+ async function refundCheckoutNamedSeat(secretKey, seat) {
5166
+ const invoiceRef = seat.combinedInvoice;
5167
+ 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();
5168
+ const call = async (method, path, params, key3) => {
5169
+ const result = await stripeRequest8({ secretKey }, method, path, params, key3 ? { idempotencyKey: key3 } : {});
5170
+ if (!result.ok || !result.body) return fail4("named_seat_refund_provider_unavailable");
5171
+ return result.body;
5172
+ };
5173
+ const metadata = { ...stripePartitionMetadata(seat), applicationId: seat.applicationId };
5174
+ const sub = await call("GET", `/v1/subscriptions/${encodeURIComponent(seat.providerSubscriptionId)}`);
5175
+ if (sub.id !== seat.providerSubscriptionId || id8(sub.customer) !== seat.providerCustomerId || sub.livemode !== (seat.environment === "prod") || !Object.entries(metadata).every(([key3, value]) => sub.metadata?.[key3] === value) || !Array.isArray(sub.items?.data) || sub.items.has_more !== false) fail4();
5176
+ const invoice = await call("GET", `/v1/invoices/${encodeURIComponent(invoiceRef.invoiceId)}`);
5177
+ if (invoice.id !== invoiceRef.invoiceId || id8(invoice.subscription ?? invoice.parent?.subscription_details?.subscription) !== sub.id || id8(invoice.customer) !== seat.providerCustomerId || invoice.status !== "paid" || invoice.billing_reason !== "subscription_create" || invoice.total !== invoiceRef.totalCents || invoice.amount_paid !== invoiceRef.totalCents || invoice.currency !== seat.currency || invoice.livemode !== (seat.environment === "prod")) fail4();
5178
+ let chargeId = id8(invoice.charge), paymentId = id8(invoice.payment_intent);
5179
+ if (!chargeId && !paymentId) {
5180
+ const payments = await call("GET", "/v1/invoice_payments", { invoice: invoice.id, status: "paid", limit: 100 });
5181
+ if (!Array.isArray(payments.data) || payments.data.length !== 1 || payments.has_more !== false) fail4();
5182
+ const payment = payments.data[0];
5183
+ if (id8(payment.invoice) !== invoice.id || payment.status !== "paid" || payment.payment?.type !== "payment_intent") fail4();
5184
+ paymentId = id8(payment.payment.payment_intent);
5185
+ }
5186
+ if (!chargeId) {
5187
+ if (!/^pi_[A-Za-z0-9_]+$/.test(paymentId)) fail4();
5188
+ const payment = await call("GET", `/v1/payment_intents/${encodeURIComponent(paymentId)}`);
5189
+ if (payment.id !== paymentId || id8(payment.customer) !== seat.providerCustomerId || payment.status !== "succeeded" || payment.amount_received !== invoiceRef.totalCents || payment.currency !== seat.currency || payment.livemode !== (seat.environment === "prod")) fail4();
5190
+ chargeId = id8(payment.latest_charge);
5191
+ }
5192
+ if (!/^ch_[A-Za-z0-9_]+$/.test(chargeId)) fail4();
5193
+ const charge = await call("GET", `/v1/charges/${encodeURIComponent(chargeId)}`);
5194
+ if (charge.id !== chargeId || id8(charge.customer) !== seat.providerCustomerId || charge.status !== "succeeded" || charge.livemode !== (seat.environment === "prod") || charge.amount !== invoiceRef.totalCents || charge.currency !== seat.currency || charge.invoice != null && id8(charge.invoice) !== invoice.id || paymentId && id8(charge.payment_intent) !== paymentId || !Number.isSafeInteger(charge.amount_refunded) || charge.amount_refunded < 0 || charge.amount_refunded > charge.amount) fail4();
5195
+ const items = sub.items.data.filter((item) => item.metadata?.seatId === seat.id);
5196
+ if (items.length > 1) fail4();
5197
+ if (items.length && sub.status !== "canceled") {
5198
+ const item = items[0];
5199
+ if (!/^si_[A-Za-z0-9_]+$/.test(item.id) || item.quantity !== 1 || item.price?.id !== seat.priceId) fail4();
5200
+ const removed = await call("DELETE", `/v1/subscription_items/${encodeURIComponent(item.id)}`, { proration_behavior: "none" });
5201
+ if (removed.id !== item.id || removed.deleted !== true) fail4("named_seat_renewal_removal_unconfirmed");
5202
+ }
5203
+ if (charge.amount_refunded === charge.amount && charge.refunded === true) return { refundedCents: seat.amountCents, refundIds: [] };
5204
+ const list = await call("GET", "/v1/refunds", { charge: chargeId, limit: 100 });
5205
+ if (!Array.isArray(list.data) || list.has_more !== false || list.data.length >= 100) fail4("named_seat_refund_history_incomplete");
5206
+ let refundedCents = 0;
5207
+ const refundIds = [];
5208
+ for (const refund of list.data) {
5209
+ if (id8(refund.charge) !== chargeId || refund.currency !== seat.currency || !Number.isSafeInteger(refund.amount) || refund.amount <= 0 || !/^re_[A-Za-z0-9_]+$/.test(refund.id) || !["succeeded", "pending", "requires_action", "failed", "canceled"].includes(refund.status)) fail4();
5210
+ if (["pending", "requires_action"].includes(refund.status)) fail4("named_seat_refund_pending");
5211
+ if (refund.status === "succeeded") {
5212
+ if (refund.metadata?.seatId !== seat.id || refund.metadata?.membershipId !== seat.primaryMembershipId) fail4("named_seat_refund_allocation_ambiguous");
5213
+ refundedCents += refund.amount;
5214
+ refundIds.push(refund.id);
5215
+ }
5216
+ }
5217
+ if (refundedCents !== charge.amount_refunded || refundedCents > seat.amountCents) fail4();
5218
+ if (refundedCents < seat.amountCents) {
5219
+ const remaining = seat.amountCents - refundedCents;
5220
+ const refund = await call("POST", "/v1/refunds", {
5221
+ charge: chargeId,
5222
+ amount: remaining,
5223
+ reason: "requested_by_customer",
5224
+ metadata: { ...metadata, membershipId: seat.primaryMembershipId, seatId: seat.id }
5225
+ }, `combined-seat-refund:${seat.id}:${remaining}`);
5226
+ if (id8(refund.charge) !== chargeId || refund.amount !== remaining || refund.currency !== seat.currency || !/^re_[A-Za-z0-9_]+$/.test(refund.id)) fail4();
5227
+ if (refund.status !== "succeeded") fail4("named_seat_refund_pending");
5228
+ refundedCents += remaining;
5229
+ refundIds.push(refund.id);
5230
+ }
5231
+ return { refundedCents, refundIds };
5232
+ }
5233
+
5234
+ // src/membership-seat-refund-stripe.ts
5235
+ var fail5 = (code = "named_seat_refund_mismatch") => {
5236
+ throw Object.assign(new Error(code), { code });
5237
+ };
4993
5238
  async function refundNamedSeatStripePayment(secretKey, seat) {
4994
- if (!seat.providerPaymentIntentId || seat.status !== "refund_pending") fail4();
5239
+ if (seat.combinedInvoice) return refundCheckoutNamedSeat(secretKey, seat);
5240
+ if (!seat.providerPaymentIntentId || seat.status !== "refund_pending") fail5();
4995
5241
  const call = async (method, path, params, idempotencyKey) => {
4996
- const result = await stripeRequest8({ secretKey }, method, path, params, idempotencyKey ? { idempotencyKey } : {});
4997
- if (!result.ok || !result.body) return fail4("named_seat_refund_provider_unavailable");
5242
+ const result = await stripeRequest9({ secretKey }, method, path, params, idempotencyKey ? { idempotencyKey } : {});
5243
+ if (!result.ok || !result.body) return fail5("named_seat_refund_provider_unavailable");
4998
5244
  return result.body;
4999
5245
  };
5000
5246
  const metadata = { ...stripePartitionMetadata(seat), applicationId: seat.applicationId, membershipId: seat.primaryMembershipId, seatId: seat.id };
5001
5247
  const payment = await call("GET", `/v1/payment_intents/${encodeURIComponent(seat.providerPaymentIntentId)}`);
5002
- if (payment.id !== seat.providerPaymentIntentId || payment.customer !== seat.providerCustomerId || payment.amount !== seat.amountCents || payment.amount_received !== seat.amountCents || payment.currency !== seat.currency || payment.livemode !== (seat.environment === "prod") || payment.status !== "succeeded" || !Object.entries(metadata).every(([key3, value]) => payment.metadata?.[key3] === value)) fail4();
5248
+ if (payment.id !== seat.providerPaymentIntentId || payment.customer !== seat.providerCustomerId || payment.amount !== seat.amountCents || payment.amount_received !== seat.amountCents || payment.currency !== seat.currency || payment.livemode !== (seat.environment === "prod") || payment.status !== "succeeded" || !Object.entries(metadata).every(([key3, value]) => payment.metadata?.[key3] === value)) fail5();
5003
5249
  const sub = await call("GET", `/v1/subscriptions/${encodeURIComponent(seat.providerSubscriptionId)}`);
5004
- if (sub.id !== seat.providerSubscriptionId || sub.customer !== seat.providerCustomerId || sub.livemode !== (seat.environment === "prod") || !Object.entries({ ...stripePartitionMetadata(seat), applicationId: seat.applicationId }).every(([key3, value]) => sub.metadata?.[key3] === value) || sub.items?.has_more !== false || !Array.isArray(sub.items?.data)) fail4();
5250
+ if (sub.id !== seat.providerSubscriptionId || sub.customer !== seat.providerCustomerId || sub.livemode !== (seat.environment === "prod") || !Object.entries({ ...stripePartitionMetadata(seat), applicationId: seat.applicationId }).every(([key3, value]) => sub.metadata?.[key3] === value) || sub.items?.has_more !== false || !Array.isArray(sub.items?.data)) fail5();
5005
5251
  const items = sub.items.data.filter((item) => item.metadata?.seatId === seat.id);
5006
- if (items.length > 1) fail4();
5252
+ if (items.length > 1) fail5();
5007
5253
  if (items.length && sub.status !== "canceled") {
5008
5254
  const item = items[0];
5009
- if (!/^si_[A-Za-z0-9_]+$/.test(item.id) || item.price?.id !== seat.priceId || item.quantity !== 1) fail4();
5255
+ if (!/^si_[A-Za-z0-9_]+$/.test(item.id) || item.price?.id !== seat.priceId || item.quantity !== 1) fail5();
5010
5256
  const removed = await call("DELETE", `/v1/subscription_items/${encodeURIComponent(item.id)}`, { proration_behavior: "none" });
5011
- if (removed.id !== item.id || removed.deleted !== true) fail4("named_seat_renewal_removal_unconfirmed");
5257
+ if (removed.id !== item.id || removed.deleted !== true) fail5("named_seat_renewal_removal_unconfirmed");
5012
5258
  }
5013
5259
  const list = await call("GET", "/v1/refunds", { payment_intent: seat.providerPaymentIntentId, limit: 100 });
5014
- if (!Array.isArray(list.data) || list.has_more !== false || list.data.length >= 100) fail4("named_seat_refund_history_incomplete");
5260
+ if (!Array.isArray(list.data) || list.has_more !== false || list.data.length >= 100) fail5("named_seat_refund_history_incomplete");
5015
5261
  let refundedCents = 0, pendingCents = 0;
5016
5262
  const refundIds = [];
5017
5263
  for (const refund of list.data) {
5018
- if (!/^re_[A-Za-z0-9_]+$/.test(refund.id) || refund.payment_intent !== seat.providerPaymentIntentId || refund.currency !== seat.currency || !Number.isSafeInteger(refund.amount) || refund.amount <= 0 || !["succeeded", "pending", "requires_action", "failed", "canceled"].includes(refund.status)) fail4();
5264
+ if (!/^re_[A-Za-z0-9_]+$/.test(refund.id) || refund.payment_intent !== seat.providerPaymentIntentId || refund.currency !== seat.currency || !Number.isSafeInteger(refund.amount) || refund.amount <= 0 || !["succeeded", "pending", "requires_action", "failed", "canceled"].includes(refund.status)) fail5();
5019
5265
  if (refund.status === "succeeded") {
5020
5266
  refundedCents += refund.amount;
5021
5267
  refundIds.push(refund.id);
5022
5268
  } else if (refund.status === "pending" || refund.status === "requires_action") pendingCents += refund.amount;
5023
5269
  }
5024
- if (refundedCents + pendingCents > seat.amountCents) fail4();
5025
- if (pendingCents) fail4("named_seat_refund_pending");
5270
+ if (refundedCents + pendingCents > seat.amountCents) fail5();
5271
+ if (pendingCents) fail5("named_seat_refund_pending");
5026
5272
  if (refundedCents < seat.amountCents) {
5027
5273
  const remaining = seat.amountCents - refundedCents;
5028
5274
  const refund = await call("POST", "/v1/refunds", {
@@ -5031,8 +5277,8 @@ async function refundNamedSeatStripePayment(secretKey, seat) {
5031
5277
  reason: "requested_by_customer",
5032
5278
  metadata
5033
5279
  }, `named-seat-refund:${seat.id}:${remaining}`);
5034
- if (refund.payment_intent !== seat.providerPaymentIntentId || refund.amount !== remaining || refund.currency !== seat.currency || !/^re_[A-Za-z0-9_]+$/.test(refund.id)) fail4();
5035
- if (refund.status !== "succeeded") fail4("named_seat_refund_pending");
5280
+ if (refund.payment_intent !== seat.providerPaymentIntentId || refund.amount !== remaining || refund.currency !== seat.currency || !/^re_[A-Za-z0-9_]+$/.test(refund.id)) fail5();
5281
+ if (refund.status !== "succeeded") fail5("named_seat_refund_pending");
5036
5282
  refundedCents += remaining;
5037
5283
  refundIds.push(refund.id);
5038
5284
  }
@@ -5048,7 +5294,7 @@ async function requestNamedSeatRefund(db, input, now) {
5048
5294
  const member = await membershipLoad(db, "networkMembership", String(seat.primaryMembershipId));
5049
5295
  requireMembershipScope(member, input);
5050
5296
  if (seat.status === "refunded" || seat.status === "refund_pending") return;
5051
- if (!seat.purchasedAt || !seat.providerPaymentIntentId && !namedSeatIncluded(seat) || input.reason === "declined" && seat.journeyApprovedAt !== void 0 || input.reason === "primary_closed" && !["refunded", "declined"].includes(String(member.status))) membershipFail("named_seat_refund_not_eligible");
5297
+ if (!seat.purchasedAt || !seat.providerPaymentIntentId && !seat.combinedInvoice && !namedSeatIncluded(seat) || input.reason === "declined" && seat.journeyApprovedAt !== void 0 || input.reason === "primary_closed" && !["refunded", "declined"].includes(String(member.status))) membershipFail("named_seat_refund_not_eligible");
5052
5298
  const entitlement = seat.entitlementId ? await membershipLoad(db, "networkEntitlement", String(seat.entitlementId)) : void 0;
5053
5299
  await db.transact([
5054
5300
  membershipUpdate("networkSeat", input.seatId, { status: "refund_pending", refundReason: input.reason, revision: Number(seat.revision) + 1, updatedAt: now }),
@@ -5153,14 +5399,14 @@ var at = (value) => Number.isSafeInteger(value) ? Number(value) : void 0;
5153
5399
  async function readNamedSeatOffer(db, input, now) {
5154
5400
  membershipClock(now);
5155
5401
  await loadMembershipAuthorityState(db, input);
5156
- const id10 = await networkMembershipId(input), member = await membershipLoad(db, "networkMembership", id10);
5402
+ const id11 = await networkMembershipId(input), member = await membershipLoad(db, "networkMembership", id11);
5157
5403
  requireMembershipScope(member, input);
5158
5404
  const offering = membershipOfferingVersion(member.offeringSnapshot);
5159
5405
  const seat = member.namedSeatId ? await membershipLoad(db, "networkSeat", String(member.namedSeatId)) : void 0;
5160
5406
  if (seat) requireMembershipScope(seat, input);
5161
5407
  const purchaseEndsAt = seatPurchaseDeadline(offering, member);
5162
5408
  const offer = {
5163
- membershipId: id10,
5409
+ membershipId: id11,
5164
5410
  amountCents: offering.seatPriceCents,
5165
5411
  currency: offering.currency,
5166
5412
  interval: offering.interval,
@@ -5438,9 +5684,14 @@ function createMembershipAuthorityHandler(options) {
5438
5684
  try {
5439
5685
  input = JSON.parse(verified.body);
5440
5686
  const fields = action === "seat-invitation" ? MEMBERSHIP_SEAT_ACTION_FIELDS[action] : action === "offer-preview" ? ["skuId"] : ["applicationId", "skuId", ...action === "offers" ? ["requestId"] : action === "checkouts" ? ["offerId", "offerDigest", "quoteDigest", "priceId"] : action === "payments" ? ["eventId", "providerSubscriptionId", "providerCustomerId"] : action === "renewals" ? ["eventId"] : action === "restarts" ? ["previousSubscriptionId"] : MEMBERSHIP_SEAT_ACTION_FIELDS[action] ?? []];
5441
- if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).some((key3) => !fields.includes(key3)) || !fields.every((key3) => typeof input[key3] === "string" && (["recipientName", "recipientEmail", "purchaserEmail", "verifiedRecipientEmail"].includes(key3) ? input[key3].length > 0 && input[key3].length <= 254 && !/[\u0000-\u001f\u007f]/.test(input[key3]) : SAFE.test(input[key3])))) {
5687
+ if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).some((key3) => !fields.includes(key3) && !(action === "checkouts" && key3 === "namedSeat")) || !fields.every((key3) => typeof input[key3] === "string" && (["recipientName", "recipientEmail", "purchaserEmail", "verifiedRecipientEmail"].includes(key3) ? input[key3].length > 0 && input[key3].length <= 254 && !/[\u0000-\u001f\u007f]/.test(input[key3]) : SAFE.test(input[key3])))) {
5442
5688
  throw new TypeError("invalid request");
5443
5689
  }
5690
+ if (action === "checkouts" && input.namedSeat !== void 0) {
5691
+ const seat = input.namedSeat;
5692
+ if (!seat || Object.keys(seat).some((key3) => !["recipientName", "recipientEmail", "purchaserEmail"].includes(key3)) || typeof seat.purchaserEmail !== "string" || seat.purchaserEmail.length > 254) throw new TypeError("invalid seat");
5693
+ checkoutNamedSeatSelection(seat, seat.purchaserEmail);
5694
+ }
5444
5695
  } catch {
5445
5696
  return respond({ error: "invalid_offer_request" }, 400);
5446
5697
  }
@@ -5513,8 +5764,9 @@ function createMembershipAuthorityHandler(options) {
5513
5764
  offerId: String(input.offerId),
5514
5765
  offerDigest: String(input.offerDigest),
5515
5766
  quoteDigest: String(input.quoteDigest),
5516
- priceId: String(input.priceId)
5517
- }, options.now?.() ?? Date.now());
5767
+ priceId: String(input.priceId),
5768
+ ...input.namedSeat ? { namedSeat: input.namedSeat } : {}
5769
+ }, options.now?.() ?? Date.now(), peer.seatPriceId);
5518
5770
  return respond({ ok: true, binding });
5519
5771
  }
5520
5772
  const offerId = await membershipOfferIdForRequest({ ...peer, applicationId: String(input.applicationId), requestId: String(input.requestId) });
@@ -5607,7 +5859,7 @@ async function requestMembershipCommercialOffer(options) {
5607
5859
  async function requestMembershipOfferPreview(options) {
5608
5860
  const result = await requestAuthority(options, "offer-preview", { skuId: options.skuId });
5609
5861
  const preview = result.preview;
5610
- if (!preview || preview.skuId !== options.skuId || typeof preview.founding !== "boolean" || !Number.isSafeInteger(preview.basePriceCents) || !Number.isSafeInteger(preview.dueTodayCents) || preview.dueTodayCents < 0 || preview.dueTodayCents > preview.basePriceCents || !Number.isSafeInteger(preview.renewalCents) || !/^[a-z]{3}$/.test(preview.currency) || !["month", "year"].includes(preview.interval) || preview.cohortLabel !== void 0 && typeof preview.cohortLabel !== "string" || preview.discountDuration !== void 0 && !["first_term", "while_active"].includes(preview.discountDuration)) throw new Error("invalid offer preview");
5862
+ if (!preview || preview.skuId !== options.skuId || typeof preview.founding !== "boolean" || !Number.isSafeInteger(preview.basePriceCents) || !Number.isSafeInteger(preview.dueTodayCents) || preview.dueTodayCents < 0 || preview.dueTodayCents > preview.basePriceCents || !Number.isSafeInteger(preview.renewalCents) || !/^[a-z]{3}$/.test(preview.currency) || !["month", "year"].includes(preview.interval) || preview.cohortLabel !== void 0 && typeof preview.cohortLabel !== "string" || preview.seatPriceCents !== void 0 && (!Number.isSafeInteger(preview.seatPriceCents) || preview.seatPriceCents < 0) || preview.discountDuration !== void 0 && !["first_term", "while_active"].includes(preview.discountDuration)) throw new Error("invalid offer preview");
5611
5863
  return preview;
5612
5864
  }
5613
5865
  async function requestMembershipCheckoutBinding(options) {
@@ -5617,7 +5869,8 @@ async function requestMembershipCheckoutBinding(options) {
5617
5869
  offerId: options.offerId,
5618
5870
  offerDigest: options.offerDigest,
5619
5871
  quoteDigest: options.quoteDigest,
5620
- priceId: options.priceId
5872
+ priceId: options.priceId,
5873
+ ...options.namedSeat ? { namedSeat: options.namedSeat } : {}
5621
5874
  });
5622
5875
  return verifyMembershipCheckoutBinding(result.binding, options);
5623
5876
  }
@@ -5827,7 +6080,7 @@ async function requestMembershipCancellation(options) {
5827
6080
  }
5828
6081
 
5829
6082
  // src/membership-seat-cancel.ts
5830
- import { initStripe, stripeRequest as stripeRequest9 } from "@odla-ai/stripe";
6083
+ import { initStripe, stripeRequest as stripeRequest10 } from "@odla-ai/stripe";
5831
6084
  async function cancelUnpaidNamedSeat(db, target, secretKey, now) {
5832
6085
  membershipClock(now);
5833
6086
  const state = await loadMembershipAuthorityState(db, target);
@@ -5855,7 +6108,7 @@ async function cancelUnpaidNamedSeat(db, target, secretKey, now) {
5855
6108
  }
5856
6109
  const metadata = { ...stripePartitionMetadata(target), applicationId: target.applicationId, membershipId: seat.primaryMembershipId, seatId: seat.id };
5857
6110
  const request = async (method, path, params) => {
5858
- const result = await stripeRequest9(
6111
+ const result = await stripeRequest10(
5859
6112
  { secretKey },
5860
6113
  method,
5861
6114
  path,
@@ -5881,9 +6134,9 @@ async function cancelUnpaidNamedSeat(db, target, secretKey, now) {
5881
6134
  if (payment.status === "succeeded" || payment.amount_received > 0) throw new Error("paid seat requires payment evidence and full refund");
5882
6135
  if (payment.status !== "canceled") {
5883
6136
  if (!["requires_payment_method", "requires_confirmation", "requires_action", "processing"].includes(payment.status)) throw new Error("seat payment outcome unresolved");
5884
- const id10 = payment.id;
5885
- payment = await request("POST", `/v1/payment_intents/${encodeURIComponent(id10)}/cancel`, { cancellation_reason: "abandoned" });
5886
- if (payment.id !== id10) throw new Error("seat cancellation receipt mismatch");
6137
+ const id11 = payment.id;
6138
+ payment = await request("POST", `/v1/payment_intents/${encodeURIComponent(id11)}/cancel`, { cancellation_reason: "abandoned" });
6139
+ if (payment.id !== id11) throw new Error("seat cancellation receipt mismatch");
5887
6140
  }
5888
6141
  if (!valid(payment) || payment.status !== "canceled" || payment.amount_received !== 0) throw new Error("seat cancellation unconfirmed");
5889
6142
  await db.transact([membershipUpdate("networkSeat", target.seatId, { status: "canceled", revision: Number(seat.revision) + 1, updatedAt: now })], {
@@ -5983,11 +6236,11 @@ async function drainMembershipEffects(db, options, now) {
5983
6236
 
5984
6237
  // src/schema-membership-offers.ts
5985
6238
  var attr8 = (type, optional = false, unique = false, indexed = false) => ({ type, optional, unique, indexed });
5986
- var id8 = () => attr8("string", false, true, true);
6239
+ var id9 = () => attr8("string", false, true, true);
5987
6240
  var key = () => attr8("string", false, false, true);
5988
6241
  var membershipOfferEntities = {
5989
6242
  membershipAuthorityState: { attrs: {
5990
- id: id8(),
6243
+ id: id9(),
5991
6244
  chapterId: key(),
5992
6245
  environment: key(),
5993
6246
  mode: key(),
@@ -5997,7 +6250,7 @@ var membershipOfferEntities = {
5997
6250
  updatedAt: attr8("number")
5998
6251
  } },
5999
6252
  membershipCheckoutOffer: { attrs: {
6000
- id: id8(),
6253
+ id: id9(),
6001
6254
  applicationId: key(),
6002
6255
  appId: key(),
6003
6256
  chapterId: key(),
@@ -6012,7 +6265,7 @@ var membershipOfferEntities = {
6012
6265
  authorityRevision: attr8("number")
6013
6266
  } },
6014
6267
  membershipOfferCheckout: { attrs: {
6015
- id: id8(),
6268
+ id: id9(),
6016
6269
  appId: key(),
6017
6270
  chapterId: key(),
6018
6271
  environment: key(),
@@ -6023,20 +6276,21 @@ var membershipOfferEntities = {
6023
6276
  offerDigest: attr8("string"),
6024
6277
  quoteDigest: attr8("string"),
6025
6278
  priceId: attr8("string"),
6026
- boundAt: attr8("number")
6279
+ boundAt: attr8("number"),
6280
+ namedSeat: attr8("json", true)
6027
6281
  } }
6028
6282
  };
6029
6283
  var membershipOfferRules = Object.fromEntries(Object.keys(membershipOfferEntities).map((ns) => [ns, { view: "false", create: "false", update: "false", delete: "false" }]));
6030
6284
 
6031
6285
  // src/schema-membership-authority.ts
6032
6286
  var attr9 = (type, optional = false, unique = false, indexed = false) => ({ type, optional, unique, indexed });
6033
- var id9 = () => attr9("string", false, true, true);
6287
+ var id10 = () => attr9("string", false, true, true);
6034
6288
  var key2 = (optional = false) => attr9("string", optional, false, true);
6035
6289
  var scope = () => ({ appId: key2(), chapterId: key2(), environment: key2(), runtime: key2(), applicationId: key2(), skuId: key2() });
6036
6290
  var times = () => ({ revision: attr9("number"), createdAt: attr9("number"), updatedAt: attr9("number") });
6037
6291
  var membershipAuthorityEntities = {
6038
6292
  networkMembership: { attrs: {
6039
- id: id9(),
6293
+ id: id10(),
6040
6294
  membershipId: key2(),
6041
6295
  ...scope(),
6042
6296
  personId: key2(true),
@@ -6076,7 +6330,7 @@ var membershipAuthorityEntities = {
6076
6330
  restartId: key2(true)
6077
6331
  } },
6078
6332
  membershipRestart: { attrs: {
6079
- id: id9(),
6333
+ id: id10(),
6080
6334
  ...scope(),
6081
6335
  membershipId: key2(),
6082
6336
  restartId: key2(),
@@ -6086,7 +6340,7 @@ var membershipAuthorityEntities = {
6086
6340
  createdAt: attr9("number")
6087
6341
  } },
6088
6342
  membershipPurchaseHistory: { attrs: {
6089
- id: id9(),
6343
+ id: id10(),
6090
6344
  ...scope(),
6091
6345
  membershipId: key2(),
6092
6346
  providerSubscriptionId: key2(),
@@ -6095,7 +6349,7 @@ var membershipAuthorityEntities = {
6095
6349
  archivedAt: attr9("number")
6096
6350
  } },
6097
6351
  networkEntitlement: { attrs: {
6098
- id: id9(),
6352
+ id: id10(),
6099
6353
  ...scope(),
6100
6354
  membershipId: key2(),
6101
6355
  seatId: key2(true),
@@ -6112,10 +6366,12 @@ var membershipAuthorityEntities = {
6112
6366
  journeyApprovedAt: attr9("number", true)
6113
6367
  } },
6114
6368
  networkSeat: { attrs: {
6115
- id: id9(),
6369
+ id: id10(),
6116
6370
  ...scope(),
6117
6371
  primaryMembershipId: key2(),
6118
- purchaserUserId: key2(),
6372
+ purchaserUserId: key2(true),
6373
+ purchaserEmailNormalized: key2(true),
6374
+ combinedInvoice: attr9("json", true),
6119
6375
  recipientName: attr9("string"),
6120
6376
  recipientEmailNormalized: key2(),
6121
6377
  recipientClaimId: key2(),
@@ -6147,9 +6403,9 @@ var membershipAuthorityEntities = {
6147
6403
  refundedCents: attr9("number", true),
6148
6404
  providerRefundIds: attr9("json", true)
6149
6405
  } },
6150
- networkSeatRecipient: { attrs: { id: id9(), appId: key2(), chapterId: key2(), environment: key2(), runtime: key2(), seatId: key2(), recipientEmailNormalized: key2(), createdAt: attr9("number") } },
6406
+ networkSeatRecipient: { attrs: { id: id10(), appId: key2(), chapterId: key2(), environment: key2(), runtime: key2(), seatId: key2(), recipientEmailNormalized: key2(), createdAt: attr9("number") } },
6151
6407
  membershipEffect: { attrs: {
6152
- id: id9(),
6408
+ id: id10(),
6153
6409
  chapterId: key2(),
6154
6410
  environment: key2(),
6155
6411
  runtime: key2(),
@@ -6274,14 +6530,14 @@ function validateMemberContentDocument(input, options = {}) {
6274
6530
  continue;
6275
6531
  }
6276
6532
  unknownKeys(raw2, PAGE_KEYS, path, errors);
6277
- const id10 = textValue(raw2.id, `${path}.id`, errors, 80, true) ?? "";
6533
+ const id11 = textValue(raw2.id, `${path}.id`, errors, 80, true) ?? "";
6278
6534
  const slug = textValue(raw2.slug, `${path}.slug`, errors, 120, true) ?? "";
6279
6535
  const title = textValue(raw2.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
6280
- if (id10 && !ID.test(id10)) errors.push(`${path}.id must be a lowercase kebab-case id`);
6536
+ if (id11 && !ID.test(id11)) errors.push(`${path}.id must be a lowercase kebab-case id`);
6281
6537
  if (slug && !SLUG8.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
6282
- if (ids2.has(id10) || knownParentIds.has(id10)) errors.push(`${path}.id duplicates page "${id10}"`);
6538
+ if (ids2.has(id11) || knownParentIds.has(id11)) errors.push(`${path}.id duplicates page "${id11}"`);
6283
6539
  if (slugs.has(slug) || (options.parentPages ?? []).some((page) => page.slug === slug)) errors.push(`${path}.slug duplicates "${slug}"`);
6284
- ids2.add(id10);
6540
+ ids2.add(id11);
6285
6541
  slugs.add(slug);
6286
6542
  const navRaw = record(raw2.navigation);
6287
6543
  let navigation = { section: "General", order: index2 };
@@ -6337,7 +6593,7 @@ function validateMemberContentDocument(input, options = {}) {
6337
6593
  else if (owner === "chapter") upsell = { owner, ...offer ? { offer } : {} };
6338
6594
  }
6339
6595
  }
6340
- pages.push({ id: id10, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
6596
+ pages.push({ id: id11, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
6341
6597
  }
6342
6598
  const allIds = /* @__PURE__ */ new Set([...knownParentIds, ...ids2]);
6343
6599
  for (const [index2, page] of pages.entries()) {
@@ -6543,8 +6799,8 @@ function resourceValue(value, path, errors) {
6543
6799
  return null;
6544
6800
  }
6545
6801
  unknownKeys2(raw2, RESOURCE_KEYS, path, errors);
6546
- const id10 = text3(raw2.id, `${path}.id`, errors, 80) ?? "";
6547
- if (id10 && !ID2.test(id10)) errors.push(`${path}.id must be lowercase kebab-case`);
6802
+ const id11 = text3(raw2.id, `${path}.id`, errors, 80) ?? "";
6803
+ if (id11 && !ID2.test(id11)) errors.push(`${path}.id must be lowercase kebab-case`);
6548
6804
  const kind = text3(raw2.kind, `${path}.kind`, errors) ?? "";
6549
6805
  if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
6550
6806
  const title = text3(raw2.title, `${path}.title`, errors, 240) ?? "";
@@ -6601,7 +6857,7 @@ function resourceValue(value, path, errors) {
6601
6857
  updatedAt: integer3(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
6602
6858
  ...promotedFrom ? { promotedFrom } : {}
6603
6859
  };
6604
- return { id: id10, kind, title, ...summary ? { summary } : {}, tags, scope: scope2, status: status2, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
6860
+ return { id: id11, kind, title, ...summary ? { summary } : {}, tags, scope: scope2, status: status2, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
6605
6861
  }
6606
6862
  function validateBuilderLibraryDocument(value, options = {}) {
6607
6863
  const errors = [];
@@ -6632,7 +6888,7 @@ function validateBuilderLibraryDocument(value, options = {}) {
6632
6888
  const inputs = Array.isArray(raw2.resources) ? raw2.resources : [];
6633
6889
  if (inputs.length > MAX_RESOURCES) errors.push(`document.resources may contain at most ${MAX_RESOURCES} items`);
6634
6890
  const resources = inputs.map((item, index2) => resourceValue(item, `document.resources[${index2}]`, errors)).filter(Boolean);
6635
- const ids2 = resources.map(({ id: id10 }) => id10);
6891
+ const ids2 = resources.map(({ id: id11 }) => id11);
6636
6892
  if (new Set(ids2).size !== ids2.length) errors.push("document.resources ids must be unique");
6637
6893
  for (const resource of resources) {
6638
6894
  if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
@@ -6886,7 +7142,7 @@ async function frozenCheckoutVariables(db, app) {
6886
7142
  const intent = (await db.query({ subscriptionCheckoutIntents: { $: { where: { id: String(app.id) }, limit: 1 } } })).subscriptionCheckoutIntents?.[0];
6887
7143
  const offer = intent?.membershipOffer, contract = offer?.contract;
6888
7144
  const offering = contract?.offering, quote = intent?.paymentQuote;
6889
- const amount = contract?.dueTodayCents ?? intent?.priceCents;
7145
+ const amount = quote?.lineItems?.dueTodayCents ?? contract?.dueTodayCents ?? intent?.priceCents;
6890
7146
  const currency = offering?.currency ?? intent?.currency;
6891
7147
  let formatted = "";
6892
7148
  if (Number.isSafeInteger(amount) && Number(amount) > 0 && typeof currency === "string" && /^[a-z]{3}$/.test(currency)) {
@@ -6962,11 +7218,11 @@ async function sendTemplated(deps, input) {
6962
7218
  const previous = prior[0];
6963
7219
  if (previous && (previous.groupId !== input.group.id || previous.template !== input.template || previous.to !== decision.to || previous.transport !== decision.transport || previous.applicationId !== input.applicationId)) return { sent: false, reason: "delivery-identity-conflict" };
6964
7220
  const hash2 = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input.dedupeKey));
6965
- const id10 = previous ? String(previous.id) : `email_${Array.from(new Uint8Array(hash2), (b) => b.toString(16).padStart(2, "0")).join("")}`;
7221
+ const id11 = previous ? String(previous.id) : `email_${Array.from(new Uint8Array(hash2), (b) => b.toString(16).padStart(2, "0")).join("")}`;
6966
7222
  const attempt = Number(previous?.deliveryAttempt ?? 0) + 1;
6967
7223
  if (!Number.isSafeInteger(attempt) || attempt < 1) return { sent: false, reason: "delivery-state-invalid" };
6968
7224
  const row2 = {
6969
- id: id10,
7225
+ id: id11,
6970
7226
  groupId: input.group.id,
6971
7227
  to: decision.to,
6972
7228
  template: input.template,
@@ -6984,15 +7240,15 @@ async function sendTemplated(deps, input) {
6984
7240
  if (isRetiredEmailCopy(String(row2.subject), String(row2.body))) return { sent: false, reason: "template-retired" };
6985
7241
  if (validateEmailCopy3({ subject: row2.subject, text: row2.body }, { allowedVariables: [] }).length) return { sent: false, reason: "template-invalid" };
6986
7242
  try {
6987
- const claim = await deps.db.transact([{ t: "update", ns: "emailLog", id: id10, attrs: row2 }], {
6988
- mutationId: `email-claim:${id10}:${attempt}`,
7243
+ const claim = await deps.db.transact([{ t: "update", ns: "emailLog", id: id11, attrs: row2 }], {
7244
+ mutationId: `email-claim:${id11}:${attempt}`,
6989
7245
  guards: [previous ? {
6990
7246
  ns: "emailLog",
6991
- id: id10,
7247
+ id: id11,
6992
7248
  exists: true,
6993
7249
  equals: { deliveryState: "retry_ready", ...previous.deliveryAttempt !== void 0 ? { deliveryAttempt: previous.deliveryAttempt } : {} },
6994
7250
  ...previous.deliveryAttempt === void 0 ? { absent: ["deliveryAttempt"] } : {}
6995
- } : { ns: "emailLog", id: id10, exists: false }]
7251
+ } : { ns: "emailLog", id: id11, exists: false }]
6996
7252
  });
6997
7253
  if (claim.duplicate) return { sent: false, reason: "delivery-uncertain" };
6998
7254
  } catch (error) {
@@ -7012,16 +7268,16 @@ async function sendTemplated(deps, input) {
7012
7268
  if (!response || typeof response.messageId !== "string" || !response.messageId) throw new Error("missing delivery receipt");
7013
7269
  messageId = response.messageId;
7014
7270
  } catch {
7015
- await deps.db.transact([{ t: "update", ns: "emailLog", id: id10, attrs: { deliveryState: "uncertain", error: "delivery-uncertain" } }], {
7016
- guards: [{ ns: "emailLog", id: id10, exists: true, equals: { deliveryState: "sending", deliveryAttempt: attempt } }]
7271
+ await deps.db.transact([{ t: "update", ns: "emailLog", id: id11, attrs: { deliveryState: "uncertain", error: "delivery-uncertain" } }], {
7272
+ guards: [{ ns: "emailLog", id: id11, exists: true, equals: { deliveryState: "sending", deliveryAttempt: attempt } }]
7017
7273
  });
7018
7274
  return { sent: false, reason: "delivery-uncertain" };
7019
7275
  }
7020
- await deps.db.transact([{ t: "update", ns: "emailLog", id: id10, attrs: { deliveryState: "sent", error: "", messageId, sentAt: deps.now() } }], {
7021
- mutationId: `email-confirmed:${id10}:${attempt}`,
7276
+ await deps.db.transact([{ t: "update", ns: "emailLog", id: id11, attrs: { deliveryState: "sent", error: "", messageId, sentAt: deps.now() } }], {
7277
+ mutationId: `email-confirmed:${id11}:${attempt}`,
7022
7278
  guards: [{
7023
7279
  ns: "emailLog",
7024
- id: id10,
7280
+ id: id11,
7025
7281
  exists: true,
7026
7282
  equals: { deliveryState: "sending", deliveryAttempt: attempt }
7027
7283
  }]
@@ -7279,14 +7535,14 @@ function validateAdmissionGrantDocument(input) {
7279
7535
  const seenEmails = /* @__PURE__ */ new Set();
7280
7536
  for (const [index2, raw2] of rawGrants.slice(0, MAX_GRANTS).entries()) {
7281
7537
  const grant = raw2 && typeof raw2 === "object" ? raw2 : {};
7282
- const id10 = typeof grant.id === "string" ? grant.id : "";
7538
+ const id11 = typeof grant.id === "string" ? grant.id : "";
7283
7539
  const email = typeof grant.email === "string" ? grant.email.trim() : "";
7284
7540
  const normalized = normalizeAdmissionEmail(email);
7285
7541
  const decision = grant.decision;
7286
7542
  const note = typeof grant.note === "string" ? grant.note.trim() : void 0;
7287
7543
  const expiresAt = grant.expiresAt === void 0 ? void 0 : Number(grant.expiresAt);
7288
- if (!ID3.test(id10)) errors.push(`document.grants[${index2}].id must be an identifier`);
7289
- if (seenIds.has(id10)) errors.push(`document.grants repeats id "${id10}"`);
7544
+ if (!ID3.test(id11)) errors.push(`document.grants[${index2}].id must be an identifier`);
7545
+ if (seenIds.has(id11)) errors.push(`document.grants repeats id "${id11}"`);
7290
7546
  if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index2}].email must be valid`);
7291
7547
  if (seenEmails.has(normalized)) errors.push(`document.grants repeats email "${normalized}"`);
7292
7548
  if (decision !== "waive_interview" && decision !== "require_interview") {
@@ -7296,10 +7552,10 @@ function validateAdmissionGrantDocument(input) {
7296
7552
  if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= 0)) {
7297
7553
  errors.push(`document.grants[${index2}].expiresAt must be epoch milliseconds`);
7298
7554
  }
7299
- seenIds.add(id10);
7555
+ seenIds.add(id11);
7300
7556
  seenEmails.add(normalized);
7301
7557
  grants.push({
7302
- id: id10,
7558
+ id: id11,
7303
7559
  email,
7304
7560
  decision,
7305
7561
  ...note ? { note } : {},
@@ -7371,8 +7627,8 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
7371
7627
  const incomingIds = new Set(document.grants.map((grant) => `admission:parent:${document.source.appId}:${grant.id}`));
7372
7628
  const ops = [];
7373
7629
  for (const row2 of current) {
7374
- const id10 = String(row2.id ?? "");
7375
- if (id10 && !incomingIds.has(id10)) ops.push({ t: "update", ns: "admissionGrants", id: id10, attrs: {
7630
+ const id11 = String(row2.id ?? "");
7631
+ if (id11 && !incomingIds.has(id11)) ops.push({ t: "update", ns: "admissionGrants", id: id11, attrs: {
7376
7632
  active: false,
7377
7633
  revokedAt: receivedAt,
7378
7634
  revokedBy: document.source.actor,
@@ -7381,10 +7637,10 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
7381
7637
  } });
7382
7638
  }
7383
7639
  for (const grant of document.grants) {
7384
- const id10 = `admission:parent:${document.source.appId}:${grant.id}`;
7385
- const existing = current.find((row2) => row2.id === id10);
7386
- ops.push({ t: "update", ns: "admissionGrants", id: id10, attrs: {
7387
- id: id10,
7640
+ const id11 = `admission:parent:${document.source.appId}:${grant.id}`;
7641
+ const existing = current.find((row2) => row2.id === id11);
7642
+ ops.push({ t: "update", ns: "admissionGrants", id: id11, attrs: {
7643
+ id: id11,
7388
7644
  sourceGrantId: grant.id,
7389
7645
  sourceScope: "parent",
7390
7646
  sourceId: document.source.appId,
@@ -7405,7 +7661,7 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
7405
7661
  ...!grant.note && existing?.note !== void 0 ? ["note"] : [],
7406
7662
  ...grant.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
7407
7663
  ];
7408
- if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id10, attrs: retract });
7664
+ if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id11, attrs: retract });
7409
7665
  }
7410
7666
  if (previous?.active) ops.push({ t: "update", ns: "admissionGrantRevisions", id: previous.id, attrs: { active: false } });
7411
7667
  const revisionId = `admission-revision:${document.source.appId}:${document.target.environment}:${document.revision}`;
@@ -7476,10 +7732,10 @@ function validateGiftOfferCatalog(input, tierIds) {
7476
7732
  }
7477
7733
  const allowed2 = /* @__PURE__ */ new Set(["id", "target", "billingMode", "stripePriceId", "priceCents", "currency", "redemptionLifetimeHours", "activeFrom", "activeUntil", "title", "description", "ctaLabel"]);
7478
7734
  for (const key3 of Object.keys(item)) if (!allowed2.has(key3)) errors.push(`${path}.${key3} is not supported`);
7479
- const id10 = text4(item.id, `${path}.id`, errors, 80);
7480
- if (id10 && !ID4.test(id10)) errors.push(`${path}.id must be a lowercase kebab-case id`);
7481
- if (ids2.has(id10)) errors.push(`${path}.id duplicates "${id10}"`);
7482
- ids2.add(id10);
7735
+ const id11 = text4(item.id, `${path}.id`, errors, 80);
7736
+ if (id11 && !ID4.test(id11)) errors.push(`${path}.id must be a lowercase kebab-case id`);
7737
+ if (ids2.has(id11)) errors.push(`${path}.id duplicates "${id11}"`);
7738
+ ids2.add(id11);
7483
7739
  const targetRaw = record3(item.target);
7484
7740
  let target = { type: "entitlement", entitlementKey: "invalid" };
7485
7741
  if (targetRaw?.type === "tier") {
@@ -7505,7 +7761,7 @@ function validateGiftOfferCatalog(input, tierIds) {
7505
7761
  if (activeUntil !== void 0 && (!Number.isSafeInteger(activeUntil) || activeUntil < 1)) errors.push(`${path}.activeUntil must be epoch milliseconds`);
7506
7762
  if (activeFrom !== void 0 && activeUntil !== void 0 && activeUntil <= activeFrom) errors.push(`${path}.activeUntil must follow activeFrom`);
7507
7763
  offers.push({
7508
- id: id10,
7764
+ id: id11,
7509
7765
  target,
7510
7766
  billingMode: item.billingMode,
7511
7767
  stripePriceId,
@@ -7554,10 +7810,10 @@ async function ensureProjectedPerson(deps, input, options = {}) {
7554
7810
  const existing = await find();
7555
7811
  if (existing) return { record: existing, created: false };
7556
7812
  const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`chapter:person:v1:${email}`)));
7557
- const id10 = `person-${Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
7813
+ const id11 = `person-${Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
7558
7814
  try {
7559
7815
  const created = await createRecord({ ...deps, db: deps.db }, {
7560
- id: id10,
7816
+ id: id11,
7561
7817
  type: "person",
7562
7818
  input: { ...input, email },
7563
7819
  ...options
@@ -7980,9 +8236,9 @@ function subAnnualCents(sub) {
7980
8236
  }
7981
8237
 
7982
8238
  // src/payments-stripe.ts
7983
- import { stripeRequest as stripeRequest10, verifyStripeSignature as verifyStripeSignature2 } from "@odla-ai/stripe";
8239
+ import { stripeRequest as stripeRequest11, verifyStripeSignature as verifyStripeSignature2 } from "@odla-ai/stripe";
7984
8240
  async function stripeCall(sk, method, path, params, idempotencyKey) {
7985
- return stripeRequest10(
8241
+ return stripeRequest11(
7986
8242
  { secretKey: sk },
7987
8243
  method,
7988
8244
  path,
@@ -8060,8 +8316,8 @@ async function updateClerkUserMetadata(secretKey, userId, input, fetchImpl = fet
8060
8316
  return res.ok;
8061
8317
  }
8062
8318
  async function updateClerkUserMetadataByEmail(secretKey, email, input, fetchImpl = fetch) {
8063
- const id10 = await existingClerkUserId(secretKey, email, fetchImpl);
8064
- return id10 ? updateClerkUserMetadata(secretKey, id10, input, fetchImpl) : false;
8319
+ const id11 = await existingClerkUserId(secretKey, email, fetchImpl);
8320
+ return id11 ? updateClerkUserMetadata(secretKey, id11, input, fetchImpl) : false;
8065
8321
  }
8066
8322
  async function existingClerkUserId(secretKey, email, fetchImpl) {
8067
8323
  const auth = { authorization: `Bearer ${secretKey}` };
@@ -8134,10 +8390,10 @@ async function clerkGetUserByEmail(secretKey, email, fetchImpl = fetch, source =
8134
8390
  const user = Array.isArray(data) ? data[0] : void 0;
8135
8391
  return user ? toRecord(user, source) : null;
8136
8392
  }
8137
- async function clerkGetUser(secretKey, id10, fetchImpl = fetch, source = "public") {
8138
- const data = await clerkGet(`/v1/users/${encodeURIComponent(id10)}`, secretKey, fetchImpl).catch(() => null);
8393
+ async function clerkGetUser(secretKey, id11, fetchImpl = fetch, source = "public") {
8394
+ const data = await clerkGet(`/v1/users/${encodeURIComponent(id11)}`, secretKey, fetchImpl).catch(() => null);
8139
8395
  const record4 = data ? toRecord(data, source) : null;
8140
- return record4?.id === id10 ? record4 : null;
8396
+ return record4?.id === id11 ? record4 : null;
8141
8397
  }
8142
8398
  async function clerkListUsers(secretKey, fetchImpl = fetch, source = "public") {
8143
8399
  const out = [];
@@ -8153,8 +8409,8 @@ async function clerkListUsers(secretKey, fetchImpl = fetch, source = "public") {
8153
8409
  }
8154
8410
  return out;
8155
8411
  }
8156
- async function clerkSetRole(secretKey, id10, role, fetchImpl = fetch, source = "public") {
8157
- const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id10)}/metadata`, {
8412
+ async function clerkSetRole(secretKey, id11, role, fetchImpl = fetch, source = "public") {
8413
+ const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id11)}/metadata`, {
8158
8414
  method: "PATCH",
8159
8415
  headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
8160
8416
  redirect: "manual",
@@ -8171,7 +8427,7 @@ function planPrivateRoleMigration(input) {
8171
8427
  if (input.environment !== "dev") blockers.push("dev_only");
8172
8428
  if (!input.ladder.length || new Set(input.ladder).size !== input.ladder.length || input.ladder.some((role) => !role || role === "super_admin")) blockers.push("invalid_role_ladder");
8173
8429
  const expected = new Set(input.expectedRuntimes);
8174
- if (!expected.size || expected.size !== input.expectedRuntimes.length || input.expectedRuntimes.some((id10) => !id10)) blockers.push("runtime_inventory_required");
8430
+ if (!expected.size || expected.size !== input.expectedRuntimes.length || input.expectedRuntimes.some((id11) => !id11)) blockers.push("runtime_inventory_required");
8175
8431
  if (input.runtimes.length !== expected.size || new Set(input.runtimes.map((r) => r.runtime)).size !== expected.size || input.runtimes.some((r) => !expected.has(r.runtime))) blockers.push("runtime_inventory_mismatch");
8176
8432
  for (const runtime of input.expectedRuntimes) {
8177
8433
  const proof = input.runtimes.find((r) => r.runtime === runtime);
@@ -8201,7 +8457,7 @@ function planPrivateRoleMigration(input) {
8201
8457
  if (privateRole === role && !Object.hasOwn(user.publicMetadata, "role")) alreadyCurrent++;
8202
8458
  else changes.push({ userId: user.id, role, publicRole, privateRole });
8203
8459
  }
8204
- if (!input.protectedSuperAdminUserIds.some((id10) => ids2.has(id10))) blockers.push("protected_operator_grant_required");
8460
+ if (!input.protectedSuperAdminUserIds.some((id11) => ids2.has(id11))) blockers.push("protected_operator_grant_required");
8205
8461
  return { ready: blockers.length === 0, blockers, changes, alreadyCurrent, totalUsers: input.users.length };
8206
8462
  }
8207
8463
 
@@ -8779,10 +9035,10 @@ function tiersOf(value, errors) {
8779
9035
  return [];
8780
9036
  }
8781
9037
  unknownKeys3(item, TIER_KEYS2, path, errors);
8782
- const id10 = text5(item.id, `${path}.id`, errors, 80);
8783
- if (id10 && !TIER.test(id10)) errors.push(`${path}.id must be a lowercase kebab-case id`);
8784
- if (seen.has(id10)) errors.push(`${path}.id duplicates "${id10}"`);
8785
- seen.add(id10);
9038
+ const id11 = text5(item.id, `${path}.id`, errors, 80);
9039
+ if (id11 && !TIER.test(id11)) errors.push(`${path}.id must be a lowercase kebab-case id`);
9040
+ if (seen.has(id11)) errors.push(`${path}.id duplicates "${id11}"`);
9041
+ seen.add(id11);
8786
9042
  const name = text5(item.name, `${path}.name`, errors, 160);
8787
9043
  if (typeof item.available !== "boolean") errors.push(`${path}.available must be boolean`);
8788
9044
  if (!Number.isInteger(item.sortOrder) || Number(item.sortOrder) < 0) errors.push(`${path}.sortOrder must be a non-negative integer`);
@@ -8803,7 +9059,7 @@ function tiersOf(value, errors) {
8803
9059
  }
8804
9060
  const blurb = item.blurb === void 0 ? void 0 : text5(item.blurb, `${path}.blurb`, errors, 4e3);
8805
9061
  return [{
8806
- id: id10,
9062
+ id: id11,
8807
9063
  name,
8808
9064
  available: item.available === true,
8809
9065
  sortOrder: Number(item.sortOrder) || 0,
@@ -8865,15 +9121,15 @@ function policyOf(value, errors, tiers2) {
8865
9121
  if (input.tierPolicies !== void 0) {
8866
9122
  const overrides = recordOf(input.tierPolicies);
8867
9123
  if (!overrides || Object.keys(overrides).length > 20) errors.push("document.policy.tierPolicies must be a bounded tier map");
8868
- else for (const [id10, raw2] of Object.entries(overrides)) {
8869
- const path = `document.policy.tierPolicies.${id10}`;
9124
+ else for (const [id11, raw2] of Object.entries(overrides)) {
9125
+ const path = `document.policy.tierPolicies.${id11}`;
8870
9126
  const policy2 = recordOf(raw2);
8871
- if (!tiers2.some((tier) => tier.id === id10) || !policy2) {
9127
+ if (!tiers2.some((tier) => tier.id === id11) || !policy2) {
8872
9128
  errors.push(`${path} must name a declared tier and policy`);
8873
9129
  continue;
8874
9130
  }
8875
9131
  unknownKeys3(policy2, /* @__PURE__ */ new Set(["refundPolicyText", "trustCopy"]), path, errors);
8876
- tierPolicies[id10] = {
9132
+ tierPolicies[id11] = {
8877
9133
  refundPolicyText: text5(policy2.refundPolicyText, `${path}.refundPolicyText`, errors),
8878
9134
  ...policy2.trustCopy !== void 0 ? { trustCopy: text5(policy2.trustCopy, `${path}.trustCopy`, errors) } : {}
8879
9135
  };
@@ -9198,7 +9454,7 @@ var exclusions = (chapterId, paidTierIds) => [
9198
9454
  id: chapterId,
9199
9455
  reason: "provider_identity",
9200
9456
  paths: [
9201
- ...paidTierIds.map((id10) => `tiers[${id10}].stripePriceId`),
9457
+ ...paidTierIds.map((id11) => `tiers[${id11}].stripePriceId`),
9202
9458
  "groups.stripePublishableKey",
9203
9459
  "applications.stripeCustomerId",
9204
9460
  "applications.stripeSubscriptionId",
@@ -9240,9 +9496,9 @@ async function exportChapterPromotion(input) {
9240
9496
  sourceIds: [`signup-control:${document.revision}`, document.source.appId, document.target.chapterId].sort(),
9241
9497
  receiptDigests: [input.digest]
9242
9498
  };
9243
- const artifact = (kind, id10, content) => ({
9499
+ const artifact = (kind, id11, content) => ({
9244
9500
  kind,
9245
- id: id10,
9501
+ id: id11,
9246
9502
  sourceRevision: revision,
9247
9503
  provenance: promotionProvenance,
9248
9504
  content: canonicalPromotionValue(content)
@@ -9287,7 +9543,7 @@ var MEMBER_SYSTEM_RECORD_OWNERS = {
9287
9543
  provider: "originating-provider",
9288
9544
  protectedRoles: "odla-platform"
9289
9545
  };
9290
- var rows2 = (group) => group.fields.map((id10) => ({ ...group, id: id10 }));
9546
+ var rows2 = (group) => group.fields.map((id11) => ({ ...group, id: id11 }));
9291
9547
  var MEMBER_SYSTEM_AUTHORITY_MATRIX = [
9292
9548
  ...rows2({
9293
9549
  scope: "network",