@odla-ai/chapter 0.44.0 → 0.45.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.
@@ -39,8 +39,8 @@ var import_jose = require("jose");
39
39
 
40
40
  // src/auth.ts
41
41
  function roleFromClaim(payload, auth) {
42
- const raw = payload[auth.claim];
43
- return typeof raw === "string" && auth.ladder.includes(raw) ? raw : auth.ladder[0];
42
+ const raw2 = payload[auth.claim];
43
+ return typeof raw2 === "string" && auth.ladder.includes(raw2) ? raw2 : auth.ladder[0];
44
44
  }
45
45
  function isAdminRole(role, auth) {
46
46
  return role === auth.adminRole;
@@ -223,7 +223,7 @@ function resolveApplication(a) {
223
223
  throw new Error(`defineChapter.application.${name}: field "${unsafe}" must match ${SCHEMA_FIELD.source}`);
224
224
  }
225
225
  }
226
- const duplicate = [...required, ...optional].find((field, index, fields) => fields.indexOf(field) !== index);
226
+ const duplicate = [...required, ...optional].find((field, index2, fields) => fields.indexOf(field) !== index2);
227
227
  if (duplicate) {
228
228
  throw new Error(`defineChapter.application: duplicate field "${duplicate}" across required/optional lists`);
229
229
  }
@@ -371,8 +371,8 @@ function tierPayable(tier, group, hasSecretKey) {
371
371
  function byOrder(a, b) {
372
372
  return a.sortOrder - b.sortOrder || a.id.localeCompare(b.id);
373
373
  }
374
- function resolveTiers(group, rows5 = []) {
375
- const stored = rows5.filter((tier) => tier.active);
374
+ function resolveTiers(group, rows6 = []) {
375
+ const stored = rows6.filter((tier) => tier.active);
376
376
  if (stored.length) return [...stored].sort(byOrder);
377
377
  return [{
378
378
  id: "standard",
@@ -418,15 +418,15 @@ function rowToTier(row, groupId) {
418
418
  }
419
419
  async function loadTiers(db, group) {
420
420
  const groupId = String(group.id ?? "");
421
- let rows5;
421
+ let rows6;
422
422
  try {
423
- rows5 = (await db.query({
423
+ rows6 = (await db.query({
424
424
  tiers: { $: { where: { groupId }, order: { sortOrder: "asc" }, limit: 100 } }
425
425
  })).tiers;
426
426
  } catch {
427
- rows5 = void 0;
427
+ rows6 = void 0;
428
428
  }
429
- const list = Array.isArray(rows5) ? rows5.map((row) => rowToTier(row, groupId)) : [];
429
+ const list = Array.isArray(rows6) ? rows6.map((row) => rowToTier(row, groupId)) : [];
430
430
  return resolveTiers(group, list);
431
431
  }
432
432
  async function loadOfferableTiers(db, group, hasSecretKey) {
@@ -563,8 +563,8 @@ function canonicalSignupControl(value) {
563
563
  return canonicalValue(value);
564
564
  }
565
565
  async function signupControlDigest(document) {
566
- const digest = await crypto.subtle.digest("SHA-256", encoder.encode(canonicalSignupControl(document)));
567
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
566
+ const digest2 = await crypto.subtle.digest("SHA-256", encoder.encode(canonicalSignupControl(document)));
567
+ return `sha256:${Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
568
568
  }
569
569
 
570
570
  // src/admission-grants.ts
@@ -600,24 +600,24 @@ function validateAdmissionGrantDocument(input) {
600
600
  const grants = [];
601
601
  const seenIds = /* @__PURE__ */ new Set();
602
602
  const seenEmails = /* @__PURE__ */ new Set();
603
- for (const [index, raw] of rawGrants.slice(0, MAX_GRANTS).entries()) {
604
- const grant = raw && typeof raw === "object" ? raw : {};
603
+ for (const [index2, raw2] of rawGrants.slice(0, MAX_GRANTS).entries()) {
604
+ const grant = raw2 && typeof raw2 === "object" ? raw2 : {};
605
605
  const id = typeof grant.id === "string" ? grant.id : "";
606
606
  const email = typeof grant.email === "string" ? grant.email.trim() : "";
607
607
  const normalized = normalizeAdmissionEmail(email);
608
608
  const decision = grant.decision;
609
609
  const note = typeof grant.note === "string" ? grant.note.trim() : void 0;
610
610
  const expiresAt = grant.expiresAt === void 0 ? void 0 : Number(grant.expiresAt);
611
- if (!ID.test(id)) errors.push(`document.grants[${index}].id must be an identifier`);
611
+ if (!ID.test(id)) errors.push(`document.grants[${index2}].id must be an identifier`);
612
612
  if (seenIds.has(id)) errors.push(`document.grants repeats id "${id}"`);
613
- if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index}].email must be valid`);
613
+ if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index2}].email must be valid`);
614
614
  if (seenEmails.has(normalized)) errors.push(`document.grants repeats email "${normalized}"`);
615
615
  if (decision !== "waive_interview" && decision !== "require_interview") {
616
- errors.push(`document.grants[${index}].decision is invalid`);
616
+ errors.push(`document.grants[${index2}].decision is invalid`);
617
617
  }
618
- if (note && note.length > MAX_NOTE) errors.push(`document.grants[${index}].note is too long`);
618
+ if (note && note.length > MAX_NOTE) errors.push(`document.grants[${index2}].note is too long`);
619
619
  if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= 0)) {
620
- errors.push(`document.grants[${index}].expiresAt must be epoch milliseconds`);
620
+ errors.push(`document.grants[${index2}].expiresAt must be epoch milliseconds`);
621
621
  }
622
622
  seenIds.add(id);
623
623
  seenEmails.add(normalized);
@@ -685,7 +685,7 @@ async function loadLatestAdmissionRevision(db, sourceId, targetId, environment)
685
685
  return row ? row : null;
686
686
  }
687
687
  async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
688
- const { document, digest } = envelope;
688
+ const { document, digest: digest2 } = envelope;
689
689
  const current = rows((await db.query({ admissionGrants: { $: {
690
690
  where: { sourceScope: "parent", sourceId: document.source.appId, targetId: document.target.chapterId },
691
691
  limit: MAX_GRANTS
@@ -737,11 +737,11 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
737
737
  targetId: document.target.chapterId,
738
738
  environment: document.target.environment,
739
739
  revision: document.revision,
740
- digest,
740
+ digest: digest2,
741
741
  active: true,
742
742
  receivedAt
743
743
  } });
744
- await db.transact(ops, { mutationId: `admission-grants:${document.source.appId}:${document.target.environment}:${document.revision}:${digest}` });
744
+ await db.transact(ops, { mutationId: `admission-grants:${document.source.appId}:${document.target.environment}:${document.revision}:${digest2}` });
745
745
  }
746
746
 
747
747
  // src/flow-fields.ts
@@ -759,8 +759,8 @@ var APPLICATION_LABELS = {
759
759
  state: "State"
760
760
  };
761
761
  function humanize(id) {
762
- const text4 = id.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
763
- return text4 ? `${text4[0].toUpperCase()}${text4.slice(1)}` : id;
762
+ const text5 = id.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
763
+ return text5 ? `${text5[0].toUpperCase()}${text5.slice(1)}` : id;
764
764
  }
765
765
  function applicationDefs(chapter) {
766
766
  try {
@@ -775,7 +775,7 @@ function applicationType(id, def) {
775
775
  }
776
776
  function applicationFlowFields(chapter, application = chapter.application) {
777
777
  const defs = applicationDefs(chapter);
778
- return [...application.required, ...application.optional].map((id, order) => {
778
+ return [...application.required, ...application.optional].map((id, order2) => {
779
779
  const def = defs[id];
780
780
  const conditions = application.conditions[id];
781
781
  const type = applicationType(id, def);
@@ -784,7 +784,7 @@ function applicationFlowFields(chapter, application = chapter.application) {
784
784
  label: def?.label ?? APPLICATION_LABELS[id] ?? humanize(id),
785
785
  type,
786
786
  required: application.required.includes(id),
787
- order,
787
+ order: order2,
788
788
  maxLength: application.maxLen[id] ?? application.defaultMaxLen,
789
789
  ...type === "enum" && def?.options ? { options: [...def.options] } : {},
790
790
  ...conditions ? { conditions: { ...conditions } } : {},
@@ -795,14 +795,14 @@ function applicationFlowFields(chapter, application = chapter.application) {
795
795
  function resolveFormationFlowFields(crm, formation) {
796
796
  if (!formation.enabled) return [];
797
797
  const def = crm.type(formation.type);
798
- return [...formation.required, ...formation.optional].map((id, order) => {
798
+ return [...formation.required, ...formation.optional].map((id, order2) => {
799
799
  const field = def.fields[id];
800
800
  return {
801
801
  id,
802
802
  label: field.label ?? humanize(id),
803
803
  type: field.type,
804
804
  required: formation.required.includes(id),
805
- order,
805
+ order: order2,
806
806
  maxLength: formation.maxLen[id] ?? formation.defaultMaxLen,
807
807
  ...field.options ? { options: [...field.options] } : {},
808
808
  ...id === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
@@ -846,9 +846,9 @@ function validateGiftOfferCatalog(input, tierIds) {
846
846
  if (rawOffers.length > MAX_GIFT_OFFERS) errors.push(`document.giftOffers.offers supports at most ${MAX_GIFT_OFFERS} entries`);
847
847
  const ids = /* @__PURE__ */ new Set();
848
848
  const offers = [];
849
- for (const [index, raw] of rawOffers.slice(0, MAX_GIFT_OFFERS).entries()) {
850
- const path = `document.giftOffers.offers[${index}]`;
851
- const item = record(raw);
849
+ for (const [index2, raw2] of rawOffers.slice(0, MAX_GIFT_OFFERS).entries()) {
850
+ const path = `document.giftOffers.offers[${index2}]`;
851
+ const item = record(raw2);
852
852
  if (!item) {
853
853
  errors.push(`${path} must be an object`);
854
854
  continue;
@@ -1005,8 +1005,8 @@ function networkSourceTag(type, hubRecordId, sourceId) {
1005
1005
  const typeKey = type.toLowerCase();
1006
1006
  const readable = /^[a-z0-9_-]+$/.test(hubRecordId);
1007
1007
  const prefix = sourceId ? `network:${sourceId.toLowerCase()}:${typeKey}` : `network:${typeKey}`;
1008
- const raw = `${prefix}:${hubRecordId}`;
1009
- if (readable && raw.length <= 64) return raw;
1008
+ const raw2 = `${prefix}:${hubRecordId}`;
1009
+ if (readable && raw2.length <= 64) return raw2;
1010
1010
  return `network:${typeKey.slice(0, 16)}:${shortHash(`${sourceId ?? ""}\0${type}\0${hubRecordId}`)}`;
1011
1011
  }
1012
1012
  function normalizeSharedRecord(record4, fallbackSourceId = "legacy-source") {
@@ -1290,10 +1290,10 @@ function planDelivery(input) {
1290
1290
  const transport = !isProd && !redirect ? "log-only" : input.cloudflareReady ? "cloudflare" : "log-only";
1291
1291
  const to = redirect ? input.group.debugEmail : input.to;
1292
1292
  const subject = (redirect ? "[dev] " : "") + render(tpl.subject, vars);
1293
- const text4 = redirect ? `(dev redirect; original recipient: ${input.to})
1293
+ const text5 = redirect ? `(dev redirect; original recipient: ${input.to})
1294
1294
 
1295
1295
  ` + render(tpl.text, vars) : render(tpl.text, vars);
1296
- return { deliver: true, transport, to, subject, text: text4, redirected: redirect };
1296
+ return { deliver: true, transport, to, subject, text: text5, redirected: redirect };
1297
1297
  }
1298
1298
 
1299
1299
  // src/notify.ts
@@ -1480,27 +1480,27 @@ async function hasSignupControl(db, targetId, environment, active) {
1480
1480
  if (db.aggregate) {
1481
1481
  return Number((await db.aggregate("signupControls", { count: true }, { where })).count ?? 0) > 0;
1482
1482
  }
1483
- const rows5 = (await db.query({ signupControls: {} })).signupControls;
1484
- return Array.isArray(rows5) && rows5.some((row) => row.targetId === targetId && row.environment === environment && (!active || row.active === true));
1483
+ const rows6 = (await db.query({ signupControls: {} })).signupControls;
1484
+ return Array.isArray(rows6) && rows6.some((row) => row.targetId === targetId && row.environment === environment && (!active || row.active === true));
1485
1485
  }
1486
1486
  async function loadLatestSignupControl(db, targetId, environment) {
1487
1487
  if (!await hasSignupControl(db, targetId, environment)) return null;
1488
- const rows5 = (await db.query({ signupControls: { $: {
1488
+ const rows6 = (await db.query({ signupControls: { $: {
1489
1489
  where: { targetId, environment },
1490
1490
  order: { revision: "desc" },
1491
1491
  limit: 1
1492
1492
  } } })).signupControls;
1493
- const latest = Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1493
+ const latest = Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1494
1494
  return asStored(latest);
1495
1495
  }
1496
1496
  async function loadActiveSignupControl(db, targetId, environment) {
1497
1497
  if (!await hasSignupControl(db, targetId, environment, true)) return null;
1498
- const rows5 = (await db.query({ signupControls: { $: {
1498
+ const rows6 = (await db.query({ signupControls: { $: {
1499
1499
  where: { targetId, environment, active: true },
1500
1500
  order: { revision: "desc" },
1501
1501
  limit: 1
1502
1502
  } } })).signupControls;
1503
- const active = Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1503
+ const active = Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1504
1504
  return asStored(active);
1505
1505
  }
1506
1506
  function controlledApplication(base, document) {
@@ -1529,7 +1529,7 @@ var byPublicId = (ns, value) => ({
1529
1529
  value
1530
1530
  });
1531
1531
  async function materializeSignupControl(db, envelope, previous, receivedAt) {
1532
- const { document, digest } = envelope;
1532
+ const { document, digest: digest2 } = envelope;
1533
1533
  const currentRows = (await db.query({ tiers: { $: {
1534
1534
  where: { groupId: document.target.chapterId },
1535
1535
  limit: 100
@@ -1580,12 +1580,12 @@ async function materializeSignupControl(db, envelope, previous, receivedAt) {
1580
1580
  targetId: document.target.chapterId,
1581
1581
  environment: document.target.environment,
1582
1582
  revision: document.revision,
1583
- digest,
1583
+ digest: digest2,
1584
1584
  document,
1585
1585
  active: true,
1586
1586
  receivedAt
1587
1587
  } });
1588
- await db.transact(ops, { mutationId: `signup-control:${document.target.environment}:${document.revision}:${digest}` });
1588
+ await db.transact(ops, { mutationId: `signup-control:${document.target.environment}:${document.revision}:${digest2}` });
1589
1589
  }
1590
1590
 
1591
1591
  // src/session.ts
@@ -2025,11 +2025,11 @@ var handleMember = async (req, url, env, ctx) => {
2025
2025
  return json({ error: `application cannot resume from status "${String(app.status ?? "")}"` }, 409);
2026
2026
  }
2027
2027
  if (req.method === "POST" && url.pathname === "/api/applications") {
2028
- const raw = await req.text();
2029
- if (raw.length > chapter.application.bodyCap) return json({ error: "request body too large" }, 413);
2028
+ const raw2 = await req.text();
2029
+ if (raw2.length > chapter.application.bodyCap) return json({ error: "request body too large" }, 413);
2030
2030
  let parsed;
2031
2031
  try {
2032
- parsed = JSON.parse(raw);
2032
+ parsed = JSON.parse(raw2);
2033
2033
  } catch {
2034
2034
  return json({ error: "invalid JSON body" }, 400);
2035
2035
  }
@@ -2086,23 +2086,23 @@ var handleNetworkShared = async (req, url, env, ctx) => {
2086
2086
  const db = ctx.makeDb(env);
2087
2087
  const secret = await getVaultSecret(db, "network_share_secret");
2088
2088
  if (!secret) return json({ error: "unauthorized" }, 401);
2089
- let raw;
2089
+ let raw2;
2090
2090
  let sourceId;
2091
2091
  if (req.headers.has(import_db2.FEDERATION_HEADERS.version)) {
2092
2092
  const verified = await (0, import_db2.verifyFederatedRequest)(req, { secret });
2093
2093
  if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
2094
- raw = verified.body;
2094
+ raw2 = verified.body;
2095
2095
  sourceId = verified.sender;
2096
2096
  } else {
2097
2097
  const provided = (req.headers.get("authorization") ?? "").replace(/^Bearer /, "");
2098
2098
  if (provided.length !== secret.length || provided !== secret) {
2099
2099
  return json({ error: "unauthorized" }, 401);
2100
2100
  }
2101
- raw = await req.text();
2101
+ raw2 = await req.text();
2102
2102
  }
2103
2103
  let payload;
2104
2104
  try {
2105
- payload = JSON.parse(raw);
2105
+ payload = JSON.parse(raw2);
2106
2106
  } catch {
2107
2107
  return json({ error: "invalid JSON body" }, 400);
2108
2108
  }
@@ -2181,10 +2181,10 @@ function boundedString(value, path, errors, max, required = false) {
2181
2181
  errors.push(`${path} must be a string`);
2182
2182
  return void 0;
2183
2183
  }
2184
- const text4 = value.trim();
2185
- if (required && !text4) errors.push(`${path} is required`);
2186
- if (text4.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
2187
- return text4;
2184
+ const text5 = value.trim();
2185
+ if (required && !text5) errors.push(`${path} is required`);
2186
+ if (text5.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
2187
+ return text5;
2188
2188
  }
2189
2189
  function validateMembershipPageDocument(input, options = {}) {
2190
2190
  const errors = [];
@@ -2202,9 +2202,9 @@ function validateMembershipPageDocument(input, options = {}) {
2202
2202
  const authority = new Map((options.tiers ?? []).map((tier) => [tier.id, tier]));
2203
2203
  const seen = /* @__PURE__ */ new Set();
2204
2204
  const tiers = [];
2205
- for (const [index, raw] of rawTiers.slice(0, MAX_TIERS).entries()) {
2206
- const path = `document.tiers[${index}]`;
2207
- const item = objectRecord(raw);
2205
+ for (const [index2, raw2] of rawTiers.slice(0, MAX_TIERS).entries()) {
2206
+ const path = `document.tiers[${index2}]`;
2207
+ const item = objectRecord(raw2);
2208
2208
  if (!item) {
2209
2209
  errors.push(`${path} must be an object`);
2210
2210
  continue;
@@ -2233,8 +2233,8 @@ function validateMembershipPageDocument(input, options = {}) {
2233
2233
  else {
2234
2234
  if (item.benefits.length > MAX_BENEFITS) errors.push(`${path}.benefits supports at most ${MAX_BENEFITS} entries`);
2235
2235
  benefits = item.benefits.slice(0, MAX_BENEFITS).flatMap((benefit, benefitIndex) => {
2236
- const text4 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
2237
- return text4 ? [text4] : [];
2236
+ const text5 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
2237
+ return text5 ? [text5] : [];
2238
2238
  });
2239
2239
  }
2240
2240
  }
@@ -2343,9 +2343,9 @@ function tiersOf(value, errors) {
2343
2343
  }
2344
2344
  if (value.length > 20) errors.push("document.tiers supports at most 20 entries");
2345
2345
  const seen = /* @__PURE__ */ new Set();
2346
- return value.slice(0, 20).flatMap((raw, index) => {
2347
- const path = `document.tiers[${index}]`;
2348
- const item = recordOf(raw);
2346
+ return value.slice(0, 20).flatMap((raw2, index2) => {
2347
+ const path = `document.tiers[${index2}]`;
2348
+ const item = recordOf(raw2);
2349
2349
  if (!item) {
2350
2350
  errors.push(`${path} must be an object`);
2351
2351
  return [];
@@ -2472,7 +2472,7 @@ function validateSignupControlDocument(input) {
2472
2472
  const available = tiers.filter((tier) => tier.available).sort((a, b) => a.sortOrder - b.sortOrder);
2473
2473
  const page = validateMembershipPageDocument(root.membershipPage, { tiers: available });
2474
2474
  if (!page.ok) errors.push(...page.errors);
2475
- else if (page.value.tiers.some((tier, index) => tier.tierId !== available[index]?.id)) {
2475
+ else if (page.value.tiers.some((tier, index2) => tier.tierId !== available[index2]?.id)) {
2476
2476
  errors.push("document.membershipPage.tiers must follow the available tier display order");
2477
2477
  }
2478
2478
  const application = applicationOf(root.application, errors);
@@ -2520,7 +2520,7 @@ var handleSignupControl = async (req, url, env, ctx) => {
2520
2520
  } catch {
2521
2521
  return reject("invalid_document", 400, ["request body must be JSON"]);
2522
2522
  }
2523
- const digest = typeof input.digest === "string" ? input.digest : "";
2523
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
2524
2524
  const checked = validateSignupControlDocument(input.document);
2525
2525
  if (!checked.ok) return reject("invalid_document", 400, checked.errors);
2526
2526
  const document = checked.value;
@@ -2530,11 +2530,11 @@ var handleSignupControl = async (req, url, env, ctx) => {
2530
2530
  if (document.target.stripeMode !== authority.stripeMode) return reject("wrong_stripe_mode", 409);
2531
2531
  const applicationErrors = validateControlledApplication(ctx.chapter, document);
2532
2532
  if (applicationErrors.length) return reject("invalid_document", 400, applicationErrors);
2533
- if (!digest || await signupControlDigest(document) !== digest) return reject("invalid_digest", 400);
2533
+ if (!digest2 || await signupControlDigest(document) !== digest2) return reject("invalid_digest", 400);
2534
2534
  const latest = await loadLatestSignupControl(db, ctx.chapter.id, env.ODLA_ENV);
2535
2535
  if (latest && document.revision < latest.revision) return reject("stale_revision", 409);
2536
2536
  if (latest && document.revision === latest.revision) {
2537
- if (latest.digest !== digest) return reject("conflicting_revision", 409);
2537
+ if (latest.digest !== digest2) return reject("conflicting_revision", 409);
2538
2538
  return json({
2539
2539
  ok: true,
2540
2540
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
@@ -2546,7 +2546,7 @@ var handleSignupControl = async (req, url, env, ctx) => {
2546
2546
  }
2547
2547
  const receivedAt = Date.now();
2548
2548
  try {
2549
- await materializeSignupControl(db, { digest, document }, latest, receivedAt);
2549
+ await materializeSignupControl(db, { digest: digest2, document }, latest, receivedAt);
2550
2550
  } catch {
2551
2551
  return reject("materialization_failed", 503);
2552
2552
  }
@@ -2554,7 +2554,7 @@ var handleSignupControl = async (req, url, env, ctx) => {
2554
2554
  ok: true,
2555
2555
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
2556
2556
  revision: document.revision,
2557
- digest,
2557
+ digest: digest2,
2558
2558
  status: "accepted",
2559
2559
  acknowledgedAt: receivedAt
2560
2560
  });
@@ -2582,8 +2582,8 @@ function canonicalMemberContent(value) {
2582
2582
  return canonicalValue2(value);
2583
2583
  }
2584
2584
  async function memberContentDigest(document) {
2585
- const digest = await crypto.subtle.digest("SHA-256", encoder2.encode(canonicalMemberContent(document)));
2586
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
2585
+ const digest2 = await crypto.subtle.digest("SHA-256", encoder2.encode(canonicalMemberContent(document)));
2586
+ return `sha256:${Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
2587
2587
  }
2588
2588
 
2589
2589
  // src/member-content-validation-helpers.ts
@@ -2620,21 +2620,21 @@ function textValue(value, path, errors, max = MAX_TEXT, required = false) {
2620
2620
  return valueText;
2621
2621
  }
2622
2622
  function offerValue(value, path, errors, tiers, audience) {
2623
- const raw = record2(value);
2624
- if (!raw) {
2623
+ const raw2 = record2(value);
2624
+ if (!raw2) {
2625
2625
  errors.push(`${path} must be an object`);
2626
2626
  return void 0;
2627
2627
  }
2628
- unknownKeys2(raw, OFFER_KEYS, path, errors);
2629
- const tierId = textValue(raw.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2628
+ unknownKeys2(raw2, OFFER_KEYS, path, errors);
2629
+ const tierId = textValue(raw2.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2630
2630
  if (tierId && !ID3.test(tierId)) errors.push(`${path}.tierId must be a lowercase kebab-case id`);
2631
2631
  if (tiers.size && !tiers.has(tierId)) errors.push(`${path}.tierId must reference a declared tier`);
2632
2632
  if (audience?.kind === "tiers" && tierId && !audience.tierIds.includes(tierId)) {
2633
2633
  errors.push(`${path}.tierId must grant access to this page`);
2634
2634
  }
2635
- const label = textValue(raw.label, `${path}.label`, errors);
2636
- const bodyMarkdown = textValue(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2);
2637
- const ctaLabel = textValue(raw.ctaLabel, `${path}.ctaLabel`, errors);
2635
+ const label = textValue(raw2.label, `${path}.label`, errors);
2636
+ const bodyMarkdown = textValue(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2);
2637
+ const ctaLabel = textValue(raw2.ctaLabel, `${path}.ctaLabel`, errors);
2638
2638
  return {
2639
2639
  tierId,
2640
2640
  ...label ? { label } : {},
@@ -2686,37 +2686,37 @@ function validateMemberContentDocument(input, options = {}) {
2686
2686
  const ids = /* @__PURE__ */ new Set();
2687
2687
  const slugs = /* @__PURE__ */ new Set();
2688
2688
  const pages = [];
2689
- for (const [index, value] of rawPages.slice(0, MAX_PAGES).entries()) {
2690
- const path = `document.pages[${index}]`;
2691
- const raw = record2(value);
2692
- if (!raw) {
2689
+ for (const [index2, value] of rawPages.slice(0, MAX_PAGES).entries()) {
2690
+ const path = `document.pages[${index2}]`;
2691
+ const raw2 = record2(value);
2692
+ if (!raw2) {
2693
2693
  errors.push(`${path} must be an object`);
2694
2694
  continue;
2695
2695
  }
2696
- unknownKeys2(raw, PAGE_KEYS, path, errors);
2697
- const id = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2698
- const slug = textValue(raw.slug, `${path}.slug`, errors, 120, true) ?? "";
2699
- const title = textValue(raw.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
2696
+ unknownKeys2(raw2, PAGE_KEYS, path, errors);
2697
+ const id = textValue(raw2.id, `${path}.id`, errors, 80, true) ?? "";
2698
+ const slug = textValue(raw2.slug, `${path}.slug`, errors, 120, true) ?? "";
2699
+ const title = textValue(raw2.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
2700
2700
  if (id && !ID3.test(id)) errors.push(`${path}.id must be a lowercase kebab-case id`);
2701
2701
  if (slug && !SLUG2.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
2702
2702
  if (ids.has(id) || knownParentIds.has(id)) errors.push(`${path}.id duplicates page "${id}"`);
2703
2703
  if (slugs.has(slug) || (options.parentPages ?? []).some((page) => page.slug === slug)) errors.push(`${path}.slug duplicates "${slug}"`);
2704
2704
  ids.add(id);
2705
2705
  slugs.add(slug);
2706
- const navRaw = record2(raw.navigation);
2707
- let navigation = { section: "General", order: index };
2706
+ const navRaw = record2(raw2.navigation);
2707
+ let navigation = { section: "General", order: index2 };
2708
2708
  if (!navRaw) errors.push(`${path}.navigation must be an object`);
2709
2709
  else {
2710
2710
  unknownKeys2(navRaw, NAV_KEYS, `${path}.navigation`, errors);
2711
2711
  const section = textValue(navRaw.section, `${path}.navigation.section`, errors, 120, true) ?? "";
2712
- const order = navRaw.order;
2713
- if (!Number.isInteger(order) || Number(order) < 0 || Number(order) > 1e6) {
2712
+ const order2 = navRaw.order;
2713
+ if (!Number.isInteger(order2) || Number(order2) < 0 || Number(order2) > 1e6) {
2714
2714
  errors.push(`${path}.navigation.order must be an integer from 0 to 1,000,000`);
2715
2715
  }
2716
2716
  const parentId = textValue(navRaw.parentId, `${path}.navigation.parentId`, errors, 80);
2717
- navigation = { section, order: Number.isInteger(order) ? Number(order) : index, ...parentId ? { parentId } : {} };
2717
+ navigation = { section, order: Number.isInteger(order2) ? Number(order2) : index2, ...parentId ? { parentId } : {} };
2718
2718
  }
2719
- const audienceRaw = record2(raw.audience);
2719
+ const audienceRaw = record2(raw2.audience);
2720
2720
  let audience = { kind: "all-members" };
2721
2721
  if (!audienceRaw) errors.push(`${path}.audience must be an object`);
2722
2722
  else {
@@ -2738,12 +2738,12 @@ function validateMemberContentDocument(input, options = {}) {
2738
2738
  audience = { kind: "tiers", tierIds: normalized };
2739
2739
  } else errors.push(`${path}.audience.kind must be all-members or tiers`);
2740
2740
  }
2741
- const summary = textValue(raw.summary, `${path}.summary`, errors, 2e3);
2742
- const teaserMarkdown = textValue(raw.teaserMarkdown, `${path}.teaserMarkdown`, errors, MAX_MARKDOWN2);
2743
- const bodyMarkdown = textValue(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, true) ?? "";
2741
+ const summary = textValue(raw2.summary, `${path}.summary`, errors, 2e3);
2742
+ const teaserMarkdown = textValue(raw2.teaserMarkdown, `${path}.teaserMarkdown`, errors, MAX_MARKDOWN2);
2743
+ const bodyMarkdown = textValue(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, true) ?? "";
2744
2744
  let upsell;
2745
- if (raw.upsell !== void 0) {
2746
- const upsellRaw = record2(raw.upsell);
2745
+ if (raw2.upsell !== void 0) {
2746
+ const upsellRaw = record2(raw2.upsell);
2747
2747
  if (!upsellRaw) errors.push(`${path}.upsell must be an object`);
2748
2748
  else {
2749
2749
  unknownKeys2(upsellRaw, UPSELL_KEYS, `${path}.upsell`, errors);
@@ -2760,10 +2760,10 @@ function validateMemberContentDocument(input, options = {}) {
2760
2760
  pages.push({ id, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2761
2761
  }
2762
2762
  const allIds = /* @__PURE__ */ new Set([...knownParentIds, ...ids]);
2763
- for (const [index, page] of pages.entries()) {
2763
+ for (const [index2, page] of pages.entries()) {
2764
2764
  const parentId = page.navigation.parentId;
2765
- if (parentId && !allIds.has(parentId)) errors.push(`document.pages[${index}].navigation.parentId references unknown page "${parentId}"`);
2766
- if (parentId === page.id) errors.push(`document.pages[${index}].navigation.parentId cannot reference itself`);
2765
+ if (parentId && !allIds.has(parentId)) errors.push(`document.pages[${index2}].navigation.parentId references unknown page "${parentId}"`);
2766
+ if (parentId === page.id) errors.push(`document.pages[${index2}].navigation.parentId cannot reference itself`);
2767
2767
  }
2768
2768
  for (const page of pages) {
2769
2769
  const seen = /* @__PURE__ */ new Set([page.id]);
@@ -2784,21 +2784,21 @@ function validateMemberContentDocument(input, options = {}) {
2784
2784
  const values = Array.isArray(root.upsellOverrides) ? root.upsellOverrides : [];
2785
2785
  const seen = /* @__PURE__ */ new Set();
2786
2786
  upsellOverrides = [];
2787
- for (const [index, value] of values.entries()) {
2788
- const path = `document.upsellOverrides[${index}]`;
2789
- const raw = record2(value);
2790
- if (!raw) {
2787
+ for (const [index2, value] of values.entries()) {
2788
+ const path = `document.upsellOverrides[${index2}]`;
2789
+ const raw2 = record2(value);
2790
+ if (!raw2) {
2791
2791
  errors.push(`${path} must be an object`);
2792
2792
  continue;
2793
2793
  }
2794
- unknownKeys2(raw, OVERRIDE_KEYS, path, errors);
2795
- const pageId = textValue(raw.pageId, `${path}.pageId`, errors, 80, true) ?? "";
2794
+ unknownKeys2(raw2, OVERRIDE_KEYS, path, errors);
2795
+ const pageId = textValue(raw2.pageId, `${path}.pageId`, errors, 80, true) ?? "";
2796
2796
  if (seen.has(pageId)) errors.push(`${path}.pageId duplicates "${pageId}"`);
2797
2797
  seen.add(pageId);
2798
2798
  const parentPage = (options.parentPages ?? []).find((page) => page.id === pageId);
2799
2799
  if (!parentPage) errors.push(`${path}.pageId must reference a parent page`);
2800
2800
  else if (parentPage.upsell?.owner !== "chapter") errors.push(`${path}.pageId does not delegate upsell authority to the chapter`);
2801
- const offer = offerValue(raw.offer, `${path}.offer`, errors, tierIds, parentPage?.audience);
2801
+ const offer = offerValue(raw2.offer, `${path}.offer`, errors, tierIds, parentPage?.audience);
2802
2802
  if (offer) upsellOverrides.push({ pageId, offer });
2803
2803
  }
2804
2804
  }
@@ -2876,36 +2876,36 @@ var control = (row) => row && row.document && typeof row.document === "object" ?
2876
2876
  var local = (row) => row && row.document && typeof row.document === "object" ? row : null;
2877
2877
  async function exists(db, ns, where) {
2878
2878
  if (db.aggregate) return Number((await db.aggregate(ns, { count: true }, { where })).count ?? 0) > 0;
2879
- const rows5 = (await db.query({ [ns]: {} }))[ns] ?? [];
2880
- return rows5.some((row) => Object.entries(where).every(([key, value]) => row[key] === value));
2879
+ const rows6 = (await db.query({ [ns]: {} }))[ns] ?? [];
2880
+ return rows6.some((row) => Object.entries(where).every(([key, value]) => row[key] === value));
2881
2881
  }
2882
2882
  async function loadActiveMemberContentControl(db, targetId, environment) {
2883
2883
  const where = { targetId, environment, active: true };
2884
2884
  if (!await exists(db, "memberContentControls", where)) return null;
2885
- const rows5 = (await db.query({ memberContentControls: { $: {
2885
+ const rows6 = (await db.query({ memberContentControls: { $: {
2886
2886
  where,
2887
2887
  order: { revision: "desc" },
2888
2888
  limit: 1
2889
2889
  } } })).memberContentControls;
2890
- return control(Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2890
+ return control(Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2891
2891
  }
2892
2892
  async function loadLatestMemberContentControl(db, targetId, environment) {
2893
2893
  const where = { targetId, environment };
2894
2894
  if (!await exists(db, "memberContentControls", where)) return null;
2895
- const rows5 = (await db.query({ memberContentControls: { $: {
2895
+ const rows6 = (await db.query({ memberContentControls: { $: {
2896
2896
  where,
2897
2897
  order: { revision: "desc" },
2898
2898
  limit: 1
2899
2899
  } } })).memberContentControls;
2900
- return control(Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2900
+ return control(Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2901
2901
  }
2902
2902
  async function loadLocalMemberContent(db, chapterId, environment) {
2903
2903
  const id = `member-content-local:${environment}`;
2904
- const rows5 = (await db.query({ memberContentLocal: { $: { where: { id }, limit: 1 } } })).memberContentLocal;
2905
- return local(Array.isArray(rows5) ? rows5[0] : void 0);
2904
+ const rows6 = (await db.query({ memberContentLocal: { $: { where: { id }, limit: 1 } } })).memberContentLocal;
2905
+ return local(Array.isArray(rows6) ? rows6[0] : void 0);
2906
2906
  }
2907
2907
  async function materializeMemberContentControl(db, envelope, previous, receivedAt) {
2908
- const { document, digest } = envelope;
2908
+ const { document, digest: digest2 } = envelope;
2909
2909
  const target = document.target;
2910
2910
  const id = `member-content-control:${target.environment}:${document.revision}`;
2911
2911
  const ops = [];
@@ -2918,12 +2918,12 @@ async function materializeMemberContentControl(db, envelope, previous, receivedA
2918
2918
  targetId: target.chapterId,
2919
2919
  environment: target.environment,
2920
2920
  revision: document.revision,
2921
- digest,
2921
+ digest: digest2,
2922
2922
  document,
2923
2923
  active: true,
2924
2924
  receivedAt
2925
2925
  } });
2926
- await db.transact(ops, { mutationId: `member-content-control:${target.environment}:${document.revision}:${digest}` });
2926
+ await db.transact(ops, { mutationId: `member-content-control:${target.environment}:${document.revision}:${digest2}` });
2927
2927
  }
2928
2928
  async function saveLocalMemberContent(db, document, environment, expectedRevision, updatedAt, updatedBy) {
2929
2929
  const id = `member-content-local:${environment}`;
@@ -2957,12 +2957,12 @@ async function groupAndTiers(db, chapterId) {
2957
2957
  }
2958
2958
  async function memberTierId(db, chapterId, email) {
2959
2959
  if (!email) return null;
2960
- let rows5 = (await db.query({ applications: { $: {
2960
+ let rows6 = (await db.query({ applications: { $: {
2961
2961
  where: { emailNormalized: normalizeAdmissionEmail(email) },
2962
2962
  limit: 20
2963
2963
  } } })).applications;
2964
- if (!rows5?.length) rows5 = (await db.query({ applications: { $: { where: { email }, limit: 20 } } })).applications;
2965
- const latest = Array.isArray(rows5) ? [...rows5].filter((row) => String(row.groupId ?? chapterId) === chapterId).sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0] : void 0;
2964
+ if (!rows6?.length) rows6 = (await db.query({ applications: { $: { where: { email }, limit: 20 } } })).applications;
2965
+ const latest = Array.isArray(rows6) ? [...rows6].filter((row) => String(row.groupId ?? chapterId) === chapterId).sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0] : void 0;
2966
2966
  return typeof latest?.tierId === "string" && latest.tierId ? latest.tierId : null;
2967
2967
  }
2968
2968
  async function activeDocuments(db, chapterId, environment) {
@@ -2987,7 +2987,7 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
2987
2987
  } catch {
2988
2988
  return reject2("invalid_document", 400, ["request body must be JSON"]);
2989
2989
  }
2990
- const digest = typeof input.digest === "string" ? input.digest : "";
2990
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
2991
2991
  const initial = validateMemberContentDocument(input.document, {
2992
2992
  sourceType: "parent"
2993
2993
  });
@@ -3015,11 +3015,11 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
3015
3015
  return reject2("invalid_document", 409, localCheck.errors.map((error) => `existing chapter content: ${error}`));
3016
3016
  }
3017
3017
  }
3018
- if (!digest || await memberContentDigest(document) !== digest) return reject2("invalid_digest", 400);
3018
+ if (!digest2 || await memberContentDigest(document) !== digest2) return reject2("invalid_digest", 400);
3019
3019
  const latest = await loadLatestMemberContentControl(db, ctx.chapter.id, env.ODLA_ENV);
3020
3020
  if (latest && document.revision < latest.revision) return reject2("stale_revision", 409);
3021
3021
  if (latest && document.revision === latest.revision) {
3022
- if (latest.digest !== digest) return reject2("conflicting_revision", 409);
3022
+ if (latest.digest !== digest2) return reject2("conflicting_revision", 409);
3023
3023
  return json({
3024
3024
  ok: true,
3025
3025
  target: document.target,
@@ -3031,7 +3031,7 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
3031
3031
  }
3032
3032
  const receivedAt = Date.now();
3033
3033
  try {
3034
- await materializeMemberContentControl(db, { digest, document }, latest, receivedAt);
3034
+ await materializeMemberContentControl(db, { digest: digest2, document }, latest, receivedAt);
3035
3035
  } catch {
3036
3036
  return reject2("materialization_failed", 503);
3037
3037
  }
@@ -3039,7 +3039,7 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
3039
3039
  ok: true,
3040
3040
  target: document.target,
3041
3041
  revision: document.revision,
3042
- digest,
3042
+ digest: digest2,
3043
3043
  status: "accepted",
3044
3044
  acknowledgedAt: receivedAt
3045
3045
  });
@@ -3091,11 +3091,11 @@ var handleAdminMemberContent = async (req, url, env, ctx) => {
3091
3091
  }
3092
3092
  if (req.method !== "PUT") return json({ error: "method not allowed" }, 405);
3093
3093
  if (!ctx.chapter.memberContent.localAuthoring) return json({ error: "local authoring is disabled" }, 403);
3094
- const raw = await req.text();
3095
- if (raw.length > BODY_CAP) return json({ error: "request body too large" }, 413);
3094
+ const raw2 = await req.text();
3095
+ if (raw2.length > BODY_CAP) return json({ error: "request body too large" }, 413);
3096
3096
  let input;
3097
3097
  try {
3098
- input = JSON.parse(raw);
3098
+ input = JSON.parse(raw2);
3099
3099
  } catch {
3100
3100
  return json({ error: "invalid JSON body" }, 400);
3101
3101
  }
@@ -3163,18 +3163,18 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3163
3163
  } catch {
3164
3164
  return reject3("invalid_document", 400, ["request body must be JSON"]);
3165
3165
  }
3166
- const digest = typeof input.digest === "string" ? input.digest : "";
3166
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
3167
3167
  const checked = validateAdmissionGrantDocument(input.document);
3168
3168
  if (!checked.ok) return reject3("invalid_document", 400, checked.errors);
3169
3169
  const document = checked.value;
3170
3170
  if (document.source.appId !== claimedSender) return reject3("wrong_source", 403);
3171
3171
  if (document.target.chapterId !== ctx.chapter.id) return reject3("wrong_target", 409);
3172
3172
  if (document.target.environment !== env.ODLA_ENV) return reject3("wrong_environment", 409);
3173
- if (!digest || await signupControlDigest(document) !== digest) return reject3("invalid_digest", 400);
3173
+ if (!digest2 || await signupControlDigest(document) !== digest2) return reject3("invalid_digest", 400);
3174
3174
  const latest = await loadLatestAdmissionRevision(db, claimedSender, ctx.chapter.id, env.ODLA_ENV);
3175
3175
  if (latest && document.revision < latest.revision) return reject3("stale_revision", 409);
3176
3176
  if (latest && document.revision === latest.revision) {
3177
- if (latest.digest !== digest) return reject3("conflicting_revision", 409);
3177
+ if (latest.digest !== digest2) return reject3("conflicting_revision", 409);
3178
3178
  return json({
3179
3179
  ok: true,
3180
3180
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
@@ -3186,7 +3186,7 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3186
3186
  }
3187
3187
  const receivedAt = Date.now();
3188
3188
  try {
3189
- await materializeAdmissionGrants(db, { digest, document }, latest, receivedAt);
3189
+ await materializeAdmissionGrants(db, { digest: digest2, document }, latest, receivedAt);
3190
3190
  } catch {
3191
3191
  return reject3("materialization_failed", 503);
3192
3192
  }
@@ -3194,7 +3194,7 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3194
3194
  ok: true,
3195
3195
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
3196
3196
  revision: document.revision,
3197
- digest,
3197
+ digest: digest2,
3198
3198
  status: "accepted",
3199
3199
  acknowledgedAt: receivedAt
3200
3200
  });
@@ -3397,9 +3397,9 @@ function personAdmissionEmail(ctx, record4) {
3397
3397
  if (record4.type !== "person") return null;
3398
3398
  const field = ctx.chapter.crm.config.types.person?.emailField;
3399
3399
  if (!field) return null;
3400
- const raw = record4.fields[field] ?? record4.primaryEmail;
3401
- if (typeof raw !== "string") return null;
3402
- const normalized = normalizeAdmissionEmail(raw);
3400
+ const raw2 = record4.fields[field] ?? record4.primaryEmail;
3401
+ if (typeof raw2 !== "string") return null;
3402
+ const normalized = normalizeAdmissionEmail(raw2);
3403
3403
  return isValidEmail(normalized) ? normalized : null;
3404
3404
  }
3405
3405
  async function grantInput(db, ctx, assignment) {
@@ -3419,7 +3419,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3419
3419
  const current = await loadLeaderAdmissionAssignments(opts.db, opts.target.id);
3420
3420
  const id = assignmentId(opts.target.id, opts.personId);
3421
3421
  const existing = current.find((row) => row.id === id);
3422
- const digest = await signupControlDigest({
3422
+ const digest2 = await signupControlDigest({
3423
3423
  targetId: opts.target.id,
3424
3424
  personId: opts.personId,
3425
3425
  decision: opts.decision,
@@ -3434,7 +3434,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3434
3434
  } } });
3435
3435
  const receipt = admissionRows(priorMutation2.leaderAdmissionMutations)[0];
3436
3436
  if (receipt) {
3437
- if (receipt.requestDigest !== digest || receipt.personId !== opts.personId) {
3437
+ if (receipt.requestDigest !== digest2 || receipt.personId !== opts.personId) {
3438
3438
  throw new Error("mutation_conflict");
3439
3439
  }
3440
3440
  const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
@@ -3442,7 +3442,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3442
3442
  return { delivery: saved, duplicate: true };
3443
3443
  }
3444
3444
  if (existing?.lastMutationId === opts.mutationId) {
3445
- if (existing.lastRequestDigest !== digest) throw new Error("mutation_conflict");
3445
+ if (existing.lastRequestDigest !== digest2) throw new Error("mutation_conflict");
3446
3446
  const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3447
3447
  if (!saved) throw new Error("delivery_missing");
3448
3448
  return { delivery: saved, duplicate: true };
@@ -3457,7 +3457,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3457
3457
  ...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
3458
3458
  active: opts.active,
3459
3459
  lastMutationId: opts.mutationId,
3460
- lastRequestDigest: digest,
3460
+ lastRequestDigest: digest2,
3461
3461
  createdAt: existing?.createdAt ?? now,
3462
3462
  createdBy: existing?.createdBy ?? opts.actor,
3463
3463
  updatedAt: now,
@@ -3493,7 +3493,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3493
3493
  id: mutationReceiptId,
3494
3494
  targetId: opts.target.id,
3495
3495
  personId: opts.personId,
3496
- requestDigest: digest,
3496
+ requestDigest: digest2,
3497
3497
  revision,
3498
3498
  createdAt: now
3499
3499
  } }
@@ -3688,8 +3688,8 @@ function makeCalendar(env) {
3688
3688
  }
3689
3689
  async function firstRow(db, ns, q) {
3690
3690
  const res = await db.query({ [ns]: { $: q } });
3691
- const rows5 = res[ns];
3692
- return Array.isArray(rows5) ? rows5[0] : void 0;
3691
+ const rows6 = res[ns];
3692
+ return Array.isArray(rows6) ? rows6[0] : void 0;
3693
3693
  }
3694
3694
  async function computeSlots(cal, cfg) {
3695
3695
  const { from, to } = slotWindow(Date.now(), cfg.windowDays);
@@ -3946,17 +3946,17 @@ async function notifyAdminOfPayment(db, env, eventId, app) {
3946
3946
  try {
3947
3947
  const group = await groupFor(db, app);
3948
3948
  if (!group || typeof group.notificationEmail !== "string" || !group.notificationEmail) return;
3949
- const text4 = (value) => typeof value === "string" ? value : "";
3949
+ const text5 = (value) => typeof value === "string" ? value : "";
3950
3950
  await sendTemplated(deps(db, env), {
3951
3951
  group: emailGroupFrom(group),
3952
3952
  template: "adminNotification",
3953
3953
  to: group.notificationEmail,
3954
3954
  vars: {
3955
- firstName: text4(app.firstName),
3956
- lastName: text4(app.lastName),
3957
- email: text4(app.email),
3958
- phone: text4(app.phone),
3959
- state: text4(app.state)
3955
+ firstName: text5(app.firstName),
3956
+ lastName: text5(app.lastName),
3957
+ email: text5(app.email),
3958
+ phone: text5(app.phone),
3959
+ state: text5(app.state)
3960
3960
  },
3961
3961
  dedupeKey: `${eventId}:admin`,
3962
3962
  applicationId: String(app.id)
@@ -4089,8 +4089,8 @@ function createStripeProvider(config) {
4089
4089
  const sk = requireSecret(secretKey);
4090
4090
  const charges = await stripeCall(sk, "GET", "/v1/charges", { customer: input.customerId, limit: 100 });
4091
4091
  if (!charges.ok) fail("charges list", charges);
4092
- const rows5 = charges.body.data ?? [];
4093
- const paid = rows5.filter((c) => c.status === "succeeded" && c.refunded !== true);
4092
+ const rows6 = charges.body.data ?? [];
4093
+ const paid = rows6.filter((c) => c.status === "succeeded" && c.refunded !== true);
4094
4094
  const charge = paid[paid.length - 1];
4095
4095
  if (!charge) {
4096
4096
  const err = new Error("no paid charge to refund");
@@ -4198,8 +4198,8 @@ async function issueGiftClaim(db, intent, provider, now) {
4198
4198
  throw new Error("gift issuance conflict");
4199
4199
  }
4200
4200
  }
4201
- function buyerGiftClaims(rows5, now) {
4202
- return rows5.map((row) => {
4201
+ function buyerGiftClaims(rows6, now) {
4202
+ return rows6.map((row) => {
4203
4203
  const stored = String(row.status ?? "pending");
4204
4204
  const status = stored === "pending" && Number(row.codeExpiresAt) <= now ? "expired" : stored;
4205
4205
  return {
@@ -4491,8 +4491,8 @@ function classifyMemberStripeEvent(identity, target, hasProvenLegacyLocalLink) {
4491
4491
 
4492
4492
  // src/stripe-webhook-partition.ts
4493
4493
  var firstRow2 = async (db, ns, q) => {
4494
- const rows5 = (await db.query({ [ns]: { $: q } }))[ns];
4495
- return Array.isArray(rows5) ? rows5[0] : void 0;
4494
+ const rows6 = (await db.query({ [ns]: { $: q } }))[ns];
4495
+ return Array.isArray(rows6) ? rows6[0] : void 0;
4496
4496
  };
4497
4497
  var stripeEventDigest = async (rawBody) => {
4498
4498
  const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(rawBody));
@@ -4539,9 +4539,9 @@ async function storeStripeReceipt(db, input) {
4539
4539
  const raced = await receiptFor(db, input.eventId);
4540
4540
  return raced?.digest === input.digest ? "replayed" : "collision";
4541
4541
  }
4542
- async function quarantineStripeEvent(db, eventId, digest, event) {
4542
+ async function quarantineStripeEvent(db, eventId, digest2, event) {
4543
4543
  const disposition = "quarantine_unmatched";
4544
- const stored = await storeStripeReceipt(db, { eventId, digest, event, disposition, matched: false });
4544
+ const stored = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition, matched: false });
4545
4545
  if (stored === "collision") {
4546
4546
  reportStripePartition("error", eventId, event, "quarantine_collision");
4547
4547
  return json({ ok: true, matched: false, collision: true });
@@ -4549,10 +4549,10 @@ async function quarantineStripeEvent(db, eventId, digest, event) {
4549
4549
  reportStripePartition("warn", eventId, event, disposition);
4550
4550
  return json({ ok: true, matched: false, disposition, ...stored === "replayed" ? { replayed: true } : {} });
4551
4551
  }
4552
- async function replayedStripeReceipt(db, eventId, digest, event) {
4552
+ async function replayedStripeReceipt(db, eventId, digest2, event) {
4553
4553
  const existing = await receiptFor(db, eventId);
4554
4554
  if (!existing) return null;
4555
- if (existing.digest !== digest) {
4555
+ if (existing.digest !== digest2) {
4556
4556
  reportStripePartition("error", eventId, event, "quarantine_collision");
4557
4557
  return json({ ok: true, matched: false, collision: true });
4558
4558
  }
@@ -4562,8 +4562,8 @@ async function replayedStripeReceipt(db, eventId, digest, event) {
4562
4562
  // src/worker-routes-payments.ts
4563
4563
  async function firstRow3(db, ns, q) {
4564
4564
  const res = await db.query({ [ns]: { $: q } });
4565
- const rows5 = res[ns];
4566
- return Array.isArray(rows5) ? rows5[0] : void 0;
4565
+ const rows6 = res[ns];
4566
+ return Array.isArray(rows6) ? rows6[0] : void 0;
4567
4567
  }
4568
4568
  async function findApplication(db, event) {
4569
4569
  if ("applicationId" in event && event.applicationId) {
@@ -4687,7 +4687,7 @@ async function ingestWebhook(req, env, ctx) {
4687
4687
  if (!ingest.ok) return json({ error: "invalid signature" }, 400);
4688
4688
  const { eventId, event } = ingest;
4689
4689
  if (event.kind === "ignored") return json({ ok: true, ignored: event.type });
4690
- const digest = await stripeEventDigest(rawBody);
4690
+ const digest2 = await stripeEventDigest(rawBody);
4691
4691
  const identity = stripeEventIdentity(event);
4692
4692
  const target = stripeEventTarget(env, ctx);
4693
4693
  const preliminary = classifyMemberStripeEvent(identity, target, false);
@@ -4701,23 +4701,23 @@ async function ingestWebhook(req, env, ctx) {
4701
4701
  reportStripePartition("warn", eventId, event, "ignore_sibling");
4702
4702
  return json({ ok: true, matched: false, disposition: "ignore_sibling" });
4703
4703
  }
4704
- const replay2 = await replayedStripeReceipt(db, eventId, digest, event);
4704
+ const replay2 = await replayedStripeReceipt(db, eventId, digest2, event);
4705
4705
  if (replay2) return replay2;
4706
4706
  const localIntent = Boolean(intent && intent.chapterId === ctx.chapter.id && intent.environment === env.ODLA_ENV && (typeof intent.runtime !== "string" || intent.runtime === target.runtime));
4707
4707
  const disposition2 = classifyMemberStripeEvent(identity, target, localIntent);
4708
4708
  if (!localIntent || disposition2 === "quarantine_unmatched") {
4709
- return quarantineStripeEvent(db, eventId, digest, event);
4709
+ return quarantineStripeEvent(db, eventId, digest2, event);
4710
4710
  }
4711
4711
  if (event.kind === "gift_paid") {
4712
4712
  if (intent.providerObjectId !== event.providerObjectId || intent.status === "refunded" || intent.status === "canceled") {
4713
- return quarantineStripeEvent(db, eventId, digest, event);
4713
+ return quarantineStripeEvent(db, eventId, digest2, event);
4714
4714
  }
4715
4715
  try {
4716
4716
  const issued = await issueGiftClaim(db, intent, {
4717
4717
  eventId,
4718
4718
  objectId: event.providerObjectId
4719
4719
  }, Date.now());
4720
- const stored3 = await storeStripeReceipt(db, { eventId, digest, event, disposition: disposition2, matched: true });
4720
+ const stored3 = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition: disposition2, matched: true });
4721
4721
  if (stored3 === "collision") {
4722
4722
  reportStripePartition("error", eventId, event, "quarantine_collision");
4723
4723
  return json({ ok: true, matched: false, collision: true });
@@ -4733,7 +4733,7 @@ async function ingestWebhook(req, env, ctx) {
4733
4733
  } }], { mutationId: `gift-revoked-intent:${eventId}` });
4734
4734
  const claim = await firstRow3(db, "giftClaims", { where: { intentId: event.giftIntentId }, limit: 1 });
4735
4735
  if (claim) await revokeGiftClaim(db, claim, event.reason, Date.now());
4736
- const stored2 = await storeStripeReceipt(db, { eventId, digest, event, disposition: disposition2, matched: Boolean(claim) });
4736
+ const stored2 = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition: disposition2, matched: Boolean(claim) });
4737
4737
  if (stored2 === "collision") {
4738
4738
  reportStripePartition("error", eventId, event, "quarantine_collision");
4739
4739
  return json({ ok: true, matched: false, collision: true });
@@ -4745,12 +4745,12 @@ async function ingestWebhook(req, env, ctx) {
4745
4745
  reportStripePartition("warn", eventId, event, "ignore_sibling");
4746
4746
  return json({ ok: true, matched: false, disposition: "ignore_sibling" });
4747
4747
  }
4748
- const replay = await replayedStripeReceipt(db, eventId, digest, event);
4748
+ const replay = await replayedStripeReceipt(db, eventId, digest2, event);
4749
4749
  if (replay) return replay;
4750
4750
  const localApplication = Boolean(app && (typeof app.stripeRuntime !== "string" || app.stripeRuntime === target.runtime));
4751
4751
  const disposition = classifyMemberStripeEvent(identity, target, localApplication);
4752
4752
  if (!localApplication || disposition === "quarantine_unmatched") {
4753
- return quarantineStripeEvent(db, eventId, digest, event);
4753
+ return quarantineStripeEvent(db, eventId, digest2, event);
4754
4754
  }
4755
4755
  const patch = webhookPatch(event, String(app.status ?? ""));
4756
4756
  let next = app;
@@ -4773,7 +4773,7 @@ async function ingestWebhook(req, env, ctx) {
4773
4773
  });
4774
4774
  }
4775
4775
  }
4776
- const stored = await storeStripeReceipt(db, { eventId, digest, event, disposition, matched: true });
4776
+ const stored = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition, matched: true });
4777
4777
  if (stored === "collision") {
4778
4778
  reportStripePartition("error", eventId, event, "quarantine_collision");
4779
4779
  return json({ ok: true, matched: false, collision: true });
@@ -5029,8 +5029,8 @@ async function upcomingEvents(env) {
5029
5029
  const res = await cal.availability.upcoming();
5030
5030
  return res.events.map((e) => ({ eventId: e.eventId, status: e.status, startAt: e.startAt, endAt: e.endAt }));
5031
5031
  }
5032
- function toReconcile(rows5) {
5033
- return rows5.map((m) => ({
5032
+ function toReconcile(rows6) {
5033
+ return rows6.map((m) => ({
5034
5034
  id: String(m.id),
5035
5035
  applicationId: String(m.applicationId),
5036
5036
  googleEventId: typeof m.googleEventId === "string" ? m.googleEventId : null,
@@ -5052,12 +5052,12 @@ var handleAdminMeetings = async (req, url, env, ctx) => {
5052
5052
  const query = await db.query({
5053
5053
  meetings: { $: { where: all ? {} : { status: "scheduled" }, order: { startAt: "asc" }, limit: 500 } }
5054
5054
  });
5055
- let rows5 = Array.isArray(query.meetings) ? query.meetings : [];
5056
- if (Number.isFinite(from)) rows5 = rows5.filter((m) => Number(m.startAt ?? 0) >= from);
5057
- if (Number.isFinite(to)) rows5 = rows5.filter((m) => Number(m.startAt ?? 0) <= to);
5055
+ let rows6 = Array.isArray(query.meetings) ? query.meetings : [];
5056
+ if (Number.isFinite(from)) rows6 = rows6.filter((m) => Number(m.startAt ?? 0) >= from);
5057
+ if (Number.isFinite(to)) rows6 = rows6.filter((m) => Number(m.startAt ?? 0) <= to);
5058
5058
  let decisions = [];
5059
5059
  try {
5060
- decisions = reconcileMeetings(toReconcile(rows5), await upcomingEvents(env), Date.now());
5060
+ decisions = reconcileMeetings(toReconcile(rows6), await upcomingEvents(env), Date.now());
5061
5061
  } catch {
5062
5062
  }
5063
5063
  const patched = /* @__PURE__ */ new Map();
@@ -5084,7 +5084,7 @@ var handleAdminMeetings = async (req, url, env, ctx) => {
5084
5084
  };
5085
5085
  const group = (await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups?.[0];
5086
5086
  const timezone = resolveScheduling(group?.schedulingJson).timezone;
5087
- const meetings = rows5.map((m) => ({ ...m, ...patched.get(String(m.id)) ?? {}, applicant: applicantOf(m) })).filter((m) => all || m.status === "scheduled");
5087
+ const meetings = rows6.map((m) => ({ ...m, ...patched.get(String(m.id)) ?? {}, applicant: applicantOf(m) })).filter((m) => all || m.status === "scheduled");
5088
5088
  return json({ meetings, adopted: decisions.length, timezone });
5089
5089
  };
5090
5090
 
@@ -5105,16 +5105,16 @@ var crmDeps2 = (db, ctx) => ({
5105
5105
  });
5106
5106
  var handleAdminCrmSync = async (req, url, env, ctx) => {
5107
5107
  if (req.method !== "POST" || url.pathname !== "/api/admin/crm/sync") return null;
5108
- const gate11 = await adminGate(req, env, ctx);
5109
- if (gate11 instanceof Response) return gate11;
5110
- const result = await backfillCrm(crmDeps2(gate11.db, ctx));
5108
+ const gate12 = await adminGate(req, env, ctx);
5109
+ if (gate12 instanceof Response) return gate12;
5110
+ const result = await backfillCrm(crmDeps2(gate12.db, ctx));
5111
5111
  return json({ ok: true, ...result });
5112
5112
  };
5113
5113
  var handleAdminPeople = async (req, url, env, ctx) => {
5114
5114
  if (req.method !== "GET" || url.pathname !== "/api/admin/people") return null;
5115
- const gate11 = await adminGate(req, env, ctx);
5116
- if (gate11 instanceof Response) return gate11;
5117
- const { db } = gate11;
5115
+ const gate12 = await adminGate(req, env, ctx);
5116
+ if (gate12 instanceof Response) return gate12;
5117
+ const { db } = gate12;
5118
5118
  const sk = await getVaultSecret(db, "clerk_secret_key");
5119
5119
  const [data, roleList] = await Promise.all([
5120
5120
  db.query({
@@ -5160,16 +5160,16 @@ var handleAdminPeople = async (req, url, env, ctx) => {
5160
5160
  });
5161
5161
  }
5162
5162
  }
5163
- const rows5 = [...people.values()].sort(
5163
+ const rows6 = [...people.values()].sort(
5164
5164
  (x, y) => (y.application?.createdAt ?? -1) - (x.application?.createdAt ?? -1)
5165
5165
  );
5166
- return json({ people: rows5 });
5166
+ return json({ people: rows6 });
5167
5167
  };
5168
5168
  var handleAdminPeopleAccess = async (req, url, env, ctx) => {
5169
5169
  if (req.method !== "GET" || url.pathname !== "/api/admin/people/access") return null;
5170
- const gate11 = await adminGate(req, env, ctx);
5171
- if (gate11 instanceof Response) return gate11;
5172
- const { db } = gate11;
5170
+ const gate12 = await adminGate(req, env, ctx);
5171
+ if (gate12 instanceof Response) return gate12;
5172
+ const { db } = gate12;
5173
5173
  const targetId = url.searchParams.get("userId") ?? "";
5174
5174
  if (!targetId.startsWith("user_")) return json({ error: "invalid userId" }, 400);
5175
5175
  const sk = await getVaultSecret(db, "clerk_secret_key");
@@ -5180,9 +5180,9 @@ var handleAdminPeopleAccess = async (req, url, env, ctx) => {
5180
5180
  };
5181
5181
  var handleAdminPeopleRole = async (req, url, env, ctx) => {
5182
5182
  if (req.method !== "POST" || url.pathname !== "/api/admin/people/role") return null;
5183
- const gate11 = await adminGate(req, env, ctx);
5184
- if (gate11 instanceof Response) return gate11;
5185
- const { db, actor } = gate11;
5183
+ const gate12 = await adminGate(req, env, ctx);
5184
+ if (gate12 instanceof Response) return gate12;
5185
+ const { db, actor } = gate12;
5186
5186
  let body;
5187
5187
  try {
5188
5188
  body = await req.json();
@@ -5218,8 +5218,8 @@ function canonical(value) {
5218
5218
  }
5219
5219
  return JSON.stringify(value) ?? "undefined";
5220
5220
  }
5221
- function positiveInt(raw, fallback, max) {
5222
- const parsed = Number(raw);
5221
+ function positiveInt(raw2, fallback, max) {
5222
+ const parsed = Number(raw2);
5223
5223
  return Number.isSafeInteger(parsed) && parsed >= 0 ? Math.min(parsed, max) : fallback;
5224
5224
  }
5225
5225
  var handleAdminClerkPrivateProfileSync = async (req, url, env, ctx) => {
@@ -5300,14 +5300,14 @@ function periodSeries(points, now, count, periodMs) {
5300
5300
  for (const point of points) {
5301
5301
  if (!Number.isFinite(point.t) || !Number.isFinite(point.v)) continue;
5302
5302
  if (point.t >= start && point.t <= now) {
5303
- const index = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
5304
- current[index] = (current[index] ?? 0) + point.v;
5303
+ const index2 = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
5304
+ current[index2] = (current[index2] ?? 0) + point.v;
5305
5305
  } else if (point.t >= previousStart && point.t < start) {
5306
- const index = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
5307
- previous[index] = (previous[index] ?? 0) + point.v;
5306
+ const index2 = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
5307
+ previous[index2] = (previous[index2] ?? 0) + point.v;
5308
5308
  }
5309
5309
  }
5310
- const labels = current.map((_, index) => new Date(start + index * periodMs).toISOString().slice(5, 10));
5310
+ const labels = current.map((_, index2) => new Date(start + index2 * periodMs).toISOString().slice(5, 10));
5311
5311
  return { labels, current, previous };
5312
5312
  }
5313
5313
  function dashboardMetricData(points, now) {
@@ -5720,11 +5720,11 @@ var handleAdminGroupEmail = async (req, url, env, ctx) => {
5720
5720
  for (const key of EMAIL_TEMPLATE_NAMES) {
5721
5721
  const t = templates[key];
5722
5722
  const subject = typeof t?.subject === "string" ? t.subject.trim() : "";
5723
- const text4 = typeof t?.text === "string" ? t.text : "";
5724
- if (!subject || !text4.trim()) return json({ error: `template "${key}" needs a subject and a body` }, 400);
5723
+ const text5 = typeof t?.text === "string" ? t.text : "";
5724
+ if (!subject || !text5.trim()) return json({ error: `template "${key}" needs a subject and a body` }, 400);
5725
5725
  if (/[\r\n]/.test(subject) || subject.length > 200) return json({ error: `template "${key}" subject must be a single line under 200 characters` }, 400);
5726
- if (text4.length > 1e4) return json({ error: `template "${key}" body is too long` }, 400);
5727
- clean[key] = { subject, text: text4, enabled: t?.enabled !== false };
5726
+ if (text5.length > 1e4) return json({ error: `template "${key}" body is too long` }, 400);
5727
+ clean[key] = { subject, text: text5, enabled: t?.enabled !== false };
5728
5728
  }
5729
5729
  const emailish = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
5730
5730
  const notificationEmail = str2(body.notificationEmail).trim();
@@ -6769,18 +6769,18 @@ function emailLooksReal(value) {
6769
6769
  function validateNetworkPersonImport(input, allowedChapterIds) {
6770
6770
  const errors = [];
6771
6771
  if (!input || typeof input !== "object" || Array.isArray(input)) return { ok: false, errors: ["import must be an object"] };
6772
- const raw = input;
6773
- if (raw.sourceKind !== "external_reimport") errors.push("sourceKind must be external_reimport");
6774
- if (raw.approved !== true || raw.isRealPerson !== true) errors.push("import must be approved and identify a real person");
6775
- const sourceSystem = typeof raw.sourceSystem === "string" ? raw.sourceSystem.trim().toLowerCase() : "";
6776
- const sourceRecordId = typeof raw.sourceRecordId === "string" ? raw.sourceRecordId.trim() : "";
6777
- const provenance = typeof raw.provenance === "string" ? raw.provenance.trim() : "";
6778
- const displayName = typeof raw.displayName === "string" ? raw.displayName.trim() : "";
6772
+ const raw2 = input;
6773
+ if (raw2.sourceKind !== "external_reimport") errors.push("sourceKind must be external_reimport");
6774
+ if (raw2.approved !== true || raw2.isRealPerson !== true) errors.push("import must be approved and identify a real person");
6775
+ const sourceSystem = typeof raw2.sourceSystem === "string" ? raw2.sourceSystem.trim().toLowerCase() : "";
6776
+ const sourceRecordId = typeof raw2.sourceRecordId === "string" ? raw2.sourceRecordId.trim() : "";
6777
+ const provenance = typeof raw2.provenance === "string" ? raw2.provenance.trim() : "";
6778
+ const displayName = typeof raw2.displayName === "string" ? raw2.displayName.trim() : "";
6779
6779
  if (!TOKEN.test(sourceSystem) || /(?:^|[._:-])(fixture|test|development|dev)(?:$|[._:-])/.test(sourceSystem)) errors.push("sourceSystem must name an approved non-development external system");
6780
6780
  if (!sourceRecordId || sourceRecordId.length > 200) errors.push("sourceRecordId is required and bounded");
6781
6781
  if (!provenance || provenance.length > 500) errors.push("provenance is required and bounded");
6782
6782
  if (!displayName || displayName.length > 200) errors.push("displayName is required and bounded");
6783
- const rawConsent = raw.consent && typeof raw.consent === "object" && !Array.isArray(raw.consent) ? raw.consent : {};
6783
+ const rawConsent = raw2.consent && typeof raw2.consent === "object" && !Array.isArray(raw2.consent) ? raw2.consent : {};
6784
6784
  const consentStates = ["unknown", "granted", "denied"];
6785
6785
  const marketing = typeof rawConsent.marketing === "string" ? rawConsent.marketing : "";
6786
6786
  const discretionary = typeof rawConsent.discretionary === "string" ? rawConsent.discretionary : "";
@@ -6790,10 +6790,10 @@ function validateNetworkPersonImport(input, allowedChapterIds) {
6790
6790
  errors.push("consent must include marketing and discretionary states, recordedAt, and a bounded basis");
6791
6791
  }
6792
6792
  const identities = [];
6793
- if (!Array.isArray(raw.identities) || raw.identities.length < 1 || raw.identities.length > 8) {
6793
+ if (!Array.isArray(raw2.identities) || raw2.identities.length < 1 || raw2.identities.length > 8) {
6794
6794
  errors.push("one to eight verified identities are required");
6795
6795
  } else {
6796
- for (const item of raw.identities) {
6796
+ for (const item of raw2.identities) {
6797
6797
  if (!item || typeof item !== "object" || Array.isArray(item)) {
6798
6798
  errors.push("identity must be an object");
6799
6799
  continue;
@@ -6825,9 +6825,9 @@ function validateNetworkPersonImport(input, allowedChapterIds) {
6825
6825
  seen.add(key);
6826
6826
  }
6827
6827
  const chapterLinks = [];
6828
- if (raw.chapterLinks !== void 0) {
6829
- if (!Array.isArray(raw.chapterLinks) || raw.chapterLinks.length > allowedChapterIds.length) errors.push("chapterLinks are invalid or too numerous");
6830
- else for (const item of raw.chapterLinks) {
6828
+ if (raw2.chapterLinks !== void 0) {
6829
+ if (!Array.isArray(raw2.chapterLinks) || raw2.chapterLinks.length > allowedChapterIds.length) errors.push("chapterLinks are invalid or too numerous");
6830
+ else for (const item of raw2.chapterLinks) {
6831
6831
  if (!item || typeof item !== "object" || Array.isArray(item)) {
6832
6832
  errors.push("chapter link must be an object");
6833
6833
  continue;
@@ -6863,9 +6863,9 @@ async function sha256(value) {
6863
6863
  }
6864
6864
  async function networkIdentityClaim(input) {
6865
6865
  const normalized = normalizeNetworkIdentity(input.kind, input.value);
6866
- const digest = await sha256(`${input.kind}\0${normalized}`);
6866
+ const digest2 = await sha256(`${input.kind}\0${normalized}`);
6867
6867
  const hint = input.kind === "email" ? `${normalized.slice(0, 1)}***@${normalized.split("@")[1]}` : input.kind === "phone" ? `***${normalized.slice(-4)}` : void 0;
6868
- return { id: `identity_${digest}`, kind: input.kind, value: normalized, ...hint ? { hint } : {}, verifiedAt: input.verifiedAt };
6868
+ return { id: `identity_${digest2}`, kind: input.kind, value: normalized, ...hint ? { hint } : {}, verifiedAt: input.verifiedAt };
6869
6869
  }
6870
6870
  async function networkSourceKey(sourceSystem, sourceRecordId) {
6871
6871
  return `alias_${await sha256(`${sourceSystem}\0${sourceRecordId}`)}`;
@@ -7207,9 +7207,9 @@ var handleAdminNetworkPersonRegistry = async (req, url, env, ctx) => {
7207
7207
  if (req.method === "POST") {
7208
7208
  let body;
7209
7209
  try {
7210
- const text4 = await req.text();
7211
- if (new TextEncoder().encode(text4).byteLength > MAX_BODY2) return json({ error: "request is too large" }, 413);
7212
- const parsed = JSON.parse(text4);
7210
+ const text5 = await req.text();
7211
+ if (new TextEncoder().encode(text5).byteLength > MAX_BODY2) return json({ error: "request is too large" }, 413);
7212
+ const parsed = JSON.parse(text5);
7213
7213
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("invalid");
7214
7214
  body = parsed;
7215
7215
  } catch {
@@ -7341,6 +7341,602 @@ var handleAdminNetworkPersonRegistry = async (req, url, env, ctx) => {
7341
7341
  });
7342
7342
  };
7343
7343
 
7344
+ // src/worker-routes-network-commercial.ts
7345
+ var import_stripe3 = require("@odla-ai/stripe");
7346
+
7347
+ // src/config-network-commercial.ts
7348
+ function networkCommercialSourceKey(source) {
7349
+ return [source.stripeMode, source.appId, source.environment, source.dataEnvironment, source.runtime, source.chapterId].join(":");
7350
+ }
7351
+
7352
+ // src/network-commercial.ts
7353
+ var raw = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
7354
+ var text3 = (value) => typeof value === "string" && value.length > 0 && value.length <= 255 ? value : void 0;
7355
+ var integer2 = (value) => typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
7356
+ var bool = (value) => typeof value === "boolean" ? value : void 0;
7357
+ var providerId = (value, prefix) => {
7358
+ const valueText = text3(value);
7359
+ return valueText && (!prefix || valueText.startsWith(prefix)) ? valueText : void 0;
7360
+ };
7361
+ function metadataCandidates(object) {
7362
+ const parent = raw(object.parent);
7363
+ const subscriptionDetails = raw(object.subscription_details);
7364
+ const parentSubscription = raw(parent.subscription_details);
7365
+ return [raw(object.metadata), raw(subscriptionDetails.metadata), raw(parentSubscription.metadata)];
7366
+ }
7367
+ function metadataValue(candidates, key) {
7368
+ return candidates.map((candidate) => text3(candidate[key])).find(Boolean);
7369
+ }
7370
+ function refId(value, prefix) {
7371
+ if (typeof value === "string") return providerId(value, prefix);
7372
+ return providerId(raw(value).id, prefix);
7373
+ }
7374
+ function subscriptionRef(object) {
7375
+ return refId(object.subscription, "sub_") ?? refId(raw(object.subscription_details).subscription, "sub_") ?? refId(raw(raw(object.parent).subscription_details).subscription, "sub_");
7376
+ }
7377
+ function firstPrice(object) {
7378
+ const data = raw(object.items).data;
7379
+ const item = Array.isArray(data) ? raw(data[0]) : {};
7380
+ const priceId = refId(item.price, "price_");
7381
+ const quantity = integer2(item.quantity);
7382
+ return { ...priceId ? { priceId } : {}, ...quantity !== void 0 ? { quantity } : {} };
7383
+ }
7384
+ function normalizeNetworkCommercialStripeEvent(input, digest2) {
7385
+ const event = raw(input);
7386
+ const eventId = providerId(event.id, "evt_") ?? "";
7387
+ const eventType = text3(event.type) ?? "";
7388
+ const object = raw(raw(event.data).object);
7389
+ const objectId = providerId(object.id) ?? "";
7390
+ const candidates = metadataCandidates(object);
7391
+ const identity = {};
7392
+ const identityKeys = {
7393
+ appId: "odlaAppId",
7394
+ chapterId: "chapterId",
7395
+ environment: "odlaEnvironment",
7396
+ dataEnvironment: "odlaDataEnvironment",
7397
+ runtime: "odlaRuntime"
7398
+ };
7399
+ for (const [target, source] of Object.entries(identityKeys)) {
7400
+ const value = metadataValue(candidates, source);
7401
+ if (value) identity[target] = value;
7402
+ }
7403
+ let objectKind = "ignored";
7404
+ if (eventType.startsWith("customer.subscription.")) objectKind = "subscription";
7405
+ else if (eventType.startsWith("customer.")) objectKind = "customer";
7406
+ else if (eventType.startsWith("invoice.")) objectKind = "invoice";
7407
+ else if (eventType.startsWith("refund.")) objectKind = "refund";
7408
+ else if (eventType === "charge.refunded") objectKind = "refund";
7409
+ const customerId = refId(object.customer, "cus_");
7410
+ const subscriptionId = objectKind === "subscription" ? providerId(objectId, "sub_") : subscriptionRef(object);
7411
+ const invoiceId = objectKind === "invoice" ? providerId(objectId, "in_") : refId(object.invoice, "in_");
7412
+ const chargeId = eventType === "charge.refunded" ? providerId(objectId, "ch_") : refId(object.charge, "ch_");
7413
+ const paymentIntentId = refId(object.payment_intent, "pi_");
7414
+ const price = firstPrice(object);
7415
+ const amountCents = integer2(eventType === "charge.refunded" ? object.amount_refunded : object.amount);
7416
+ const currency = text3(object.currency)?.toLowerCase();
7417
+ const status = text3(object.status);
7418
+ return {
7419
+ eventId,
7420
+ digest: digest2,
7421
+ eventType,
7422
+ eventCreated: integer2(event.created) ?? 0,
7423
+ livemode: event.livemode === true,
7424
+ objectKind,
7425
+ objectId,
7426
+ identity,
7427
+ ...metadataValue(candidates, "applicationId") ? { applicationId: metadataValue(candidates, "applicationId") } : {},
7428
+ ...metadataValue(candidates, "personId") ? { personId: metadataValue(candidates, "personId") } : {},
7429
+ ...metadataValue(candidates, "membershipId") ? { membershipId: metadataValue(candidates, "membershipId") } : {},
7430
+ ...customerId ? { customerId } : {},
7431
+ ...subscriptionId ? { subscriptionId } : {},
7432
+ ...invoiceId ? { invoiceId } : {},
7433
+ ...chargeId ? { chargeId } : {},
7434
+ ...paymentIntentId ? { paymentIntentId } : {},
7435
+ ...price,
7436
+ ...status ? { status } : {},
7437
+ ...currency ? { currency } : {},
7438
+ ...amountCents !== void 0 ? { amountCents } : {},
7439
+ ...integer2(object.amount_paid) !== void 0 ? { amountPaidCents: integer2(object.amount_paid) } : {},
7440
+ ...integer2(object.amount_remaining) !== void 0 ? { amountRemainingCents: integer2(object.amount_remaining) } : {},
7441
+ ...integer2(object.current_period_start) !== void 0 ? { currentPeriodStart: integer2(object.current_period_start) } : {},
7442
+ ...integer2(object.current_period_end) !== void 0 ? { currentPeriodEnd: integer2(object.current_period_end) } : {},
7443
+ ...bool(object.cancel_at_period_end) !== void 0 ? { cancelAtPeriodEnd: bool(object.cancel_at_period_end) } : {}
7444
+ };
7445
+ }
7446
+ var identityFields = ["appId", "chapterId", "environment", "dataEnvironment", "runtime"];
7447
+ function directSource(fact, sources) {
7448
+ const mode = fact.livemode ? "live" : "test";
7449
+ const supplied = identityFields.filter((field) => fact.identity[field] !== void 0);
7450
+ const candidates = sources.filter((source) => source.stripeMode === mode && supplied.every((field) => source[field] === fact.identity[field]));
7451
+ if (supplied.length === identityFields.length) return candidates.length === 1 ? candidates[0] : "wrong";
7452
+ if (supplied.length > 0 && candidates.length === 0) return "wrong";
7453
+ return null;
7454
+ }
7455
+ var objectRefs = (fact) => [
7456
+ fact.customerId && `customer:${fact.customerId}`,
7457
+ fact.subscriptionId && `subscription:${fact.subscriptionId}`,
7458
+ fact.invoiceId && `invoice:${fact.invoiceId}`,
7459
+ fact.chargeId && `charge:${fact.chargeId}`,
7460
+ fact.paymentIntentId && `payment_intent:${fact.paymentIntentId}`
7461
+ ].filter((value) => Boolean(value));
7462
+ function ownRef(fact) {
7463
+ if (!fact.objectId) return null;
7464
+ if (fact.objectKind === "customer") return `customer:${fact.objectId}`;
7465
+ if (fact.objectKind === "subscription") return `subscription:${fact.objectId}`;
7466
+ if (fact.objectKind === "invoice") return `invoice:${fact.objectId}`;
7467
+ if (fact.eventType.startsWith("refund.")) return `refund:${fact.objectId}`;
7468
+ return fact.eventType === "charge.refunded" ? `charge:${fact.objectId}` : null;
7469
+ }
7470
+ var order = (left, right) => left.eventCreated - right.eventCreated || left.eventId.localeCompare(right.eventId);
7471
+ function projectNetworkCommercialEvents(input, sources) {
7472
+ const facts2 = [...input].sort(order);
7473
+ const exceptions = [];
7474
+ const unique = [];
7475
+ const digests = /* @__PURE__ */ new Map();
7476
+ for (const fact of facts2) {
7477
+ const prior = digests.get(fact.eventId);
7478
+ if (prior === void 0) {
7479
+ digests.set(fact.eventId, fact.digest);
7480
+ unique.push(fact);
7481
+ } else if (prior !== fact.digest) exceptions.push({ eventId: fact.eventId, kind: "collision", detail: { eventType: fact.eventType, objectId: fact.objectId } });
7482
+ }
7483
+ const assignments = /* @__PURE__ */ new Map();
7484
+ const refs = /* @__PURE__ */ new Map();
7485
+ for (const fact of unique) {
7486
+ const direct = directSource(fact, sources);
7487
+ if (direct && direct !== "wrong") assignments.set(fact.eventId, direct);
7488
+ }
7489
+ let changed = true;
7490
+ while (changed) {
7491
+ changed = false;
7492
+ for (const fact of unique) {
7493
+ const assigned = assignments.get(fact.eventId);
7494
+ if (assigned) {
7495
+ const key = networkCommercialSourceKey(assigned);
7496
+ for (const ref of [...objectRefs(fact), ownRef(fact)].filter((value) => Boolean(value))) {
7497
+ const keys2 = refs.get(ref) ?? /* @__PURE__ */ new Set();
7498
+ const before = keys2.size;
7499
+ keys2.add(key);
7500
+ refs.set(ref, keys2);
7501
+ if (keys2.size !== before) changed = true;
7502
+ }
7503
+ continue;
7504
+ }
7505
+ if (directSource(fact, sources) === "wrong") continue;
7506
+ const keys = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
7507
+ if (keys.size !== 1) continue;
7508
+ const candidate = sources.find((source) => networkCommercialSourceKey(source) === [...keys][0]);
7509
+ if (candidate && directSource({ ...fact, identity: fact.identity }, [candidate]) !== "wrong") {
7510
+ assignments.set(fact.eventId, candidate);
7511
+ changed = true;
7512
+ }
7513
+ }
7514
+ }
7515
+ const latest = /* @__PURE__ */ new Map();
7516
+ for (const fact of unique) {
7517
+ if (fact.objectKind === "ignored" || !fact.eventId || !fact.objectId) {
7518
+ exceptions.push({ eventId: fact.eventId, kind: "unsupported", detail: { eventType: fact.eventType, objectId: fact.objectId } });
7519
+ continue;
7520
+ }
7521
+ const source = assignments.get(fact.eventId);
7522
+ if (!source) {
7523
+ const direct = directSource(fact, sources);
7524
+ const keys = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
7525
+ exceptions.push({ eventId: fact.eventId, kind: direct === "wrong" ? "wrong_partition" : keys.size > 1 ? "ambiguous_partition" : "unmatched_partition", detail: { eventType: fact.eventType, objectId: fact.objectId } });
7526
+ continue;
7527
+ }
7528
+ const row = { ...fact, sourceKey: networkCommercialSourceKey(source) };
7529
+ const key = `${fact.objectKind}:${fact.objectId}`;
7530
+ const prior = latest.get(key);
7531
+ if (!prior || order(prior, fact) <= 0) latest.set(key, row);
7532
+ }
7533
+ const rows6 = [...latest.values()];
7534
+ const customers = rows6.filter((row) => row.objectKind === "customer");
7535
+ const subscriptions = rows6.filter((row) => row.objectKind === "subscription");
7536
+ const invoices = rows6.filter((row) => row.objectKind === "invoice");
7537
+ const refunds = rows6.filter((row) => row.objectKind === "refund" && row.eventType.startsWith("refund."));
7538
+ for (const charge of rows6.filter((row) => row.eventType === "charge.refunded")) {
7539
+ if (!refunds.some((refund) => refund.chargeId === charge.objectId)) {
7540
+ exceptions.push({ eventId: charge.eventId, kind: "charge_refund_without_refund_object", sourceKey: charge.sourceKey, detail: { eventType: charge.eventType, objectId: charge.objectId } });
7541
+ }
7542
+ }
7543
+ const totalsMap = /* @__PURE__ */ new Map();
7544
+ const add = (row, field, amount) => {
7545
+ if (!row.currency) return;
7546
+ const key = `${row.sourceKey}:${row.currency}`;
7547
+ const total = totalsMap.get(key) ?? { sourceKey: row.sourceKey, currency: row.currency, paidCents: 0, refundedCents: 0, netCents: 0 };
7548
+ total[field] += amount;
7549
+ total.netCents = total.paidCents - total.refundedCents;
7550
+ totalsMap.set(key, total);
7551
+ };
7552
+ for (const row of invoices) add(row, "paidCents", row.amountPaidCents ?? 0);
7553
+ for (const row of refunds) add(row, "refundedCents", row.amountCents ?? 0);
7554
+ return { customers, subscriptions, invoices, refunds, exceptions, totals: [...totalsMap.values()].sort((a, b) => `${a.sourceKey}:${a.currency}`.localeCompare(`${b.sourceKey}:${b.currency}`)) };
7555
+ }
7556
+
7557
+ // src/network-commercial-store.ts
7558
+ var eventRowId = (eventId) => `network-commercial-event:${eventId}`;
7559
+ var projectionId = (kind, sourceKey, providerId2) => `network-commercial:${kind}:${sourceKey}:${providerId2}`;
7560
+ var exceptionId = (eventId, kind) => `network-commercial-exception:${eventId}:${kind}`;
7561
+ var materializedNamespaces = [
7562
+ "networkCommercialCustomers",
7563
+ "networkCommercialSubscriptions",
7564
+ "networkCommercialInvoices",
7565
+ "networkCommercialRefunds"
7566
+ ];
7567
+ async function rows5(db, ns) {
7568
+ const all = [];
7569
+ const pageSize = 500;
7570
+ for (let offset = 0; ; offset += pageSize) {
7571
+ const result = await db.query({ [ns]: { $: { limit: pageSize, offset } } });
7572
+ const page = Array.isArray(result[ns]) ? result[ns] : [];
7573
+ all.push(...page);
7574
+ if (page.length < pageSize) return all;
7575
+ }
7576
+ }
7577
+ async function loadNetworkCommercialProjection(db, sources) {
7578
+ const stored = await rows5(db, "networkCommercialEvents");
7579
+ const projection = projectNetworkCommercialEvents(stored.map((row) => row.fact), sources);
7580
+ const persisted = await rows5(db, "networkCommercialExceptions");
7581
+ for (const row of persisted) {
7582
+ if (row.status !== "active" || row.kind !== "collision") continue;
7583
+ if (projection.exceptions.some((item) => item.eventId === row.eventId && item.kind === "collision")) continue;
7584
+ projection.exceptions.push({
7585
+ eventId: String(row.eventId),
7586
+ kind: "collision",
7587
+ ...typeof row.sourceKey === "string" ? { sourceKey: row.sourceKey } : {},
7588
+ detail: row.detail
7589
+ });
7590
+ }
7591
+ return projection;
7592
+ }
7593
+ function projectionOps(projection, now) {
7594
+ const groups = [
7595
+ ["networkCommercialCustomers", "customer", projection.customers],
7596
+ ["networkCommercialSubscriptions", "subscription", projection.subscriptions],
7597
+ ["networkCommercialInvoices", "invoice", projection.invoices],
7598
+ ["networkCommercialRefunds", "refund", projection.refunds]
7599
+ ];
7600
+ return groups.flatMap(([ns, kind, projected]) => projected.map((snapshot) => ({
7601
+ t: "update",
7602
+ ns,
7603
+ id: projectionId(kind, snapshot.sourceKey, snapshot.objectId),
7604
+ attrs: {
7605
+ id: projectionId(kind, snapshot.sourceKey, snapshot.objectId),
7606
+ providerId: snapshot.objectId,
7607
+ sourceKey: snapshot.sourceKey,
7608
+ status: "active",
7609
+ snapshot,
7610
+ eventCreated: snapshot.eventCreated,
7611
+ updatedAt: now
7612
+ }
7613
+ })));
7614
+ }
7615
+ async function rebuildNetworkCommercialProjection(db, sources, mutationId, now = Date.now()) {
7616
+ const projection = await loadNetworkCommercialProjection(db, sources);
7617
+ const [existing, ...materialized] = await Promise.all([
7618
+ rows5(db, "networkCommercialExceptions"),
7619
+ ...materializedNamespaces.map((ns) => rows5(db, ns))
7620
+ ]);
7621
+ const activeIds = new Set(projection.exceptions.map((item) => exceptionId(item.eventId, item.kind)));
7622
+ const projectedIds = new Set(projectionOps(projection, now).map((op) => String(op.id)));
7623
+ const ops = [
7624
+ ...projectionOps(projection, now),
7625
+ ...materializedNamespaces.flatMap((ns, index2) => materialized[index2].filter((row) => row.status === "active" && !projectedIds.has(String(row.id))).map((row) => ({ t: "update", ns, id: String(row.id), attrs: { status: "inactive", updatedAt: now } }))),
7626
+ ...existing.filter((row) => row.status === "active" && !activeIds.has(String(row.id))).map((row) => ({
7627
+ t: "update",
7628
+ ns: "networkCommercialExceptions",
7629
+ id: String(row.id),
7630
+ attrs: { status: "resolved", updatedAt: now }
7631
+ })),
7632
+ ...projection.exceptions.map((item) => ({
7633
+ t: "update",
7634
+ ns: "networkCommercialExceptions",
7635
+ id: exceptionId(item.eventId, item.kind),
7636
+ attrs: {
7637
+ id: exceptionId(item.eventId, item.kind),
7638
+ eventId: item.eventId,
7639
+ kind: item.kind,
7640
+ ...item.sourceKey ? { sourceKey: item.sourceKey } : {},
7641
+ status: "active",
7642
+ detail: item.detail,
7643
+ updatedAt: now
7644
+ }
7645
+ }))
7646
+ ];
7647
+ if (ops.length > 0) await db.transact(ops, { mutationId });
7648
+ return projection;
7649
+ }
7650
+ async function ingestNetworkCommercialFact(db, sources, fact, now = Date.now()) {
7651
+ const found = await db.query({ networkCommercialEvents: { $: { where: { eventId: fact.eventId }, limit: 1 } } });
7652
+ const existing = found.networkCommercialEvents?.[0];
7653
+ if (existing) {
7654
+ const projection2 = await loadNetworkCommercialProjection(db, sources);
7655
+ if (existing.digest === fact.digest) return { status: "replayed", projection: projection2 };
7656
+ const id2 = exceptionId(fact.eventId, "collision");
7657
+ await db.transact([{ t: "update", ns: "networkCommercialExceptions", id: id2, attrs: {
7658
+ id: id2,
7659
+ eventId: fact.eventId,
7660
+ kind: "collision",
7661
+ status: "active",
7662
+ detail: { eventType: fact.eventType, objectId: fact.objectId },
7663
+ updatedAt: now
7664
+ } }], { mutationId: `network-commercial-collision:${fact.eventId}:${fact.digest}` });
7665
+ return { status: "collision", projection: await loadNetworkCommercialProjection(db, sources) };
7666
+ }
7667
+ const id = eventRowId(fact.eventId);
7668
+ let lostRace = false;
7669
+ let duplicate = false;
7670
+ try {
7671
+ const write = await db.transact([{ t: "update", ns: "networkCommercialEvents", id, attrs: {
7672
+ id,
7673
+ eventId: fact.eventId,
7674
+ digest: fact.digest,
7675
+ eventType: fact.eventType,
7676
+ eventCreated: fact.eventCreated,
7677
+ livemode: fact.livemode,
7678
+ fact,
7679
+ receivedAt: now
7680
+ } }], {
7681
+ mutationId: `network-commercial-ingest:${fact.eventId}:${fact.digest}`,
7682
+ guards: [{ ns: "networkCommercialEvents", id, exists: false }]
7683
+ });
7684
+ duplicate = write.duplicate;
7685
+ } catch (error) {
7686
+ if (error.code !== "transact_guard_failed") throw error;
7687
+ lostRace = true;
7688
+ }
7689
+ if (duplicate || lostRace) {
7690
+ const raced = await db.query({ networkCommercialEvents: { $: { where: { eventId: fact.eventId }, limit: 1 } } });
7691
+ const row = raced.networkCommercialEvents?.[0];
7692
+ const projection2 = await loadNetworkCommercialProjection(db, sources);
7693
+ if (row?.digest === fact.digest) return { status: "replayed", projection: projection2 };
7694
+ const id2 = exceptionId(fact.eventId, "collision");
7695
+ await db.transact([{ t: "update", ns: "networkCommercialExceptions", id: id2, attrs: {
7696
+ id: id2,
7697
+ eventId: fact.eventId,
7698
+ kind: "collision",
7699
+ status: "active",
7700
+ detail: { eventType: fact.eventType, objectId: fact.objectId },
7701
+ updatedAt: now
7702
+ } }], { mutationId: `network-commercial-collision:${fact.eventId}:${fact.digest}` });
7703
+ return { status: "collision", projection: await loadNetworkCommercialProjection(db, sources) };
7704
+ }
7705
+ const projection = await rebuildNetworkCommercialProjection(
7706
+ db,
7707
+ sources,
7708
+ `network-commercial-project:${fact.eventId}:${fact.digest}`,
7709
+ now
7710
+ );
7711
+ return { status: "stored", projection };
7712
+ }
7713
+
7714
+ // src/network-commercial-reconcile.ts
7715
+ var asRaw = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
7716
+ async function listStripeObjects(client, path, params = {}, maxPages = 20) {
7717
+ const objects = [];
7718
+ let startingAfter;
7719
+ for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
7720
+ const page = await client.request("GET", path, {
7721
+ ...params,
7722
+ limit: 100,
7723
+ ...startingAfter ? { starting_after: startingAfter } : {}
7724
+ });
7725
+ if (!Array.isArray(page.data) || typeof page.has_more !== "boolean") {
7726
+ throw new Error("Stripe returned an invalid commercial list response");
7727
+ }
7728
+ const rows6 = page.data.map(asRaw).filter((row) => row !== null);
7729
+ if (rows6.length !== page.data.length) throw new Error("Stripe returned a malformed commercial object");
7730
+ objects.push(...rows6);
7731
+ if (!page.has_more) return { objects, complete: true };
7732
+ const cursor = rows6.at(-1)?.id;
7733
+ if (typeof cursor !== "string" || !cursor) throw new Error("Stripe pagination did not provide a cursor");
7734
+ startingAfter = cursor;
7735
+ }
7736
+ return { objects, complete: false };
7737
+ }
7738
+ function facts(snapshots) {
7739
+ const groups = [
7740
+ ["customer.updated", snapshots.customers.objects],
7741
+ ["customer.subscription.updated", snapshots.subscriptions.objects],
7742
+ ["invoice.updated", snapshots.invoices.objects],
7743
+ ["refund.updated", snapshots.refunds.objects]
7744
+ ];
7745
+ return groups.flatMap(([eventType, objects]) => objects.map((object) => {
7746
+ const id = typeof object.id === "string" ? object.id : "invalid";
7747
+ return normalizeNetworkCommercialStripeEvent({
7748
+ id: `evt_snapshot_${eventType.replaceAll(".", "_")}_${id}`,
7749
+ type: eventType,
7750
+ created: typeof object.created === "number" ? object.created : 0,
7751
+ livemode: object.livemode === true,
7752
+ data: { object }
7753
+ }, `stripe-snapshot:${eventType}:${id}`);
7754
+ }));
7755
+ }
7756
+ var selected = (row) => ({
7757
+ status: row.status ?? null,
7758
+ currency: row.currency ?? null,
7759
+ amountCents: row.amountCents ?? null,
7760
+ amountPaidCents: row.amountPaidCents ?? null,
7761
+ amountRemainingCents: row.amountRemainingCents ?? null,
7762
+ customerId: row.customerId ?? null,
7763
+ subscriptionId: row.subscriptionId ?? null,
7764
+ invoiceId: row.invoiceId ?? null,
7765
+ priceId: row.priceId ?? null,
7766
+ quantity: row.quantity ?? null,
7767
+ applicationId: row.applicationId ?? null,
7768
+ personId: row.personId ?? null,
7769
+ membershipId: row.membershipId ?? null,
7770
+ currentPeriodStart: row.currentPeriodStart ?? null,
7771
+ currentPeriodEnd: row.currentPeriodEnd ?? null,
7772
+ cancelAtPeriodEnd: row.cancelAtPeriodEnd ?? null
7773
+ });
7774
+ var index = (projection) => new Map(
7775
+ [...projection.customers, ...projection.subscriptions, ...projection.invoices, ...projection.refunds].map((row) => [`${row.sourceKey}:${row.objectKind}:${row.objectId}`, row])
7776
+ );
7777
+ function reconcileStripeSnapshot(shadow, snapshots, sources) {
7778
+ const stripe = projectNetworkCommercialEvents(facts(snapshots), sources);
7779
+ const shadowRows = index(shadow);
7780
+ const stripeRows = index(stripe);
7781
+ const exceptions = [];
7782
+ const complete = Object.values(snapshots).every((snapshot) => snapshot.complete);
7783
+ if (!complete) exceptions.push({ kind: "stripe_snapshot_truncated", detail: {} });
7784
+ for (const [key, row] of shadowRows) {
7785
+ const other = stripeRows.get(key);
7786
+ if (!other) {
7787
+ exceptions.push({ kind: "missing_from_stripe", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: {} });
7788
+ } else if (JSON.stringify(selected(row)) !== JSON.stringify(selected(other))) {
7789
+ exceptions.push({ kind: "object_mismatch", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: { shadow: selected(row), stripe: selected(other) } });
7790
+ }
7791
+ }
7792
+ for (const [key, row] of stripeRows) {
7793
+ if (!shadowRows.has(key)) exceptions.push({ kind: "missing_from_shadow", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: {} });
7794
+ }
7795
+ if (JSON.stringify(shadow.totals) !== JSON.stringify(stripe.totals)) {
7796
+ exceptions.push({ kind: "totals_mismatch", detail: { shadow: shadow.totals, stripe: stripe.totals } });
7797
+ }
7798
+ return {
7799
+ ok: exceptions.length === 0,
7800
+ complete,
7801
+ ignoredObjects: stripe.exceptions.filter((item) => item.kind === "wrong_partition" || item.kind === "unmatched_partition").length,
7802
+ exceptions,
7803
+ stripeTotals: stripe.totals
7804
+ };
7805
+ }
7806
+
7807
+ // src/worker-routes-network-commercial.ts
7808
+ var ADMIN_PATH2 = "/api/admin/network-commercial-read-model";
7809
+ var WEBHOOK_PATH = "/api/network-commercial/stripe-webhook";
7810
+ var MAX_BODY3 = 512e3;
7811
+ var MUTATION_ID5 = /^[A-Za-z0-9_-]{16,128}$/;
7812
+ async function digest(body) {
7813
+ const value = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(body));
7814
+ return [...new Uint8Array(value)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
7815
+ }
7816
+ async function boundedBody(req) {
7817
+ if (Number(req.headers.get("content-length") ?? 0) > MAX_BODY3) return null;
7818
+ const body = await req.text();
7819
+ return new TextEncoder().encode(body).byteLength <= MAX_BODY3 ? body : null;
7820
+ }
7821
+ async function gate11(req, env, ctx) {
7822
+ const db = ctx.makeDb(env);
7823
+ const user = await ctx.verifyUser(req, env);
7824
+ if (!user) return { response: json({ error: "unauthorized" }, 401) };
7825
+ if (!await ctx.isSuperAdminEmail(db, user.email)) return { response: json({ error: "forbidden" }, 403) };
7826
+ return { db, user };
7827
+ }
7828
+ function safeProjection(projection) {
7829
+ const row = (item) => ({
7830
+ sourceKey: item.sourceKey,
7831
+ providerId: item.objectId,
7832
+ eventType: item.eventType,
7833
+ eventCreated: item.eventCreated,
7834
+ status: item.status ?? null,
7835
+ currency: item.currency ?? null,
7836
+ amountCents: item.amountCents ?? null,
7837
+ amountPaidCents: item.amountPaidCents ?? null,
7838
+ amountRemainingCents: item.amountRemainingCents ?? null,
7839
+ customerId: item.customerId ?? null,
7840
+ subscriptionId: item.subscriptionId ?? null,
7841
+ invoiceId: item.invoiceId ?? null,
7842
+ priceId: item.priceId ?? null,
7843
+ quantity: item.quantity ?? null,
7844
+ applicationId: item.applicationId ?? null,
7845
+ personId: item.personId ?? null,
7846
+ membershipId: item.membershipId ?? null,
7847
+ currentPeriodStart: item.currentPeriodStart ?? null,
7848
+ currentPeriodEnd: item.currentPeriodEnd ?? null,
7849
+ cancelAtPeriodEnd: item.cancelAtPeriodEnd ?? null
7850
+ });
7851
+ return {
7852
+ version: 1,
7853
+ mode: "shadow",
7854
+ writable: false,
7855
+ authoritativeForCharges: false,
7856
+ totals: projection.totals,
7857
+ customers: projection.customers.map(row),
7858
+ subscriptions: projection.subscriptions.map(row),
7859
+ invoices: projection.invoices.map(row),
7860
+ refunds: projection.refunds.map(row),
7861
+ exceptions: projection.exceptions
7862
+ };
7863
+ }
7864
+ var handleNetworkCommercialStripeWebhook = async (req, url, env, ctx) => {
7865
+ if (url.pathname !== WEBHOOK_PATH) return null;
7866
+ if (!ctx.chapter.networkCommercialReadModel) return json({ error: "not found" }, 404);
7867
+ if (req.method !== "POST") return json({ error: "method not allowed" }, 405);
7868
+ const body = await boundedBody(req);
7869
+ if (body === null) return json({ error: "request is too large" }, 413);
7870
+ const signature = req.headers.get("stripe-signature") ?? "";
7871
+ const db = ctx.makeDb(env);
7872
+ let secret;
7873
+ try {
7874
+ secret = await db.secrets.get("network_commercial_stripe_webhook_secret");
7875
+ } catch {
7876
+ return json({ error: "commercial webhook is not configured" }, 503);
7877
+ }
7878
+ try {
7879
+ const event = await (0, import_stripe3.constructStripeEvent)(body, signature, secret);
7880
+ const fact = normalizeNetworkCommercialStripeEvent(event, await digest(body));
7881
+ if (!fact.eventId || !fact.eventType || !fact.objectId) return json({ error: "Stripe event identity is invalid" }, 400);
7882
+ const result = await ingestNetworkCommercialFact(db, ctx.chapter.networkCommercialReadModel.sources, fact);
7883
+ if (result.status === "collision") {
7884
+ console.error("network commercial event collision", JSON.stringify({ eventId: fact.eventId, eventType: fact.eventType }));
7885
+ }
7886
+ return json({ ok: true, status: result.status, eventId: fact.eventId });
7887
+ } catch (error) {
7888
+ if (error instanceof import_stripe3.StripeError) return json({ error: error.message, code: error.code }, 400);
7889
+ throw error;
7890
+ }
7891
+ };
7892
+ var handleAdminNetworkCommercialReadModel = async (req, url, env, ctx) => {
7893
+ if (url.pathname !== ADMIN_PATH2) return null;
7894
+ if (!ctx.chapter.networkCommercialReadModel) return json({ error: "not found" }, 404);
7895
+ if (req.method !== "GET" && req.method !== "POST") return json({ error: "method not allowed" }, 405);
7896
+ const got = await gate11(req, env, ctx);
7897
+ if ("response" in got) return got.response;
7898
+ const db = got.db;
7899
+ if (req.method === "POST") {
7900
+ const body = await boundedBody(req);
7901
+ if (body === null) return json({ error: "request is too large" }, 413);
7902
+ let parsed;
7903
+ try {
7904
+ const value = JSON.parse(body);
7905
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("shape");
7906
+ parsed = value;
7907
+ } catch {
7908
+ return json({ error: "invalid JSON body" }, 400);
7909
+ }
7910
+ if (parsed.command === "reconcile-stripe") {
7911
+ const secretKey = await getVaultSecret(db, "stripe_secret_key");
7912
+ if (!secretKey) return json({ error: "Stripe reconciliation is not configured" }, 503);
7913
+ try {
7914
+ const stripe = (0, import_stripe3.initStripe)({ secretKey });
7915
+ const [customers, subscriptions, invoices, refunds] = await Promise.all([
7916
+ listStripeObjects(stripe, "/v1/customers"),
7917
+ listStripeObjects(stripe, "/v1/subscriptions", { status: "all" }),
7918
+ listStripeObjects(stripe, "/v1/invoices"),
7919
+ listStripeObjects(stripe, "/v1/refunds")
7920
+ ]);
7921
+ const projection2 = await loadNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources);
7922
+ return json({
7923
+ reconciled: true,
7924
+ ...reconcileStripeSnapshot(projection2, { customers, subscriptions, invoices, refunds }, ctx.chapter.networkCommercialReadModel.sources)
7925
+ });
7926
+ } catch (error) {
7927
+ if (error instanceof import_stripe3.StripeError) return json({ error: "Stripe reconciliation failed", code: error.code }, 502);
7928
+ return json({ error: "Stripe reconciliation failed" }, 502);
7929
+ }
7930
+ }
7931
+ if (parsed.command !== "rebuild" || typeof parsed.mutationId !== "string" || !MUTATION_ID5.test(parsed.mutationId)) {
7932
+ return json({ error: "rebuild or reconcile-stripe command is required; rebuild also requires a valid mutationId" }, 400);
7933
+ }
7934
+ const projection = await rebuildNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources, parsed.mutationId);
7935
+ return json({ rebuilt: true, ...safeProjection(projection) });
7936
+ }
7937
+ return json(safeProjection(await loadNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources)));
7938
+ };
7939
+
7344
7940
  // src/worker-routes-formation.ts
7345
7941
  var import_crm11 = require("@odla-ai/crm");
7346
7942
  var SUBMISSION = /^[A-Za-z0-9_-]{8,128}$/;
@@ -7357,11 +7953,11 @@ var handleFormation = async (req, url, env, ctx) => {
7357
7953
  });
7358
7954
  }
7359
7955
  if (req.method !== "POST" || url.pathname !== "/api/formation/applications") return null;
7360
- const raw = await req.text();
7361
- if (raw.length > formation.bodyCap) return json({ error: "request body too large" }, 413);
7956
+ const raw2 = await req.text();
7957
+ if (raw2.length > formation.bodyCap) return json({ error: "request body too large" }, 413);
7362
7958
  let body;
7363
7959
  try {
7364
- body = JSON.parse(raw);
7960
+ body = JSON.parse(raw2);
7365
7961
  } catch {
7366
7962
  return json({ error: "invalid JSON body" }, 400);
7367
7963
  }
@@ -7445,7 +8041,7 @@ var record3 = (value) => value !== null && typeof value === "object" && !Array.i
7445
8041
  function unknownKeys3(value, allowed2, path, errors) {
7446
8042
  for (const key of Object.keys(value)) if (!allowed2.has(key)) errors.push(`${path}.${key} is not supported`);
7447
8043
  }
7448
- function text3(value, path, errors, max = MAX_TEXT2, required = true) {
8044
+ function text4(value, path, errors, max = MAX_TEXT2, required = true) {
7449
8045
  if (value === void 0 && !required) return void 0;
7450
8046
  if (typeof value !== "string" || required && !value.trim()) {
7451
8047
  errors.push(`${path} must be a non-empty string`);
@@ -7455,23 +8051,23 @@ function text3(value, path, errors, max = MAX_TEXT2, required = true) {
7455
8051
  if (result.length > max) errors.push(`${path} must be at most ${max} characters`);
7456
8052
  return result;
7457
8053
  }
7458
- function integer2(value, path, errors, minimum = 1) {
8054
+ function integer3(value, path, errors, minimum = 1) {
7459
8055
  if (!Number.isSafeInteger(value) || Number(value) < minimum) errors.push(`${path} must be an integer >= ${minimum}`);
7460
8056
  return Number(value);
7461
8057
  }
7462
8058
  function scopeValue(value, path, errors) {
7463
- const raw = record3(value);
7464
- if (!raw) {
8059
+ const raw2 = record3(value);
8060
+ if (!raw2) {
7465
8061
  errors.push(`${path} must be an object`);
7466
8062
  return null;
7467
8063
  }
7468
- unknownKeys3(raw, SCOPE_KEYS, path, errors);
7469
- if (raw.kind === "global" || raw.kind === "built_not_found") {
7470
- if (raw.chapterId !== void 0) errors.push(`${path}.chapterId is allowed only for chapter scope`);
7471
- return { kind: raw.kind };
8064
+ unknownKeys3(raw2, SCOPE_KEYS, path, errors);
8065
+ if (raw2.kind === "global" || raw2.kind === "built_not_found") {
8066
+ if (raw2.chapterId !== void 0) errors.push(`${path}.chapterId is allowed only for chapter scope`);
8067
+ return { kind: raw2.kind };
7472
8068
  }
7473
- if (raw.kind === "chapter") {
7474
- const chapterId = text3(raw.chapterId, `${path}.chapterId`, errors, 63) ?? "";
8069
+ if (raw2.kind === "chapter") {
8070
+ const chapterId = text4(raw2.chapterId, `${path}.chapterId`, errors, 63) ?? "";
7475
8071
  if (chapterId && !APP_ID.test(chapterId)) errors.push(`${path}.chapterId must be a lowercase app slug`);
7476
8072
  return { kind: "chapter", chapterId };
7477
8073
  }
@@ -7479,28 +8075,28 @@ function scopeValue(value, path, errors) {
7479
8075
  return null;
7480
8076
  }
7481
8077
  function resourceValue(value, path, errors) {
7482
- const raw = record3(value);
7483
- if (!raw) {
8078
+ const raw2 = record3(value);
8079
+ if (!raw2) {
7484
8080
  errors.push(`${path} must be an object`);
7485
8081
  return null;
7486
8082
  }
7487
- unknownKeys3(raw, RESOURCE_KEYS, path, errors);
7488
- const id = text3(raw.id, `${path}.id`, errors, 80) ?? "";
8083
+ unknownKeys3(raw2, RESOURCE_KEYS, path, errors);
8084
+ const id = text4(raw2.id, `${path}.id`, errors, 80) ?? "";
7489
8085
  if (id && !ID4.test(id)) errors.push(`${path}.id must be lowercase kebab-case`);
7490
- const kind = text3(raw.kind, `${path}.kind`, errors) ?? "";
8086
+ const kind = text4(raw2.kind, `${path}.kind`, errors) ?? "";
7491
8087
  if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
7492
- const title = text3(raw.title, `${path}.title`, errors, 240) ?? "";
7493
- const summary = text3(raw.summary, `${path}.summary`, errors, 1e3, false);
7494
- const status = text3(raw.status, `${path}.status`, errors) ?? "";
8088
+ const title = text4(raw2.title, `${path}.title`, errors, 240) ?? "";
8089
+ const summary = text4(raw2.summary, `${path}.summary`, errors, 1e3, false);
8090
+ const status = text4(raw2.status, `${path}.status`, errors) ?? "";
7495
8091
  if (!STATUSES.has(status)) errors.push(`${path}.status must be draft, published, or archived`);
7496
- const revision = integer2(raw.revision, `${path}.revision`, errors);
7497
- const scope = scopeValue(raw.scope, `${path}.scope`, errors) ?? { kind: "global" };
7498
- const tags = Array.isArray(raw.tags) ? raw.tags.map((tag, index) => text3(tag, `${path}.tags[${index}]`, errors, 80) ?? "") : [];
7499
- if (!Array.isArray(raw.tags)) errors.push(`${path}.tags must be an array`);
8092
+ const revision = integer3(raw2.revision, `${path}.revision`, errors);
8093
+ const scope = scopeValue(raw2.scope, `${path}.scope`, errors) ?? { kind: "global" };
8094
+ const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) => text4(tag, `${path}.tags[${index2}]`, errors, 80) ?? "") : [];
8095
+ if (!Array.isArray(raw2.tags)) errors.push(`${path}.tags must be an array`);
7500
8096
  if (tags.length > MAX_TAGS) errors.push(`${path}.tags may contain at most ${MAX_TAGS} items`);
7501
8097
  if (new Set(tags).size !== tags.length) errors.push(`${path}.tags must be unique`);
7502
- const bodyMarkdown = text3(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN3, false);
7503
- const url = text3(raw.url, `${path}.url`, errors, 2048, false);
8098
+ const bodyMarkdown = text4(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN3, false);
8099
+ const url = text4(raw2.url, `${path}.url`, errors, 2048, false);
7504
8100
  if (url) {
7505
8101
  try {
7506
8102
  if (new URL(url).protocol !== "https:") errors.push(`${path}.url must use https`);
@@ -7508,13 +8104,13 @@ function resourceValue(value, path, errors) {
7508
8104
  errors.push(`${path}.url must be a valid https URL`);
7509
8105
  }
7510
8106
  }
7511
- const asset = raw.brandAsset === void 0 ? null : record3(raw.brandAsset);
7512
- if (raw.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
8107
+ const asset = raw2.brandAsset === void 0 ? null : record3(raw2.brandAsset);
8108
+ if (raw2.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
7513
8109
  if (asset) unknownKeys3(asset, ASSET_KEYS, `${path}.brandAsset`, errors);
7514
8110
  const brandAsset = asset ? {
7515
- bookId: text3(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
7516
- assetId: text3(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
7517
- ...asset.filename === void 0 ? {} : { filename: text3(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
8111
+ bookId: text4(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
8112
+ assetId: text4(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
8113
+ ...asset.filename === void 0 ? {} : { filename: text4(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
7518
8114
  } : void 0;
7519
8115
  if ((kind === "document" || kind === "note") && !bodyMarkdown) errors.push(`${path}.bodyMarkdown is required for ${kind}`);
7520
8116
  if ((kind === "link" || kind === "inspiration") && !url) errors.push(`${path}.url is required for ${kind}`);
@@ -7522,58 +8118,58 @@ function resourceValue(value, path, errors) {
7522
8118
  if (!["document", "note"].includes(kind) && bodyMarkdown) errors.push(`${path}.bodyMarkdown is not allowed for ${kind}`);
7523
8119
  if (!["link", "inspiration"].includes(kind) && url) errors.push(`${path}.url is not allowed for ${kind}`);
7524
8120
  if (!["image", "file"].includes(kind) && brandAsset) errors.push(`${path}.brandAsset is not allowed for ${kind}`);
7525
- const provenanceRaw = record3(raw.provenance);
8121
+ const provenanceRaw = record3(raw2.provenance);
7526
8122
  if (!provenanceRaw) errors.push(`${path}.provenance must be an object`);
7527
8123
  if (provenanceRaw) unknownKeys3(provenanceRaw, PROVENANCE_KEYS, `${path}.provenance`, errors);
7528
8124
  const promotedRaw = provenanceRaw?.promotedFrom === void 0 ? null : record3(provenanceRaw.promotedFrom);
7529
8125
  if (provenanceRaw?.promotedFrom !== void 0 && !promotedRaw) errors.push(`${path}.provenance.promotedFrom must be an object`);
7530
8126
  if (promotedRaw) unknownKeys3(promotedRaw, PROMOTION_KEYS, `${path}.provenance.promotedFrom`, errors);
7531
8127
  const promotedFrom = promotedRaw ? {
7532
- chapterId: text3(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
7533
- resourceId: text3(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
7534
- resourceRevision: integer2(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
7535
- documentRevision: integer2(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
7536
- promotedBy: text3(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
7537
- promotedAt: integer2(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
8128
+ chapterId: text4(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
8129
+ resourceId: text4(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
8130
+ resourceRevision: integer3(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
8131
+ documentRevision: integer3(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
8132
+ promotedBy: text4(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
8133
+ promotedAt: integer3(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
7538
8134
  } : void 0;
7539
8135
  const provenance = {
7540
- createdBy: text3(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
7541
- createdAt: integer2(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
7542
- updatedBy: text3(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
7543
- updatedAt: integer2(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
8136
+ createdBy: text4(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
8137
+ createdAt: integer3(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
8138
+ updatedBy: text4(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
8139
+ updatedAt: integer3(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
7544
8140
  ...promotedFrom ? { promotedFrom } : {}
7545
8141
  };
7546
8142
  return { id, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
7547
8143
  }
7548
8144
  function validateBuilderLibraryDocument(value, options = {}) {
7549
8145
  const errors = [];
7550
- const raw = record3(value);
7551
- if (!raw) return { ok: false, errors: ["document must be an object"] };
7552
- unknownKeys3(raw, ROOT_KEYS4, "document", errors);
7553
- if (raw.schemaVersion !== BUILDER_LIBRARY_SCHEMA_VERSION) errors.push("document.schemaVersion must be 1");
7554
- const revision = integer2(raw.revision, "document.revision", errors);
7555
- const generatedAt = integer2(raw.generatedAt, "document.generatedAt", errors, 0);
7556
- const sourceRaw = record3(raw.source);
8146
+ const raw2 = record3(value);
8147
+ if (!raw2) return { ok: false, errors: ["document must be an object"] };
8148
+ unknownKeys3(raw2, ROOT_KEYS4, "document", errors);
8149
+ if (raw2.schemaVersion !== BUILDER_LIBRARY_SCHEMA_VERSION) errors.push("document.schemaVersion must be 1");
8150
+ const revision = integer3(raw2.revision, "document.revision", errors);
8151
+ const generatedAt = integer3(raw2.generatedAt, "document.generatedAt", errors, 0);
8152
+ const sourceRaw = record3(raw2.source);
7557
8153
  if (!sourceRaw) errors.push("document.source must be an object");
7558
8154
  if (sourceRaw) unknownKeys3(sourceRaw, SOURCE_KEYS2, "document.source", errors);
7559
8155
  const sourceType = sourceRaw?.type;
7560
8156
  if (sourceType !== "chapter" && sourceType !== "built_not_found") errors.push("document.source.type must be chapter or built_not_found");
7561
- const sourceAppId = text3(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
8157
+ const sourceAppId = text4(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
7562
8158
  if (sourceAppId && !APP_ID.test(sourceAppId)) errors.push("document.source.appId must be a lowercase app slug");
7563
8159
  if (options.sourceType && sourceType !== options.sourceType) errors.push(`document.source.type must be ${options.sourceType}`);
7564
8160
  if (options.sourceAppId && sourceAppId !== options.sourceAppId) errors.push(`document.source.appId must be ${options.sourceAppId}`);
7565
- const targetRaw = raw.target === void 0 ? null : record3(raw.target);
7566
- if (raw.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
8161
+ const targetRaw = raw2.target === void 0 ? null : record3(raw2.target);
8162
+ if (raw2.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
7567
8163
  if (targetRaw) unknownKeys3(targetRaw, TARGET_KEYS2, "document.target", errors);
7568
8164
  const target = targetRaw ? {
7569
- appId: text3(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
7570
- environment: text3(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
8165
+ appId: text4(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
8166
+ environment: text4(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
7571
8167
  } : void 0;
7572
8168
  if (options.targetAppId && target?.appId !== options.targetAppId) errors.push(`document.target.appId must be ${options.targetAppId}`);
7573
- if (!Array.isArray(raw.resources)) errors.push("document.resources must be an array");
7574
- const inputs = Array.isArray(raw.resources) ? raw.resources : [];
8169
+ if (!Array.isArray(raw2.resources)) errors.push("document.resources must be an array");
8170
+ const inputs = Array.isArray(raw2.resources) ? raw2.resources : [];
7575
8171
  if (inputs.length > MAX_RESOURCES) errors.push(`document.resources may contain at most ${MAX_RESOURCES} items`);
7576
- const resources = inputs.map((item, index) => resourceValue(item, `document.resources[${index}]`, errors)).filter(Boolean);
8172
+ const resources = inputs.map((item, index2) => resourceValue(item, `document.resources[${index2}]`, errors)).filter(Boolean);
7577
8173
  const ids = resources.map(({ id }) => id);
7578
8174
  if (new Set(ids).size !== ids.length) errors.push("document.resources ids must be unique");
7579
8175
  for (const resource of resources) {
@@ -7647,28 +8243,28 @@ function canonicalBuilderLibrary(value) {
7647
8243
  return canonicalValue3(value);
7648
8244
  }
7649
8245
  async function builderLibraryDigest(document) {
7650
- const digest = await crypto.subtle.digest("SHA-256", encoder3.encode(canonicalBuilderLibrary(document)));
7651
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
8246
+ const digest2 = await crypto.subtle.digest("SHA-256", encoder3.encode(canonicalBuilderLibrary(document)));
8247
+ return `sha256:${Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
7652
8248
  }
7653
8249
 
7654
8250
  // src/builder-library-store.ts
7655
8251
  var control2 = (row) => row?.document && typeof row.document === "object" ? row : null;
7656
8252
  var local2 = (row) => row?.document && typeof row.document === "object" ? row : null;
7657
8253
  async function loadActiveBuilderLibraryControls(db, targetId, environment) {
7658
- const rows5 = (await db.query({ builderLibraryControls: { $: { where: { targetId, environment, active: true }, limit: 500 } } })).builderLibraryControls;
7659
- return Array.isArray(rows5) ? rows5.map(control2).filter(Boolean) : [];
8254
+ const rows6 = (await db.query({ builderLibraryControls: { $: { where: { targetId, environment, active: true }, limit: 500 } } })).builderLibraryControls;
8255
+ return Array.isArray(rows6) ? rows6.map(control2).filter(Boolean) : [];
7660
8256
  }
7661
8257
  async function loadLatestBuilderLibraryControl(db, sourceId, targetId, environment) {
7662
- const rows5 = (await db.query({ builderLibraryControls: { $: { where: { sourceId, targetId, environment }, order: { revision: "desc" }, limit: 20 } } })).builderLibraryControls;
7663
- return control2(Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
8258
+ const rows6 = (await db.query({ builderLibraryControls: { $: { where: { sourceId, targetId, environment }, order: { revision: "desc" }, limit: 20 } } })).builderLibraryControls;
8259
+ return control2(Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
7664
8260
  }
7665
8261
  async function loadLocalBuilderLibrary(db, appId, environment) {
7666
8262
  const id = `builder-library-local:${environment}`;
7667
- const rows5 = (await db.query({ builderLibraryLocal: { $: { where: { id }, limit: 1 } } })).builderLibraryLocal;
7668
- return local2(Array.isArray(rows5) ? rows5[0] : void 0);
8263
+ const rows6 = (await db.query({ builderLibraryLocal: { $: { where: { id }, limit: 1 } } })).builderLibraryLocal;
8264
+ return local2(Array.isArray(rows6) ? rows6[0] : void 0);
7669
8265
  }
7670
8266
  async function materializeBuilderLibraryControl(db, envelope, previous, receivedAt) {
7671
- const { document, digest } = envelope;
8267
+ const { document, digest: digest2 } = envelope;
7672
8268
  const target = document.target;
7673
8269
  const id = `builder-library-control:${document.source.appId}:${target.environment}:${document.revision}`;
7674
8270
  const ops = [];
@@ -7679,12 +8275,12 @@ async function materializeBuilderLibraryControl(db, envelope, previous, received
7679
8275
  targetId: target.appId,
7680
8276
  environment: target.environment,
7681
8277
  revision: document.revision,
7682
- digest,
8278
+ digest: digest2,
7683
8279
  document,
7684
8280
  active: true,
7685
8281
  receivedAt
7686
8282
  } });
7687
- await db.transact(ops, { mutationId: `builder-library-control:${document.source.appId}:${target.environment}:${document.revision}:${digest}` });
8283
+ await db.transact(ops, { mutationId: `builder-library-control:${document.source.appId}:${target.environment}:${document.revision}:${digest2}` });
7688
8284
  }
7689
8285
  async function saveLocalBuilderLibrary(db, document, environment, expectedRevision, updatedAt, updatedBy) {
7690
8286
  const id = `builder-library-local:${environment}`;
@@ -7737,7 +8333,7 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
7737
8333
  } catch {
7738
8334
  return json({ ok: false, reason: "invalid_document", errors: ["request body must be JSON"] }, 400);
7739
8335
  }
7740
- const digest = typeof input.digest === "string" ? input.digest : "";
8336
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
7741
8337
  const sourceType = ctx.chapter.mode === "hub" ? "chapter" : "built_not_found";
7742
8338
  const checked = validateBuilderLibraryDocument(input.document, {
7743
8339
  sourceType,
@@ -7751,29 +8347,29 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
7751
8347
  return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global or this Chapter's resources"] }, 400);
7752
8348
  }
7753
8349
  if (!document.target || document.target.environment !== env.ODLA_ENV) return json({ ok: false, reason: "wrong_environment" }, 409);
7754
- if (!digest || await builderLibraryDigest(document) !== digest) return json({ ok: false, reason: "invalid_digest" }, 400);
8350
+ if (!digest2 || await builderLibraryDigest(document) !== digest2) return json({ ok: false, reason: "invalid_digest" }, 400);
7755
8351
  const latest = await loadLatestBuilderLibraryControl(db, document.source.appId, ctx.chapter.id, env.ODLA_ENV);
7756
8352
  if (latest && document.revision < latest.revision) return json({ ok: false, reason: "stale_revision" }, 409);
7757
8353
  if (latest && document.revision === latest.revision) {
7758
- if (latest.digest !== digest) return json({ ok: false, reason: "conflicting_revision" }, 409);
8354
+ if (latest.digest !== digest2) return json({ ok: false, reason: "conflicting_revision" }, 409);
7759
8355
  return json({ ok: true, target: document.target, revision: latest.revision, digest: latest.digest, status: "replayed", acknowledgedAt: latest.receivedAt });
7760
8356
  }
7761
8357
  const receivedAt = Date.now();
7762
8358
  try {
7763
- await materializeBuilderLibraryControl(db, { document, digest }, latest, receivedAt);
8359
+ await materializeBuilderLibraryControl(db, { document, digest: digest2 }, latest, receivedAt);
7764
8360
  } catch {
7765
8361
  return json({ ok: false, reason: "materialization_failed" }, 503);
7766
8362
  }
7767
- return json({ ok: true, target: document.target, revision: document.revision, digest, status: "accepted", acknowledgedAt: receivedAt });
8363
+ return json({ ok: true, target: document.target, revision: document.revision, digest: digest2, status: "accepted", acknowledgedAt: receivedAt });
7768
8364
  };
7769
8365
  function actorFor(user) {
7770
8366
  return user.email ?? user.userId;
7771
8367
  }
7772
8368
  function withLocalAuthority2(value, mode, appId, revision, now, actor, current) {
7773
8369
  if (!value || typeof value !== "object" || Array.isArray(value)) return value;
7774
- const raw = value;
8370
+ const raw2 = value;
7775
8371
  const currentById = new Map((current?.resources ?? []).map((item) => [item.id, item]));
7776
- const resources = Array.isArray(raw.resources) ? raw.resources.map((input) => {
8372
+ const resources = Array.isArray(raw2.resources) ? raw2.resources.map((input) => {
7777
8373
  if (!input || typeof input !== "object" || Array.isArray(input)) return input;
7778
8374
  const item = input;
7779
8375
  const existing = typeof item.id === "string" ? currentById.get(item.id) : void 0;
@@ -7788,9 +8384,9 @@ function withLocalAuthority2(value, mode, appId, revision, now, actor, current)
7788
8384
  ...existing?.provenance.promotedFrom ? { promotedFrom: existing.provenance.promotedFrom } : {}
7789
8385
  }
7790
8386
  };
7791
- }) : raw.resources;
8387
+ }) : raw2.resources;
7792
8388
  return {
7793
- ...raw,
8389
+ ...raw2,
7794
8390
  schemaVersion: 1,
7795
8391
  revision,
7796
8392
  generatedAt: now,
@@ -7871,7 +8467,7 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
7871
8467
  return json({
7872
8468
  mode: ctx.chapter.mode,
7873
8469
  local: local3 ? { revision: local3.revision, document: local3.document } : null,
7874
- sources: controls.map(({ sourceId, revision, digest, document, receivedAt }) => ({ sourceId, revision, digest, document, receivedAt })),
8470
+ sources: controls.map(({ sourceId, revision, digest: digest2, document, receivedAt }) => ({ sourceId, revision, digest: digest2, document, receivedAt })),
7875
8471
  localAuthoring: ctx.chapter.builderLibrary.localAuthoring,
7876
8472
  chapterTargets: ctx.chapter.mode === "hub" ? ctx.chapter.builderLibrary.sources.map((source) => ({ chapterId: source.sourceId })) : [{ chapterId: ctx.chapter.id }],
7877
8473
  visibility: { builtNotFoundSeesChapterSources: ctx.chapter.mode === "hub", chaptersSeeSiblingSources: false }
@@ -7879,11 +8475,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
7879
8475
  }
7880
8476
  if (req.method !== "PUT") return json({ error: "method not allowed" }, 405);
7881
8477
  if (!ctx.chapter.builderLibrary.localAuthoring) return json({ error: "local authoring is disabled" }, 403);
7882
- const raw = await req.text();
7883
- if (raw.length > BODY_CAP2) return json({ error: "request body too large" }, 413);
8478
+ const raw2 = await req.text();
8479
+ if (raw2.length > BODY_CAP2) return json({ error: "request body too large" }, 413);
7884
8480
  let input;
7885
8481
  try {
7886
- input = JSON.parse(raw);
8482
+ input = JSON.parse(raw2);
7887
8483
  } catch {
7888
8484
  return json({ error: "invalid JSON body" }, 400);
7889
8485
  }
@@ -7988,14 +8584,14 @@ function validatedTokens(input) {
7988
8584
  }
7989
8585
  const tokens = {};
7990
8586
  let serializedLength = 0;
7991
- for (const [name, raw] of entries.sort(([left], [right]) => left.localeCompare(right))) {
8587
+ for (const [name, raw2] of entries.sort(([left], [right]) => left.localeCompare(right))) {
7992
8588
  if (!TOKEN_NAME.test(name)) {
7993
8589
  throw new TypeError(`signup-control theme token "${name}" must be an allowlisted --ui-* property`);
7994
8590
  }
7995
- if (typeof raw !== "string" && typeof raw !== "number" || typeof raw === "number" && !Number.isFinite(raw)) {
8591
+ if (typeof raw2 !== "string" && typeof raw2 !== "number" || typeof raw2 === "number" && !Number.isFinite(raw2)) {
7996
8592
  throw new TypeError(`signup-control theme token "${name}" must be a finite string or number`);
7997
8593
  }
7998
- const value = String(raw).trim();
8594
+ const value = String(raw2).trim();
7999
8595
  if (!value || value.length > MAX_VALUE_LENGTH || UNSAFE_VALUE.test(value)) {
8000
8596
  throw new TypeError(`signup-control theme token "${name}" has an unsafe or oversized value`);
8001
8597
  }
@@ -8117,6 +8713,7 @@ var BUILTIN_ROUTES = [
8117
8713
  handleNetworkSnapshot,
8118
8714
  handleNetworkRecords,
8119
8715
  handleNetworkSharedNotes,
8716
+ handleNetworkCommercialStripeWebhook,
8120
8717
  handleFormation,
8121
8718
  handleMember,
8122
8719
  handleMemberContent,
@@ -8157,6 +8754,7 @@ var BUILTIN_ROUTES = [
8157
8754
  handleAdminNetworkNotes,
8158
8755
  handleAdminNetworkSharedNotes,
8159
8756
  handleAdminNetworkPersonRegistry,
8757
+ handleAdminNetworkCommercialReadModel,
8160
8758
  // API requests must never fall through to an SPA asset response. Hosts still
8161
8759
  // get first refusal through options.routes, then this terminates unknown API
8162
8760
  // paths with an explicit machine-readable 404.