@odla-ai/chapter 0.44.0 → 0.46.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.
@@ -4,8 +4,8 @@ import { createRemoteJWKSet, jwtVerify } from "jose";
4
4
 
5
5
  // src/auth.ts
6
6
  function roleFromClaim(payload, auth) {
7
- const raw = payload[auth.claim];
8
- return typeof raw === "string" && auth.ladder.includes(raw) ? raw : auth.ladder[0];
7
+ const raw2 = payload[auth.claim];
8
+ return typeof raw2 === "string" && auth.ladder.includes(raw2) ? raw2 : auth.ladder[0];
9
9
  }
10
10
  function isAdminRole(role, auth) {
11
11
  return role === auth.adminRole;
@@ -188,7 +188,7 @@ function resolveApplication(a) {
188
188
  throw new Error(`defineChapter.application.${name}: field "${unsafe}" must match ${SCHEMA_FIELD.source}`);
189
189
  }
190
190
  }
191
- const duplicate = [...required, ...optional].find((field, index, fields) => fields.indexOf(field) !== index);
191
+ const duplicate = [...required, ...optional].find((field, index2, fields) => fields.indexOf(field) !== index2);
192
192
  if (duplicate) {
193
193
  throw new Error(`defineChapter.application: duplicate field "${duplicate}" across required/optional lists`);
194
194
  }
@@ -336,8 +336,8 @@ function tierPayable(tier, group, hasSecretKey) {
336
336
  function byOrder(a, b) {
337
337
  return a.sortOrder - b.sortOrder || a.id.localeCompare(b.id);
338
338
  }
339
- function resolveTiers(group, rows5 = []) {
340
- const stored = rows5.filter((tier) => tier.active);
339
+ function resolveTiers(group, rows6 = []) {
340
+ const stored = rows6.filter((tier) => tier.active);
341
341
  if (stored.length) return [...stored].sort(byOrder);
342
342
  return [{
343
343
  id: "standard",
@@ -383,15 +383,15 @@ function rowToTier(row, groupId) {
383
383
  }
384
384
  async function loadTiers(db, group) {
385
385
  const groupId = String(group.id ?? "");
386
- let rows5;
386
+ let rows6;
387
387
  try {
388
- rows5 = (await db.query({
388
+ rows6 = (await db.query({
389
389
  tiers: { $: { where: { groupId }, order: { sortOrder: "asc" }, limit: 100 } }
390
390
  })).tiers;
391
391
  } catch {
392
- rows5 = void 0;
392
+ rows6 = void 0;
393
393
  }
394
- const list = Array.isArray(rows5) ? rows5.map((row) => rowToTier(row, groupId)) : [];
394
+ const list = Array.isArray(rows6) ? rows6.map((row) => rowToTier(row, groupId)) : [];
395
395
  return resolveTiers(group, list);
396
396
  }
397
397
  async function loadOfferableTiers(db, group, hasSecretKey) {
@@ -528,8 +528,8 @@ function canonicalSignupControl(value) {
528
528
  return canonicalValue(value);
529
529
  }
530
530
  async function signupControlDigest(document) {
531
- const digest = await crypto.subtle.digest("SHA-256", encoder.encode(canonicalSignupControl(document)));
532
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
531
+ const digest2 = await crypto.subtle.digest("SHA-256", encoder.encode(canonicalSignupControl(document)));
532
+ return `sha256:${Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
533
533
  }
534
534
 
535
535
  // src/admission-grants.ts
@@ -565,24 +565,24 @@ function validateAdmissionGrantDocument(input) {
565
565
  const grants = [];
566
566
  const seenIds = /* @__PURE__ */ new Set();
567
567
  const seenEmails = /* @__PURE__ */ new Set();
568
- for (const [index, raw] of rawGrants.slice(0, MAX_GRANTS).entries()) {
569
- const grant = raw && typeof raw === "object" ? raw : {};
568
+ for (const [index2, raw2] of rawGrants.slice(0, MAX_GRANTS).entries()) {
569
+ const grant = raw2 && typeof raw2 === "object" ? raw2 : {};
570
570
  const id = typeof grant.id === "string" ? grant.id : "";
571
571
  const email = typeof grant.email === "string" ? grant.email.trim() : "";
572
572
  const normalized = normalizeAdmissionEmail(email);
573
573
  const decision = grant.decision;
574
574
  const note = typeof grant.note === "string" ? grant.note.trim() : void 0;
575
575
  const expiresAt = grant.expiresAt === void 0 ? void 0 : Number(grant.expiresAt);
576
- if (!ID.test(id)) errors.push(`document.grants[${index}].id must be an identifier`);
576
+ if (!ID.test(id)) errors.push(`document.grants[${index2}].id must be an identifier`);
577
577
  if (seenIds.has(id)) errors.push(`document.grants repeats id "${id}"`);
578
- if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index}].email must be valid`);
578
+ if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index2}].email must be valid`);
579
579
  if (seenEmails.has(normalized)) errors.push(`document.grants repeats email "${normalized}"`);
580
580
  if (decision !== "waive_interview" && decision !== "require_interview") {
581
- errors.push(`document.grants[${index}].decision is invalid`);
581
+ errors.push(`document.grants[${index2}].decision is invalid`);
582
582
  }
583
- if (note && note.length > MAX_NOTE) errors.push(`document.grants[${index}].note is too long`);
583
+ if (note && note.length > MAX_NOTE) errors.push(`document.grants[${index2}].note is too long`);
584
584
  if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= 0)) {
585
- errors.push(`document.grants[${index}].expiresAt must be epoch milliseconds`);
585
+ errors.push(`document.grants[${index2}].expiresAt must be epoch milliseconds`);
586
586
  }
587
587
  seenIds.add(id);
588
588
  seenEmails.add(normalized);
@@ -650,7 +650,7 @@ async function loadLatestAdmissionRevision(db, sourceId, targetId, environment)
650
650
  return row ? row : null;
651
651
  }
652
652
  async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
653
- const { document, digest } = envelope;
653
+ const { document, digest: digest2 } = envelope;
654
654
  const current = rows((await db.query({ admissionGrants: { $: {
655
655
  where: { sourceScope: "parent", sourceId: document.source.appId, targetId: document.target.chapterId },
656
656
  limit: MAX_GRANTS
@@ -702,11 +702,11 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
702
702
  targetId: document.target.chapterId,
703
703
  environment: document.target.environment,
704
704
  revision: document.revision,
705
- digest,
705
+ digest: digest2,
706
706
  active: true,
707
707
  receivedAt
708
708
  } });
709
- await db.transact(ops, { mutationId: `admission-grants:${document.source.appId}:${document.target.environment}:${document.revision}:${digest}` });
709
+ await db.transact(ops, { mutationId: `admission-grants:${document.source.appId}:${document.target.environment}:${document.revision}:${digest2}` });
710
710
  }
711
711
 
712
712
  // src/flow-fields.ts
@@ -724,8 +724,8 @@ var APPLICATION_LABELS = {
724
724
  state: "State"
725
725
  };
726
726
  function humanize(id) {
727
- const text4 = id.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
728
- return text4 ? `${text4[0].toUpperCase()}${text4.slice(1)}` : id;
727
+ const text6 = id.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
728
+ return text6 ? `${text6[0].toUpperCase()}${text6.slice(1)}` : id;
729
729
  }
730
730
  function applicationDefs(chapter) {
731
731
  try {
@@ -740,7 +740,7 @@ function applicationType(id, def) {
740
740
  }
741
741
  function applicationFlowFields(chapter, application = chapter.application) {
742
742
  const defs = applicationDefs(chapter);
743
- return [...application.required, ...application.optional].map((id, order) => {
743
+ return [...application.required, ...application.optional].map((id, order2) => {
744
744
  const def = defs[id];
745
745
  const conditions = application.conditions[id];
746
746
  const type = applicationType(id, def);
@@ -749,7 +749,7 @@ function applicationFlowFields(chapter, application = chapter.application) {
749
749
  label: def?.label ?? APPLICATION_LABELS[id] ?? humanize(id),
750
750
  type,
751
751
  required: application.required.includes(id),
752
- order,
752
+ order: order2,
753
753
  maxLength: application.maxLen[id] ?? application.defaultMaxLen,
754
754
  ...type === "enum" && def?.options ? { options: [...def.options] } : {},
755
755
  ...conditions ? { conditions: { ...conditions } } : {},
@@ -760,14 +760,14 @@ function applicationFlowFields(chapter, application = chapter.application) {
760
760
  function resolveFormationFlowFields(crm, formation) {
761
761
  if (!formation.enabled) return [];
762
762
  const def = crm.type(formation.type);
763
- return [...formation.required, ...formation.optional].map((id, order) => {
763
+ return [...formation.required, ...formation.optional].map((id, order2) => {
764
764
  const field = def.fields[id];
765
765
  return {
766
766
  id,
767
767
  label: field.label ?? humanize(id),
768
768
  type: field.type,
769
769
  required: formation.required.includes(id),
770
- order,
770
+ order: order2,
771
771
  maxLength: formation.maxLen[id] ?? formation.defaultMaxLen,
772
772
  ...field.options ? { options: [...field.options] } : {},
773
773
  ...id === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
@@ -811,9 +811,9 @@ function validateGiftOfferCatalog(input, tierIds) {
811
811
  if (rawOffers.length > MAX_GIFT_OFFERS) errors.push(`document.giftOffers.offers supports at most ${MAX_GIFT_OFFERS} entries`);
812
812
  const ids = /* @__PURE__ */ new Set();
813
813
  const offers = [];
814
- for (const [index, raw] of rawOffers.slice(0, MAX_GIFT_OFFERS).entries()) {
815
- const path = `document.giftOffers.offers[${index}]`;
816
- const item = record(raw);
814
+ for (const [index2, raw2] of rawOffers.slice(0, MAX_GIFT_OFFERS).entries()) {
815
+ const path = `document.giftOffers.offers[${index2}]`;
816
+ const item = record(raw2);
817
817
  if (!item) {
818
818
  errors.push(`${path} must be an object`);
819
819
  continue;
@@ -975,8 +975,8 @@ function networkSourceTag(type, hubRecordId, sourceId) {
975
975
  const typeKey = type.toLowerCase();
976
976
  const readable = /^[a-z0-9_-]+$/.test(hubRecordId);
977
977
  const prefix = sourceId ? `network:${sourceId.toLowerCase()}:${typeKey}` : `network:${typeKey}`;
978
- const raw = `${prefix}:${hubRecordId}`;
979
- if (readable && raw.length <= 64) return raw;
978
+ const raw2 = `${prefix}:${hubRecordId}`;
979
+ if (readable && raw2.length <= 64) return raw2;
980
980
  return `network:${typeKey.slice(0, 16)}:${shortHash(`${sourceId ?? ""}\0${type}\0${hubRecordId}`)}`;
981
981
  }
982
982
  function normalizeSharedRecord(record4, fallbackSourceId = "legacy-source") {
@@ -1260,10 +1260,10 @@ function planDelivery(input) {
1260
1260
  const transport = !isProd && !redirect ? "log-only" : input.cloudflareReady ? "cloudflare" : "log-only";
1261
1261
  const to = redirect ? input.group.debugEmail : input.to;
1262
1262
  const subject = (redirect ? "[dev] " : "") + render(tpl.subject, vars);
1263
- const text4 = redirect ? `(dev redirect; original recipient: ${input.to})
1263
+ const text6 = redirect ? `(dev redirect; original recipient: ${input.to})
1264
1264
 
1265
1265
  ` + render(tpl.text, vars) : render(tpl.text, vars);
1266
- return { deliver: true, transport, to, subject, text: text4, redirected: redirect };
1266
+ return { deliver: true, transport, to, subject, text: text6, redirected: redirect };
1267
1267
  }
1268
1268
 
1269
1269
  // src/notify.ts
@@ -1450,27 +1450,27 @@ async function hasSignupControl(db, targetId, environment, active) {
1450
1450
  if (db.aggregate) {
1451
1451
  return Number((await db.aggregate("signupControls", { count: true }, { where })).count ?? 0) > 0;
1452
1452
  }
1453
- const rows5 = (await db.query({ signupControls: {} })).signupControls;
1454
- return Array.isArray(rows5) && rows5.some((row) => row.targetId === targetId && row.environment === environment && (!active || row.active === true));
1453
+ const rows6 = (await db.query({ signupControls: {} })).signupControls;
1454
+ return Array.isArray(rows6) && rows6.some((row) => row.targetId === targetId && row.environment === environment && (!active || row.active === true));
1455
1455
  }
1456
1456
  async function loadLatestSignupControl(db, targetId, environment) {
1457
1457
  if (!await hasSignupControl(db, targetId, environment)) return null;
1458
- const rows5 = (await db.query({ signupControls: { $: {
1458
+ const rows6 = (await db.query({ signupControls: { $: {
1459
1459
  where: { targetId, environment },
1460
1460
  order: { revision: "desc" },
1461
1461
  limit: 1
1462
1462
  } } })).signupControls;
1463
- const latest = Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1463
+ const latest = Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1464
1464
  return asStored(latest);
1465
1465
  }
1466
1466
  async function loadActiveSignupControl(db, targetId, environment) {
1467
1467
  if (!await hasSignupControl(db, targetId, environment, true)) return null;
1468
- const rows5 = (await db.query({ signupControls: { $: {
1468
+ const rows6 = (await db.query({ signupControls: { $: {
1469
1469
  where: { targetId, environment, active: true },
1470
1470
  order: { revision: "desc" },
1471
1471
  limit: 1
1472
1472
  } } })).signupControls;
1473
- const active = Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1473
+ const active = Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision ?? 0) - Number(a.revision ?? 0))[0] : void 0;
1474
1474
  return asStored(active);
1475
1475
  }
1476
1476
  function controlledApplication(base, document) {
@@ -1499,7 +1499,7 @@ var byPublicId = (ns, value) => ({
1499
1499
  value
1500
1500
  });
1501
1501
  async function materializeSignupControl(db, envelope, previous, receivedAt) {
1502
- const { document, digest } = envelope;
1502
+ const { document, digest: digest2 } = envelope;
1503
1503
  const currentRows = (await db.query({ tiers: { $: {
1504
1504
  where: { groupId: document.target.chapterId },
1505
1505
  limit: 100
@@ -1550,12 +1550,12 @@ async function materializeSignupControl(db, envelope, previous, receivedAt) {
1550
1550
  targetId: document.target.chapterId,
1551
1551
  environment: document.target.environment,
1552
1552
  revision: document.revision,
1553
- digest,
1553
+ digest: digest2,
1554
1554
  document,
1555
1555
  active: true,
1556
1556
  receivedAt
1557
1557
  } });
1558
- await db.transact(ops, { mutationId: `signup-control:${document.target.environment}:${document.revision}:${digest}` });
1558
+ await db.transact(ops, { mutationId: `signup-control:${document.target.environment}:${document.revision}:${digest2}` });
1559
1559
  }
1560
1560
 
1561
1561
  // src/session.ts
@@ -1995,11 +1995,11 @@ var handleMember = async (req, url, env, ctx) => {
1995
1995
  return json({ error: `application cannot resume from status "${String(app.status ?? "")}"` }, 409);
1996
1996
  }
1997
1997
  if (req.method === "POST" && url.pathname === "/api/applications") {
1998
- const raw = await req.text();
1999
- if (raw.length > chapter.application.bodyCap) return json({ error: "request body too large" }, 413);
1998
+ const raw2 = await req.text();
1999
+ if (raw2.length > chapter.application.bodyCap) return json({ error: "request body too large" }, 413);
2000
2000
  let parsed;
2001
2001
  try {
2002
- parsed = JSON.parse(raw);
2002
+ parsed = JSON.parse(raw2);
2003
2003
  } catch {
2004
2004
  return json({ error: "invalid JSON body" }, 400);
2005
2005
  }
@@ -2056,23 +2056,23 @@ var handleNetworkShared = async (req, url, env, ctx) => {
2056
2056
  const db = ctx.makeDb(env);
2057
2057
  const secret = await getVaultSecret(db, "network_share_secret");
2058
2058
  if (!secret) return json({ error: "unauthorized" }, 401);
2059
- let raw;
2059
+ let raw2;
2060
2060
  let sourceId;
2061
2061
  if (req.headers.has(FEDERATION_HEADERS.version)) {
2062
2062
  const verified = await verifyFederatedRequest(req, { secret });
2063
2063
  if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
2064
- raw = verified.body;
2064
+ raw2 = verified.body;
2065
2065
  sourceId = verified.sender;
2066
2066
  } else {
2067
2067
  const provided = (req.headers.get("authorization") ?? "").replace(/^Bearer /, "");
2068
2068
  if (provided.length !== secret.length || provided !== secret) {
2069
2069
  return json({ error: "unauthorized" }, 401);
2070
2070
  }
2071
- raw = await req.text();
2071
+ raw2 = await req.text();
2072
2072
  }
2073
2073
  let payload;
2074
2074
  try {
2075
- payload = JSON.parse(raw);
2075
+ payload = JSON.parse(raw2);
2076
2076
  } catch {
2077
2077
  return json({ error: "invalid JSON body" }, 400);
2078
2078
  }
@@ -2151,10 +2151,10 @@ function boundedString(value, path, errors, max, required = false) {
2151
2151
  errors.push(`${path} must be a string`);
2152
2152
  return void 0;
2153
2153
  }
2154
- const text4 = value.trim();
2155
- if (required && !text4) errors.push(`${path} is required`);
2156
- if (text4.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
2157
- return text4;
2154
+ const text6 = value.trim();
2155
+ if (required && !text6) errors.push(`${path} is required`);
2156
+ if (text6.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
2157
+ return text6;
2158
2158
  }
2159
2159
  function validateMembershipPageDocument(input, options = {}) {
2160
2160
  const errors = [];
@@ -2172,9 +2172,9 @@ function validateMembershipPageDocument(input, options = {}) {
2172
2172
  const authority = new Map((options.tiers ?? []).map((tier) => [tier.id, tier]));
2173
2173
  const seen = /* @__PURE__ */ new Set();
2174
2174
  const tiers = [];
2175
- for (const [index, raw] of rawTiers.slice(0, MAX_TIERS).entries()) {
2176
- const path = `document.tiers[${index}]`;
2177
- const item = objectRecord(raw);
2175
+ for (const [index2, raw2] of rawTiers.slice(0, MAX_TIERS).entries()) {
2176
+ const path = `document.tiers[${index2}]`;
2177
+ const item = objectRecord(raw2);
2178
2178
  if (!item) {
2179
2179
  errors.push(`${path} must be an object`);
2180
2180
  continue;
@@ -2203,8 +2203,8 @@ function validateMembershipPageDocument(input, options = {}) {
2203
2203
  else {
2204
2204
  if (item.benefits.length > MAX_BENEFITS) errors.push(`${path}.benefits supports at most ${MAX_BENEFITS} entries`);
2205
2205
  benefits = item.benefits.slice(0, MAX_BENEFITS).flatMap((benefit, benefitIndex) => {
2206
- const text4 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
2207
- return text4 ? [text4] : [];
2206
+ const text6 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
2207
+ return text6 ? [text6] : [];
2208
2208
  });
2209
2209
  }
2210
2210
  }
@@ -2313,9 +2313,9 @@ function tiersOf(value, errors) {
2313
2313
  }
2314
2314
  if (value.length > 20) errors.push("document.tiers supports at most 20 entries");
2315
2315
  const seen = /* @__PURE__ */ new Set();
2316
- return value.slice(0, 20).flatMap((raw, index) => {
2317
- const path = `document.tiers[${index}]`;
2318
- const item = recordOf(raw);
2316
+ return value.slice(0, 20).flatMap((raw2, index2) => {
2317
+ const path = `document.tiers[${index2}]`;
2318
+ const item = recordOf(raw2);
2319
2319
  if (!item) {
2320
2320
  errors.push(`${path} must be an object`);
2321
2321
  return [];
@@ -2442,7 +2442,7 @@ function validateSignupControlDocument(input) {
2442
2442
  const available = tiers.filter((tier) => tier.available).sort((a, b) => a.sortOrder - b.sortOrder);
2443
2443
  const page = validateMembershipPageDocument(root.membershipPage, { tiers: available });
2444
2444
  if (!page.ok) errors.push(...page.errors);
2445
- else if (page.value.tiers.some((tier, index) => tier.tierId !== available[index]?.id)) {
2445
+ else if (page.value.tiers.some((tier, index2) => tier.tierId !== available[index2]?.id)) {
2446
2446
  errors.push("document.membershipPage.tiers must follow the available tier display order");
2447
2447
  }
2448
2448
  const application = applicationOf(root.application, errors);
@@ -2490,7 +2490,7 @@ var handleSignupControl = async (req, url, env, ctx) => {
2490
2490
  } catch {
2491
2491
  return reject("invalid_document", 400, ["request body must be JSON"]);
2492
2492
  }
2493
- const digest = typeof input.digest === "string" ? input.digest : "";
2493
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
2494
2494
  const checked = validateSignupControlDocument(input.document);
2495
2495
  if (!checked.ok) return reject("invalid_document", 400, checked.errors);
2496
2496
  const document = checked.value;
@@ -2500,11 +2500,11 @@ var handleSignupControl = async (req, url, env, ctx) => {
2500
2500
  if (document.target.stripeMode !== authority.stripeMode) return reject("wrong_stripe_mode", 409);
2501
2501
  const applicationErrors = validateControlledApplication(ctx.chapter, document);
2502
2502
  if (applicationErrors.length) return reject("invalid_document", 400, applicationErrors);
2503
- if (!digest || await signupControlDigest(document) !== digest) return reject("invalid_digest", 400);
2503
+ if (!digest2 || await signupControlDigest(document) !== digest2) return reject("invalid_digest", 400);
2504
2504
  const latest = await loadLatestSignupControl(db, ctx.chapter.id, env.ODLA_ENV);
2505
2505
  if (latest && document.revision < latest.revision) return reject("stale_revision", 409);
2506
2506
  if (latest && document.revision === latest.revision) {
2507
- if (latest.digest !== digest) return reject("conflicting_revision", 409);
2507
+ if (latest.digest !== digest2) return reject("conflicting_revision", 409);
2508
2508
  return json({
2509
2509
  ok: true,
2510
2510
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
@@ -2516,7 +2516,7 @@ var handleSignupControl = async (req, url, env, ctx) => {
2516
2516
  }
2517
2517
  const receivedAt = Date.now();
2518
2518
  try {
2519
- await materializeSignupControl(db, { digest, document }, latest, receivedAt);
2519
+ await materializeSignupControl(db, { digest: digest2, document }, latest, receivedAt);
2520
2520
  } catch {
2521
2521
  return reject("materialization_failed", 503);
2522
2522
  }
@@ -2524,7 +2524,7 @@ var handleSignupControl = async (req, url, env, ctx) => {
2524
2524
  ok: true,
2525
2525
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
2526
2526
  revision: document.revision,
2527
- digest,
2527
+ digest: digest2,
2528
2528
  status: "accepted",
2529
2529
  acknowledgedAt: receivedAt
2530
2530
  });
@@ -2552,8 +2552,8 @@ function canonicalMemberContent(value) {
2552
2552
  return canonicalValue2(value);
2553
2553
  }
2554
2554
  async function memberContentDigest(document) {
2555
- const digest = await crypto.subtle.digest("SHA-256", encoder2.encode(canonicalMemberContent(document)));
2556
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
2555
+ const digest2 = await crypto.subtle.digest("SHA-256", encoder2.encode(canonicalMemberContent(document)));
2556
+ return `sha256:${Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
2557
2557
  }
2558
2558
 
2559
2559
  // src/member-content-validation-helpers.ts
@@ -2590,21 +2590,21 @@ function textValue(value, path, errors, max = MAX_TEXT, required = false) {
2590
2590
  return valueText;
2591
2591
  }
2592
2592
  function offerValue(value, path, errors, tiers, audience) {
2593
- const raw = record2(value);
2594
- if (!raw) {
2593
+ const raw2 = record2(value);
2594
+ if (!raw2) {
2595
2595
  errors.push(`${path} must be an object`);
2596
2596
  return void 0;
2597
2597
  }
2598
- unknownKeys2(raw, OFFER_KEYS, path, errors);
2599
- const tierId = textValue(raw.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2598
+ unknownKeys2(raw2, OFFER_KEYS, path, errors);
2599
+ const tierId = textValue(raw2.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2600
2600
  if (tierId && !ID3.test(tierId)) errors.push(`${path}.tierId must be a lowercase kebab-case id`);
2601
2601
  if (tiers.size && !tiers.has(tierId)) errors.push(`${path}.tierId must reference a declared tier`);
2602
2602
  if (audience?.kind === "tiers" && tierId && !audience.tierIds.includes(tierId)) {
2603
2603
  errors.push(`${path}.tierId must grant access to this page`);
2604
2604
  }
2605
- const label = textValue(raw.label, `${path}.label`, errors);
2606
- const bodyMarkdown = textValue(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2);
2607
- const ctaLabel = textValue(raw.ctaLabel, `${path}.ctaLabel`, errors);
2605
+ const label = textValue(raw2.label, `${path}.label`, errors);
2606
+ const bodyMarkdown = textValue(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2);
2607
+ const ctaLabel = textValue(raw2.ctaLabel, `${path}.ctaLabel`, errors);
2608
2608
  return {
2609
2609
  tierId,
2610
2610
  ...label ? { label } : {},
@@ -2656,37 +2656,37 @@ function validateMemberContentDocument(input, options = {}) {
2656
2656
  const ids = /* @__PURE__ */ new Set();
2657
2657
  const slugs = /* @__PURE__ */ new Set();
2658
2658
  const pages = [];
2659
- for (const [index, value] of rawPages.slice(0, MAX_PAGES).entries()) {
2660
- const path = `document.pages[${index}]`;
2661
- const raw = record2(value);
2662
- if (!raw) {
2659
+ for (const [index2, value] of rawPages.slice(0, MAX_PAGES).entries()) {
2660
+ const path = `document.pages[${index2}]`;
2661
+ const raw2 = record2(value);
2662
+ if (!raw2) {
2663
2663
  errors.push(`${path} must be an object`);
2664
2664
  continue;
2665
2665
  }
2666
- unknownKeys2(raw, PAGE_KEYS, path, errors);
2667
- const id = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2668
- const slug = textValue(raw.slug, `${path}.slug`, errors, 120, true) ?? "";
2669
- const title = textValue(raw.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
2666
+ unknownKeys2(raw2, PAGE_KEYS, path, errors);
2667
+ const id = textValue(raw2.id, `${path}.id`, errors, 80, true) ?? "";
2668
+ const slug = textValue(raw2.slug, `${path}.slug`, errors, 120, true) ?? "";
2669
+ const title = textValue(raw2.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
2670
2670
  if (id && !ID3.test(id)) errors.push(`${path}.id must be a lowercase kebab-case id`);
2671
2671
  if (slug && !SLUG2.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
2672
2672
  if (ids.has(id) || knownParentIds.has(id)) errors.push(`${path}.id duplicates page "${id}"`);
2673
2673
  if (slugs.has(slug) || (options.parentPages ?? []).some((page) => page.slug === slug)) errors.push(`${path}.slug duplicates "${slug}"`);
2674
2674
  ids.add(id);
2675
2675
  slugs.add(slug);
2676
- const navRaw = record2(raw.navigation);
2677
- let navigation = { section: "General", order: index };
2676
+ const navRaw = record2(raw2.navigation);
2677
+ let navigation = { section: "General", order: index2 };
2678
2678
  if (!navRaw) errors.push(`${path}.navigation must be an object`);
2679
2679
  else {
2680
2680
  unknownKeys2(navRaw, NAV_KEYS, `${path}.navigation`, errors);
2681
2681
  const section = textValue(navRaw.section, `${path}.navigation.section`, errors, 120, true) ?? "";
2682
- const order = navRaw.order;
2683
- if (!Number.isInteger(order) || Number(order) < 0 || Number(order) > 1e6) {
2682
+ const order2 = navRaw.order;
2683
+ if (!Number.isInteger(order2) || Number(order2) < 0 || Number(order2) > 1e6) {
2684
2684
  errors.push(`${path}.navigation.order must be an integer from 0 to 1,000,000`);
2685
2685
  }
2686
2686
  const parentId = textValue(navRaw.parentId, `${path}.navigation.parentId`, errors, 80);
2687
- navigation = { section, order: Number.isInteger(order) ? Number(order) : index, ...parentId ? { parentId } : {} };
2687
+ navigation = { section, order: Number.isInteger(order2) ? Number(order2) : index2, ...parentId ? { parentId } : {} };
2688
2688
  }
2689
- const audienceRaw = record2(raw.audience);
2689
+ const audienceRaw = record2(raw2.audience);
2690
2690
  let audience = { kind: "all-members" };
2691
2691
  if (!audienceRaw) errors.push(`${path}.audience must be an object`);
2692
2692
  else {
@@ -2708,12 +2708,12 @@ function validateMemberContentDocument(input, options = {}) {
2708
2708
  audience = { kind: "tiers", tierIds: normalized };
2709
2709
  } else errors.push(`${path}.audience.kind must be all-members or tiers`);
2710
2710
  }
2711
- const summary = textValue(raw.summary, `${path}.summary`, errors, 2e3);
2712
- const teaserMarkdown = textValue(raw.teaserMarkdown, `${path}.teaserMarkdown`, errors, MAX_MARKDOWN2);
2713
- const bodyMarkdown = textValue(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, true) ?? "";
2711
+ const summary = textValue(raw2.summary, `${path}.summary`, errors, 2e3);
2712
+ const teaserMarkdown = textValue(raw2.teaserMarkdown, `${path}.teaserMarkdown`, errors, MAX_MARKDOWN2);
2713
+ const bodyMarkdown = textValue(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, true) ?? "";
2714
2714
  let upsell;
2715
- if (raw.upsell !== void 0) {
2716
- const upsellRaw = record2(raw.upsell);
2715
+ if (raw2.upsell !== void 0) {
2716
+ const upsellRaw = record2(raw2.upsell);
2717
2717
  if (!upsellRaw) errors.push(`${path}.upsell must be an object`);
2718
2718
  else {
2719
2719
  unknownKeys2(upsellRaw, UPSELL_KEYS, `${path}.upsell`, errors);
@@ -2730,10 +2730,10 @@ function validateMemberContentDocument(input, options = {}) {
2730
2730
  pages.push({ id, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2731
2731
  }
2732
2732
  const allIds = /* @__PURE__ */ new Set([...knownParentIds, ...ids]);
2733
- for (const [index, page] of pages.entries()) {
2733
+ for (const [index2, page] of pages.entries()) {
2734
2734
  const parentId = page.navigation.parentId;
2735
- if (parentId && !allIds.has(parentId)) errors.push(`document.pages[${index}].navigation.parentId references unknown page "${parentId}"`);
2736
- if (parentId === page.id) errors.push(`document.pages[${index}].navigation.parentId cannot reference itself`);
2735
+ if (parentId && !allIds.has(parentId)) errors.push(`document.pages[${index2}].navigation.parentId references unknown page "${parentId}"`);
2736
+ if (parentId === page.id) errors.push(`document.pages[${index2}].navigation.parentId cannot reference itself`);
2737
2737
  }
2738
2738
  for (const page of pages) {
2739
2739
  const seen = /* @__PURE__ */ new Set([page.id]);
@@ -2754,21 +2754,21 @@ function validateMemberContentDocument(input, options = {}) {
2754
2754
  const values = Array.isArray(root.upsellOverrides) ? root.upsellOverrides : [];
2755
2755
  const seen = /* @__PURE__ */ new Set();
2756
2756
  upsellOverrides = [];
2757
- for (const [index, value] of values.entries()) {
2758
- const path = `document.upsellOverrides[${index}]`;
2759
- const raw = record2(value);
2760
- if (!raw) {
2757
+ for (const [index2, value] of values.entries()) {
2758
+ const path = `document.upsellOverrides[${index2}]`;
2759
+ const raw2 = record2(value);
2760
+ if (!raw2) {
2761
2761
  errors.push(`${path} must be an object`);
2762
2762
  continue;
2763
2763
  }
2764
- unknownKeys2(raw, OVERRIDE_KEYS, path, errors);
2765
- const pageId = textValue(raw.pageId, `${path}.pageId`, errors, 80, true) ?? "";
2764
+ unknownKeys2(raw2, OVERRIDE_KEYS, path, errors);
2765
+ const pageId = textValue(raw2.pageId, `${path}.pageId`, errors, 80, true) ?? "";
2766
2766
  if (seen.has(pageId)) errors.push(`${path}.pageId duplicates "${pageId}"`);
2767
2767
  seen.add(pageId);
2768
2768
  const parentPage = (options.parentPages ?? []).find((page) => page.id === pageId);
2769
2769
  if (!parentPage) errors.push(`${path}.pageId must reference a parent page`);
2770
2770
  else if (parentPage.upsell?.owner !== "chapter") errors.push(`${path}.pageId does not delegate upsell authority to the chapter`);
2771
- const offer = offerValue(raw.offer, `${path}.offer`, errors, tierIds, parentPage?.audience);
2771
+ const offer = offerValue(raw2.offer, `${path}.offer`, errors, tierIds, parentPage?.audience);
2772
2772
  if (offer) upsellOverrides.push({ pageId, offer });
2773
2773
  }
2774
2774
  }
@@ -2846,36 +2846,36 @@ var control = (row) => row && row.document && typeof row.document === "object" ?
2846
2846
  var local = (row) => row && row.document && typeof row.document === "object" ? row : null;
2847
2847
  async function exists(db, ns, where) {
2848
2848
  if (db.aggregate) return Number((await db.aggregate(ns, { count: true }, { where })).count ?? 0) > 0;
2849
- const rows5 = (await db.query({ [ns]: {} }))[ns] ?? [];
2850
- return rows5.some((row) => Object.entries(where).every(([key, value]) => row[key] === value));
2849
+ const rows6 = (await db.query({ [ns]: {} }))[ns] ?? [];
2850
+ return rows6.some((row) => Object.entries(where).every(([key, value]) => row[key] === value));
2851
2851
  }
2852
2852
  async function loadActiveMemberContentControl(db, targetId, environment) {
2853
2853
  const where = { targetId, environment, active: true };
2854
2854
  if (!await exists(db, "memberContentControls", where)) return null;
2855
- const rows5 = (await db.query({ memberContentControls: { $: {
2855
+ const rows6 = (await db.query({ memberContentControls: { $: {
2856
2856
  where,
2857
2857
  order: { revision: "desc" },
2858
2858
  limit: 1
2859
2859
  } } })).memberContentControls;
2860
- return control(Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2860
+ return control(Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2861
2861
  }
2862
2862
  async function loadLatestMemberContentControl(db, targetId, environment) {
2863
2863
  const where = { targetId, environment };
2864
2864
  if (!await exists(db, "memberContentControls", where)) return null;
2865
- const rows5 = (await db.query({ memberContentControls: { $: {
2865
+ const rows6 = (await db.query({ memberContentControls: { $: {
2866
2866
  where,
2867
2867
  order: { revision: "desc" },
2868
2868
  limit: 1
2869
2869
  } } })).memberContentControls;
2870
- return control(Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2870
+ return control(Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
2871
2871
  }
2872
2872
  async function loadLocalMemberContent(db, chapterId, environment) {
2873
2873
  const id = `member-content-local:${environment}`;
2874
- const rows5 = (await db.query({ memberContentLocal: { $: { where: { id }, limit: 1 } } })).memberContentLocal;
2875
- return local(Array.isArray(rows5) ? rows5[0] : void 0);
2874
+ const rows6 = (await db.query({ memberContentLocal: { $: { where: { id }, limit: 1 } } })).memberContentLocal;
2875
+ return local(Array.isArray(rows6) ? rows6[0] : void 0);
2876
2876
  }
2877
2877
  async function materializeMemberContentControl(db, envelope, previous, receivedAt) {
2878
- const { document, digest } = envelope;
2878
+ const { document, digest: digest2 } = envelope;
2879
2879
  const target = document.target;
2880
2880
  const id = `member-content-control:${target.environment}:${document.revision}`;
2881
2881
  const ops = [];
@@ -2888,12 +2888,12 @@ async function materializeMemberContentControl(db, envelope, previous, receivedA
2888
2888
  targetId: target.chapterId,
2889
2889
  environment: target.environment,
2890
2890
  revision: document.revision,
2891
- digest,
2891
+ digest: digest2,
2892
2892
  document,
2893
2893
  active: true,
2894
2894
  receivedAt
2895
2895
  } });
2896
- await db.transact(ops, { mutationId: `member-content-control:${target.environment}:${document.revision}:${digest}` });
2896
+ await db.transact(ops, { mutationId: `member-content-control:${target.environment}:${document.revision}:${digest2}` });
2897
2897
  }
2898
2898
  async function saveLocalMemberContent(db, document, environment, expectedRevision, updatedAt, updatedBy) {
2899
2899
  const id = `member-content-local:${environment}`;
@@ -2927,12 +2927,12 @@ async function groupAndTiers(db, chapterId) {
2927
2927
  }
2928
2928
  async function memberTierId(db, chapterId, email) {
2929
2929
  if (!email) return null;
2930
- let rows5 = (await db.query({ applications: { $: {
2930
+ let rows6 = (await db.query({ applications: { $: {
2931
2931
  where: { emailNormalized: normalizeAdmissionEmail(email) },
2932
2932
  limit: 20
2933
2933
  } } })).applications;
2934
- if (!rows5?.length) rows5 = (await db.query({ applications: { $: { where: { email }, limit: 20 } } })).applications;
2935
- 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;
2934
+ if (!rows6?.length) rows6 = (await db.query({ applications: { $: { where: { email }, limit: 20 } } })).applications;
2935
+ 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;
2936
2936
  return typeof latest?.tierId === "string" && latest.tierId ? latest.tierId : null;
2937
2937
  }
2938
2938
  async function activeDocuments(db, chapterId, environment) {
@@ -2957,7 +2957,7 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
2957
2957
  } catch {
2958
2958
  return reject2("invalid_document", 400, ["request body must be JSON"]);
2959
2959
  }
2960
- const digest = typeof input.digest === "string" ? input.digest : "";
2960
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
2961
2961
  const initial = validateMemberContentDocument(input.document, {
2962
2962
  sourceType: "parent"
2963
2963
  });
@@ -2985,11 +2985,11 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
2985
2985
  return reject2("invalid_document", 409, localCheck.errors.map((error) => `existing chapter content: ${error}`));
2986
2986
  }
2987
2987
  }
2988
- if (!digest || await memberContentDigest(document) !== digest) return reject2("invalid_digest", 400);
2988
+ if (!digest2 || await memberContentDigest(document) !== digest2) return reject2("invalid_digest", 400);
2989
2989
  const latest = await loadLatestMemberContentControl(db, ctx.chapter.id, env.ODLA_ENV);
2990
2990
  if (latest && document.revision < latest.revision) return reject2("stale_revision", 409);
2991
2991
  if (latest && document.revision === latest.revision) {
2992
- if (latest.digest !== digest) return reject2("conflicting_revision", 409);
2992
+ if (latest.digest !== digest2) return reject2("conflicting_revision", 409);
2993
2993
  return json({
2994
2994
  ok: true,
2995
2995
  target: document.target,
@@ -3001,7 +3001,7 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
3001
3001
  }
3002
3002
  const receivedAt = Date.now();
3003
3003
  try {
3004
- await materializeMemberContentControl(db, { digest, document }, latest, receivedAt);
3004
+ await materializeMemberContentControl(db, { digest: digest2, document }, latest, receivedAt);
3005
3005
  } catch {
3006
3006
  return reject2("materialization_failed", 503);
3007
3007
  }
@@ -3009,7 +3009,7 @@ var handleMemberContentControl = async (req, url, env, ctx) => {
3009
3009
  ok: true,
3010
3010
  target: document.target,
3011
3011
  revision: document.revision,
3012
- digest,
3012
+ digest: digest2,
3013
3013
  status: "accepted",
3014
3014
  acknowledgedAt: receivedAt
3015
3015
  });
@@ -3061,11 +3061,11 @@ var handleAdminMemberContent = async (req, url, env, ctx) => {
3061
3061
  }
3062
3062
  if (req.method !== "PUT") return json({ error: "method not allowed" }, 405);
3063
3063
  if (!ctx.chapter.memberContent.localAuthoring) return json({ error: "local authoring is disabled" }, 403);
3064
- const raw = await req.text();
3065
- if (raw.length > BODY_CAP) return json({ error: "request body too large" }, 413);
3064
+ const raw2 = await req.text();
3065
+ if (raw2.length > BODY_CAP) return json({ error: "request body too large" }, 413);
3066
3066
  let input;
3067
3067
  try {
3068
- input = JSON.parse(raw);
3068
+ input = JSON.parse(raw2);
3069
3069
  } catch {
3070
3070
  return json({ error: "invalid JSON body" }, 400);
3071
3071
  }
@@ -3133,18 +3133,18 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3133
3133
  } catch {
3134
3134
  return reject3("invalid_document", 400, ["request body must be JSON"]);
3135
3135
  }
3136
- const digest = typeof input.digest === "string" ? input.digest : "";
3136
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
3137
3137
  const checked = validateAdmissionGrantDocument(input.document);
3138
3138
  if (!checked.ok) return reject3("invalid_document", 400, checked.errors);
3139
3139
  const document = checked.value;
3140
3140
  if (document.source.appId !== claimedSender) return reject3("wrong_source", 403);
3141
3141
  if (document.target.chapterId !== ctx.chapter.id) return reject3("wrong_target", 409);
3142
3142
  if (document.target.environment !== env.ODLA_ENV) return reject3("wrong_environment", 409);
3143
- if (!digest || await signupControlDigest(document) !== digest) return reject3("invalid_digest", 400);
3143
+ if (!digest2 || await signupControlDigest(document) !== digest2) return reject3("invalid_digest", 400);
3144
3144
  const latest = await loadLatestAdmissionRevision(db, claimedSender, ctx.chapter.id, env.ODLA_ENV);
3145
3145
  if (latest && document.revision < latest.revision) return reject3("stale_revision", 409);
3146
3146
  if (latest && document.revision === latest.revision) {
3147
- if (latest.digest !== digest) return reject3("conflicting_revision", 409);
3147
+ if (latest.digest !== digest2) return reject3("conflicting_revision", 409);
3148
3148
  return json({
3149
3149
  ok: true,
3150
3150
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
@@ -3156,7 +3156,7 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3156
3156
  }
3157
3157
  const receivedAt = Date.now();
3158
3158
  try {
3159
- await materializeAdmissionGrants(db, { digest, document }, latest, receivedAt);
3159
+ await materializeAdmissionGrants(db, { digest: digest2, document }, latest, receivedAt);
3160
3160
  } catch {
3161
3161
  return reject3("materialization_failed", 503);
3162
3162
  }
@@ -3164,7 +3164,7 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
3164
3164
  ok: true,
3165
3165
  target: { chapterId: ctx.chapter.id, environment: env.ODLA_ENV },
3166
3166
  revision: document.revision,
3167
- digest,
3167
+ digest: digest2,
3168
3168
  status: "accepted",
3169
3169
  acknowledgedAt: receivedAt
3170
3170
  });
@@ -3367,9 +3367,9 @@ function personAdmissionEmail(ctx, record4) {
3367
3367
  if (record4.type !== "person") return null;
3368
3368
  const field = ctx.chapter.crm.config.types.person?.emailField;
3369
3369
  if (!field) return null;
3370
- const raw = record4.fields[field] ?? record4.primaryEmail;
3371
- if (typeof raw !== "string") return null;
3372
- const normalized = normalizeAdmissionEmail(raw);
3370
+ const raw2 = record4.fields[field] ?? record4.primaryEmail;
3371
+ if (typeof raw2 !== "string") return null;
3372
+ const normalized = normalizeAdmissionEmail(raw2);
3373
3373
  return isValidEmail(normalized) ? normalized : null;
3374
3374
  }
3375
3375
  async function grantInput(db, ctx, assignment) {
@@ -3389,7 +3389,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3389
3389
  const current = await loadLeaderAdmissionAssignments(opts.db, opts.target.id);
3390
3390
  const id = assignmentId(opts.target.id, opts.personId);
3391
3391
  const existing = current.find((row) => row.id === id);
3392
- const digest = await signupControlDigest({
3392
+ const digest2 = await signupControlDigest({
3393
3393
  targetId: opts.target.id,
3394
3394
  personId: opts.personId,
3395
3395
  decision: opts.decision,
@@ -3404,7 +3404,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3404
3404
  } } });
3405
3405
  const receipt = admissionRows(priorMutation2.leaderAdmissionMutations)[0];
3406
3406
  if (receipt) {
3407
- if (receipt.requestDigest !== digest || receipt.personId !== opts.personId) {
3407
+ if (receipt.requestDigest !== digest2 || receipt.personId !== opts.personId) {
3408
3408
  throw new Error("mutation_conflict");
3409
3409
  }
3410
3410
  const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
@@ -3412,7 +3412,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3412
3412
  return { delivery: saved, duplicate: true };
3413
3413
  }
3414
3414
  if (existing?.lastMutationId === opts.mutationId) {
3415
- if (existing.lastRequestDigest !== digest) throw new Error("mutation_conflict");
3415
+ if (existing.lastRequestDigest !== digest2) throw new Error("mutation_conflict");
3416
3416
  const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
3417
3417
  if (!saved) throw new Error("delivery_missing");
3418
3418
  return { delivery: saved, duplicate: true };
@@ -3427,7 +3427,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3427
3427
  ...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
3428
3428
  active: opts.active,
3429
3429
  lastMutationId: opts.mutationId,
3430
- lastRequestDigest: digest,
3430
+ lastRequestDigest: digest2,
3431
3431
  createdAt: existing?.createdAt ?? now,
3432
3432
  createdBy: existing?.createdBy ?? opts.actor,
3433
3433
  updatedAt: now,
@@ -3463,7 +3463,7 @@ async function persistLeaderAdmissionDesiredSet(opts) {
3463
3463
  id: mutationReceiptId,
3464
3464
  targetId: opts.target.id,
3465
3465
  personId: opts.personId,
3466
- requestDigest: digest,
3466
+ requestDigest: digest2,
3467
3467
  revision,
3468
3468
  createdAt: now
3469
3469
  } }
@@ -3658,8 +3658,8 @@ function makeCalendar(env) {
3658
3658
  }
3659
3659
  async function firstRow(db, ns, q) {
3660
3660
  const res = await db.query({ [ns]: { $: q } });
3661
- const rows5 = res[ns];
3662
- return Array.isArray(rows5) ? rows5[0] : void 0;
3661
+ const rows6 = res[ns];
3662
+ return Array.isArray(rows6) ? rows6[0] : void 0;
3663
3663
  }
3664
3664
  async function computeSlots(cal, cfg) {
3665
3665
  const { from, to } = slotWindow(Date.now(), cfg.windowDays);
@@ -3916,17 +3916,17 @@ async function notifyAdminOfPayment(db, env, eventId, app) {
3916
3916
  try {
3917
3917
  const group = await groupFor(db, app);
3918
3918
  if (!group || typeof group.notificationEmail !== "string" || !group.notificationEmail) return;
3919
- const text4 = (value) => typeof value === "string" ? value : "";
3919
+ const text6 = (value) => typeof value === "string" ? value : "";
3920
3920
  await sendTemplated(deps(db, env), {
3921
3921
  group: emailGroupFrom(group),
3922
3922
  template: "adminNotification",
3923
3923
  to: group.notificationEmail,
3924
3924
  vars: {
3925
- firstName: text4(app.firstName),
3926
- lastName: text4(app.lastName),
3927
- email: text4(app.email),
3928
- phone: text4(app.phone),
3929
- state: text4(app.state)
3925
+ firstName: text6(app.firstName),
3926
+ lastName: text6(app.lastName),
3927
+ email: text6(app.email),
3928
+ phone: text6(app.phone),
3929
+ state: text6(app.state)
3930
3930
  },
3931
3931
  dedupeKey: `${eventId}:admin`,
3932
3932
  applicationId: String(app.id)
@@ -4059,8 +4059,8 @@ function createStripeProvider(config) {
4059
4059
  const sk = requireSecret(secretKey);
4060
4060
  const charges = await stripeCall(sk, "GET", "/v1/charges", { customer: input.customerId, limit: 100 });
4061
4061
  if (!charges.ok) fail("charges list", charges);
4062
- const rows5 = charges.body.data ?? [];
4063
- const paid = rows5.filter((c) => c.status === "succeeded" && c.refunded !== true);
4062
+ const rows6 = charges.body.data ?? [];
4063
+ const paid = rows6.filter((c) => c.status === "succeeded" && c.refunded !== true);
4064
4064
  const charge = paid[paid.length - 1];
4065
4065
  if (!charge) {
4066
4066
  const err = new Error("no paid charge to refund");
@@ -4168,8 +4168,8 @@ async function issueGiftClaim(db, intent, provider, now) {
4168
4168
  throw new Error("gift issuance conflict");
4169
4169
  }
4170
4170
  }
4171
- function buyerGiftClaims(rows5, now) {
4172
- return rows5.map((row) => {
4171
+ function buyerGiftClaims(rows6, now) {
4172
+ return rows6.map((row) => {
4173
4173
  const stored = String(row.status ?? "pending");
4174
4174
  const status = stored === "pending" && Number(row.codeExpiresAt) <= now ? "expired" : stored;
4175
4175
  return {
@@ -4461,8 +4461,8 @@ function classifyMemberStripeEvent(identity, target, hasProvenLegacyLocalLink) {
4461
4461
 
4462
4462
  // src/stripe-webhook-partition.ts
4463
4463
  var firstRow2 = async (db, ns, q) => {
4464
- const rows5 = (await db.query({ [ns]: { $: q } }))[ns];
4465
- return Array.isArray(rows5) ? rows5[0] : void 0;
4464
+ const rows6 = (await db.query({ [ns]: { $: q } }))[ns];
4465
+ return Array.isArray(rows6) ? rows6[0] : void 0;
4466
4466
  };
4467
4467
  var stripeEventDigest = async (rawBody) => {
4468
4468
  const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(rawBody));
@@ -4509,9 +4509,9 @@ async function storeStripeReceipt(db, input) {
4509
4509
  const raced = await receiptFor(db, input.eventId);
4510
4510
  return raced?.digest === input.digest ? "replayed" : "collision";
4511
4511
  }
4512
- async function quarantineStripeEvent(db, eventId, digest, event) {
4512
+ async function quarantineStripeEvent(db, eventId, digest2, event) {
4513
4513
  const disposition = "quarantine_unmatched";
4514
- const stored = await storeStripeReceipt(db, { eventId, digest, event, disposition, matched: false });
4514
+ const stored = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition, matched: false });
4515
4515
  if (stored === "collision") {
4516
4516
  reportStripePartition("error", eventId, event, "quarantine_collision");
4517
4517
  return json({ ok: true, matched: false, collision: true });
@@ -4519,10 +4519,10 @@ async function quarantineStripeEvent(db, eventId, digest, event) {
4519
4519
  reportStripePartition("warn", eventId, event, disposition);
4520
4520
  return json({ ok: true, matched: false, disposition, ...stored === "replayed" ? { replayed: true } : {} });
4521
4521
  }
4522
- async function replayedStripeReceipt(db, eventId, digest, event) {
4522
+ async function replayedStripeReceipt(db, eventId, digest2, event) {
4523
4523
  const existing = await receiptFor(db, eventId);
4524
4524
  if (!existing) return null;
4525
- if (existing.digest !== digest) {
4525
+ if (existing.digest !== digest2) {
4526
4526
  reportStripePartition("error", eventId, event, "quarantine_collision");
4527
4527
  return json({ ok: true, matched: false, collision: true });
4528
4528
  }
@@ -4532,8 +4532,8 @@ async function replayedStripeReceipt(db, eventId, digest, event) {
4532
4532
  // src/worker-routes-payments.ts
4533
4533
  async function firstRow3(db, ns, q) {
4534
4534
  const res = await db.query({ [ns]: { $: q } });
4535
- const rows5 = res[ns];
4536
- return Array.isArray(rows5) ? rows5[0] : void 0;
4535
+ const rows6 = res[ns];
4536
+ return Array.isArray(rows6) ? rows6[0] : void 0;
4537
4537
  }
4538
4538
  async function findApplication(db, event) {
4539
4539
  if ("applicationId" in event && event.applicationId) {
@@ -4657,7 +4657,7 @@ async function ingestWebhook(req, env, ctx) {
4657
4657
  if (!ingest.ok) return json({ error: "invalid signature" }, 400);
4658
4658
  const { eventId, event } = ingest;
4659
4659
  if (event.kind === "ignored") return json({ ok: true, ignored: event.type });
4660
- const digest = await stripeEventDigest(rawBody);
4660
+ const digest2 = await stripeEventDigest(rawBody);
4661
4661
  const identity = stripeEventIdentity(event);
4662
4662
  const target = stripeEventTarget(env, ctx);
4663
4663
  const preliminary = classifyMemberStripeEvent(identity, target, false);
@@ -4671,23 +4671,23 @@ async function ingestWebhook(req, env, ctx) {
4671
4671
  reportStripePartition("warn", eventId, event, "ignore_sibling");
4672
4672
  return json({ ok: true, matched: false, disposition: "ignore_sibling" });
4673
4673
  }
4674
- const replay2 = await replayedStripeReceipt(db, eventId, digest, event);
4674
+ const replay2 = await replayedStripeReceipt(db, eventId, digest2, event);
4675
4675
  if (replay2) return replay2;
4676
4676
  const localIntent = Boolean(intent && intent.chapterId === ctx.chapter.id && intent.environment === env.ODLA_ENV && (typeof intent.runtime !== "string" || intent.runtime === target.runtime));
4677
4677
  const disposition2 = classifyMemberStripeEvent(identity, target, localIntent);
4678
4678
  if (!localIntent || disposition2 === "quarantine_unmatched") {
4679
- return quarantineStripeEvent(db, eventId, digest, event);
4679
+ return quarantineStripeEvent(db, eventId, digest2, event);
4680
4680
  }
4681
4681
  if (event.kind === "gift_paid") {
4682
4682
  if (intent.providerObjectId !== event.providerObjectId || intent.status === "refunded" || intent.status === "canceled") {
4683
- return quarantineStripeEvent(db, eventId, digest, event);
4683
+ return quarantineStripeEvent(db, eventId, digest2, event);
4684
4684
  }
4685
4685
  try {
4686
4686
  const issued = await issueGiftClaim(db, intent, {
4687
4687
  eventId,
4688
4688
  objectId: event.providerObjectId
4689
4689
  }, Date.now());
4690
- const stored3 = await storeStripeReceipt(db, { eventId, digest, event, disposition: disposition2, matched: true });
4690
+ const stored3 = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition: disposition2, matched: true });
4691
4691
  if (stored3 === "collision") {
4692
4692
  reportStripePartition("error", eventId, event, "quarantine_collision");
4693
4693
  return json({ ok: true, matched: false, collision: true });
@@ -4703,7 +4703,7 @@ async function ingestWebhook(req, env, ctx) {
4703
4703
  } }], { mutationId: `gift-revoked-intent:${eventId}` });
4704
4704
  const claim = await firstRow3(db, "giftClaims", { where: { intentId: event.giftIntentId }, limit: 1 });
4705
4705
  if (claim) await revokeGiftClaim(db, claim, event.reason, Date.now());
4706
- const stored2 = await storeStripeReceipt(db, { eventId, digest, event, disposition: disposition2, matched: Boolean(claim) });
4706
+ const stored2 = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition: disposition2, matched: Boolean(claim) });
4707
4707
  if (stored2 === "collision") {
4708
4708
  reportStripePartition("error", eventId, event, "quarantine_collision");
4709
4709
  return json({ ok: true, matched: false, collision: true });
@@ -4715,12 +4715,12 @@ async function ingestWebhook(req, env, ctx) {
4715
4715
  reportStripePartition("warn", eventId, event, "ignore_sibling");
4716
4716
  return json({ ok: true, matched: false, disposition: "ignore_sibling" });
4717
4717
  }
4718
- const replay = await replayedStripeReceipt(db, eventId, digest, event);
4718
+ const replay = await replayedStripeReceipt(db, eventId, digest2, event);
4719
4719
  if (replay) return replay;
4720
4720
  const localApplication = Boolean(app && (typeof app.stripeRuntime !== "string" || app.stripeRuntime === target.runtime));
4721
4721
  const disposition = classifyMemberStripeEvent(identity, target, localApplication);
4722
4722
  if (!localApplication || disposition === "quarantine_unmatched") {
4723
- return quarantineStripeEvent(db, eventId, digest, event);
4723
+ return quarantineStripeEvent(db, eventId, digest2, event);
4724
4724
  }
4725
4725
  const patch = webhookPatch(event, String(app.status ?? ""));
4726
4726
  let next = app;
@@ -4743,7 +4743,7 @@ async function ingestWebhook(req, env, ctx) {
4743
4743
  });
4744
4744
  }
4745
4745
  }
4746
- const stored = await storeStripeReceipt(db, { eventId, digest, event, disposition, matched: true });
4746
+ const stored = await storeStripeReceipt(db, { eventId, digest: digest2, event, disposition, matched: true });
4747
4747
  if (stored === "collision") {
4748
4748
  reportStripePartition("error", eventId, event, "quarantine_collision");
4749
4749
  return json({ ok: true, matched: false, collision: true });
@@ -4999,8 +4999,8 @@ async function upcomingEvents(env) {
4999
4999
  const res = await cal.availability.upcoming();
5000
5000
  return res.events.map((e) => ({ eventId: e.eventId, status: e.status, startAt: e.startAt, endAt: e.endAt }));
5001
5001
  }
5002
- function toReconcile(rows5) {
5003
- return rows5.map((m) => ({
5002
+ function toReconcile(rows6) {
5003
+ return rows6.map((m) => ({
5004
5004
  id: String(m.id),
5005
5005
  applicationId: String(m.applicationId),
5006
5006
  googleEventId: typeof m.googleEventId === "string" ? m.googleEventId : null,
@@ -5022,12 +5022,12 @@ var handleAdminMeetings = async (req, url, env, ctx) => {
5022
5022
  const query = await db.query({
5023
5023
  meetings: { $: { where: all ? {} : { status: "scheduled" }, order: { startAt: "asc" }, limit: 500 } }
5024
5024
  });
5025
- let rows5 = Array.isArray(query.meetings) ? query.meetings : [];
5026
- if (Number.isFinite(from)) rows5 = rows5.filter((m) => Number(m.startAt ?? 0) >= from);
5027
- if (Number.isFinite(to)) rows5 = rows5.filter((m) => Number(m.startAt ?? 0) <= to);
5025
+ let rows6 = Array.isArray(query.meetings) ? query.meetings : [];
5026
+ if (Number.isFinite(from)) rows6 = rows6.filter((m) => Number(m.startAt ?? 0) >= from);
5027
+ if (Number.isFinite(to)) rows6 = rows6.filter((m) => Number(m.startAt ?? 0) <= to);
5028
5028
  let decisions = [];
5029
5029
  try {
5030
- decisions = reconcileMeetings(toReconcile(rows5), await upcomingEvents(env), Date.now());
5030
+ decisions = reconcileMeetings(toReconcile(rows6), await upcomingEvents(env), Date.now());
5031
5031
  } catch {
5032
5032
  }
5033
5033
  const patched = /* @__PURE__ */ new Map();
@@ -5054,7 +5054,7 @@ var handleAdminMeetings = async (req, url, env, ctx) => {
5054
5054
  };
5055
5055
  const group = (await db.query({ groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } } })).groups?.[0];
5056
5056
  const timezone = resolveScheduling(group?.schedulingJson).timezone;
5057
- const meetings = rows5.map((m) => ({ ...m, ...patched.get(String(m.id)) ?? {}, applicant: applicantOf(m) })).filter((m) => all || m.status === "scheduled");
5057
+ const meetings = rows6.map((m) => ({ ...m, ...patched.get(String(m.id)) ?? {}, applicant: applicantOf(m) })).filter((m) => all || m.status === "scheduled");
5058
5058
  return json({ meetings, adopted: decisions.length, timezone });
5059
5059
  };
5060
5060
 
@@ -5075,16 +5075,16 @@ var crmDeps2 = (db, ctx) => ({
5075
5075
  });
5076
5076
  var handleAdminCrmSync = async (req, url, env, ctx) => {
5077
5077
  if (req.method !== "POST" || url.pathname !== "/api/admin/crm/sync") return null;
5078
- const gate11 = await adminGate(req, env, ctx);
5079
- if (gate11 instanceof Response) return gate11;
5080
- const result = await backfillCrm(crmDeps2(gate11.db, ctx));
5078
+ const gate12 = await adminGate(req, env, ctx);
5079
+ if (gate12 instanceof Response) return gate12;
5080
+ const result = await backfillCrm(crmDeps2(gate12.db, ctx));
5081
5081
  return json({ ok: true, ...result });
5082
5082
  };
5083
5083
  var handleAdminPeople = async (req, url, env, ctx) => {
5084
5084
  if (req.method !== "GET" || url.pathname !== "/api/admin/people") return null;
5085
- const gate11 = await adminGate(req, env, ctx);
5086
- if (gate11 instanceof Response) return gate11;
5087
- const { db } = gate11;
5085
+ const gate12 = await adminGate(req, env, ctx);
5086
+ if (gate12 instanceof Response) return gate12;
5087
+ const { db } = gate12;
5088
5088
  const sk = await getVaultSecret(db, "clerk_secret_key");
5089
5089
  const [data, roleList] = await Promise.all([
5090
5090
  db.query({
@@ -5130,16 +5130,16 @@ var handleAdminPeople = async (req, url, env, ctx) => {
5130
5130
  });
5131
5131
  }
5132
5132
  }
5133
- const rows5 = [...people.values()].sort(
5133
+ const rows6 = [...people.values()].sort(
5134
5134
  (x, y) => (y.application?.createdAt ?? -1) - (x.application?.createdAt ?? -1)
5135
5135
  );
5136
- return json({ people: rows5 });
5136
+ return json({ people: rows6 });
5137
5137
  };
5138
5138
  var handleAdminPeopleAccess = async (req, url, env, ctx) => {
5139
5139
  if (req.method !== "GET" || url.pathname !== "/api/admin/people/access") return null;
5140
- const gate11 = await adminGate(req, env, ctx);
5141
- if (gate11 instanceof Response) return gate11;
5142
- const { db } = gate11;
5140
+ const gate12 = await adminGate(req, env, ctx);
5141
+ if (gate12 instanceof Response) return gate12;
5142
+ const { db } = gate12;
5143
5143
  const targetId = url.searchParams.get("userId") ?? "";
5144
5144
  if (!targetId.startsWith("user_")) return json({ error: "invalid userId" }, 400);
5145
5145
  const sk = await getVaultSecret(db, "clerk_secret_key");
@@ -5150,9 +5150,9 @@ var handleAdminPeopleAccess = async (req, url, env, ctx) => {
5150
5150
  };
5151
5151
  var handleAdminPeopleRole = async (req, url, env, ctx) => {
5152
5152
  if (req.method !== "POST" || url.pathname !== "/api/admin/people/role") return null;
5153
- const gate11 = await adminGate(req, env, ctx);
5154
- if (gate11 instanceof Response) return gate11;
5155
- const { db, actor } = gate11;
5153
+ const gate12 = await adminGate(req, env, ctx);
5154
+ if (gate12 instanceof Response) return gate12;
5155
+ const { db, actor } = gate12;
5156
5156
  let body;
5157
5157
  try {
5158
5158
  body = await req.json();
@@ -5188,8 +5188,8 @@ function canonical(value) {
5188
5188
  }
5189
5189
  return JSON.stringify(value) ?? "undefined";
5190
5190
  }
5191
- function positiveInt(raw, fallback, max) {
5192
- const parsed = Number(raw);
5191
+ function positiveInt(raw2, fallback, max) {
5192
+ const parsed = Number(raw2);
5193
5193
  return Number.isSafeInteger(parsed) && parsed >= 0 ? Math.min(parsed, max) : fallback;
5194
5194
  }
5195
5195
  var handleAdminClerkPrivateProfileSync = async (req, url, env, ctx) => {
@@ -5270,14 +5270,14 @@ function periodSeries(points, now, count, periodMs) {
5270
5270
  for (const point of points) {
5271
5271
  if (!Number.isFinite(point.t) || !Number.isFinite(point.v)) continue;
5272
5272
  if (point.t >= start && point.t <= now) {
5273
- const index = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
5274
- current[index] = (current[index] ?? 0) + point.v;
5273
+ const index2 = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
5274
+ current[index2] = (current[index2] ?? 0) + point.v;
5275
5275
  } else if (point.t >= previousStart && point.t < start) {
5276
- const index = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
5277
- previous[index] = (previous[index] ?? 0) + point.v;
5276
+ const index2 = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
5277
+ previous[index2] = (previous[index2] ?? 0) + point.v;
5278
5278
  }
5279
5279
  }
5280
- const labels = current.map((_, index) => new Date(start + index * periodMs).toISOString().slice(5, 10));
5280
+ const labels = current.map((_, index2) => new Date(start + index2 * periodMs).toISOString().slice(5, 10));
5281
5281
  return { labels, current, previous };
5282
5282
  }
5283
5283
  function dashboardMetricData(points, now) {
@@ -5690,11 +5690,11 @@ var handleAdminGroupEmail = async (req, url, env, ctx) => {
5690
5690
  for (const key of EMAIL_TEMPLATE_NAMES) {
5691
5691
  const t = templates[key];
5692
5692
  const subject = typeof t?.subject === "string" ? t.subject.trim() : "";
5693
- const text4 = typeof t?.text === "string" ? t.text : "";
5694
- if (!subject || !text4.trim()) return json({ error: `template "${key}" needs a subject and a body` }, 400);
5693
+ const text6 = typeof t?.text === "string" ? t.text : "";
5694
+ if (!subject || !text6.trim()) return json({ error: `template "${key}" needs a subject and a body` }, 400);
5695
5695
  if (/[\r\n]/.test(subject) || subject.length > 200) return json({ error: `template "${key}" subject must be a single line under 200 characters` }, 400);
5696
- if (text4.length > 1e4) return json({ error: `template "${key}" body is too long` }, 400);
5697
- clean[key] = { subject, text: text4, enabled: t?.enabled !== false };
5696
+ if (text6.length > 1e4) return json({ error: `template "${key}" body is too long` }, 400);
5697
+ clean[key] = { subject, text: text6, enabled: t?.enabled !== false };
5698
5698
  }
5699
5699
  const emailish = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
5700
5700
  const notificationEmail = str2(body.notificationEmail).trim();
@@ -5911,7 +5911,8 @@ var handleAdminNetworkTargets = async (req, url, env, ctx) => {
5911
5911
  fields,
5912
5912
  sharedNotes,
5913
5913
  editableFields,
5914
- stageTransitions
5914
+ stageTransitions,
5915
+ commercialParity
5915
5916
  }) => ({
5916
5917
  id,
5917
5918
  name,
@@ -5919,7 +5920,8 @@ var handleAdminNetworkTargets = async (req, url, env, ctx) => {
5919
5920
  types: Object.keys(fields ?? DEFAULT_SHARE_FIELDS),
5920
5921
  sharedNotes,
5921
5922
  editableFields,
5922
- stageTransitions
5923
+ stageTransitions,
5924
+ commercialParity
5923
5925
  }))
5924
5926
  });
5925
5927
  };
@@ -6743,18 +6745,18 @@ function emailLooksReal(value) {
6743
6745
  function validateNetworkPersonImport(input, allowedChapterIds) {
6744
6746
  const errors = [];
6745
6747
  if (!input || typeof input !== "object" || Array.isArray(input)) return { ok: false, errors: ["import must be an object"] };
6746
- const raw = input;
6747
- if (raw.sourceKind !== "external_reimport") errors.push("sourceKind must be external_reimport");
6748
- if (raw.approved !== true || raw.isRealPerson !== true) errors.push("import must be approved and identify a real person");
6749
- const sourceSystem = typeof raw.sourceSystem === "string" ? raw.sourceSystem.trim().toLowerCase() : "";
6750
- const sourceRecordId = typeof raw.sourceRecordId === "string" ? raw.sourceRecordId.trim() : "";
6751
- const provenance = typeof raw.provenance === "string" ? raw.provenance.trim() : "";
6752
- const displayName = typeof raw.displayName === "string" ? raw.displayName.trim() : "";
6748
+ const raw2 = input;
6749
+ if (raw2.sourceKind !== "external_reimport") errors.push("sourceKind must be external_reimport");
6750
+ if (raw2.approved !== true || raw2.isRealPerson !== true) errors.push("import must be approved and identify a real person");
6751
+ const sourceSystem = typeof raw2.sourceSystem === "string" ? raw2.sourceSystem.trim().toLowerCase() : "";
6752
+ const sourceRecordId = typeof raw2.sourceRecordId === "string" ? raw2.sourceRecordId.trim() : "";
6753
+ const provenance = typeof raw2.provenance === "string" ? raw2.provenance.trim() : "";
6754
+ const displayName = typeof raw2.displayName === "string" ? raw2.displayName.trim() : "";
6753
6755
  if (!TOKEN.test(sourceSystem) || /(?:^|[._:-])(fixture|test|development|dev)(?:$|[._:-])/.test(sourceSystem)) errors.push("sourceSystem must name an approved non-development external system");
6754
6756
  if (!sourceRecordId || sourceRecordId.length > 200) errors.push("sourceRecordId is required and bounded");
6755
6757
  if (!provenance || provenance.length > 500) errors.push("provenance is required and bounded");
6756
6758
  if (!displayName || displayName.length > 200) errors.push("displayName is required and bounded");
6757
- const rawConsent = raw.consent && typeof raw.consent === "object" && !Array.isArray(raw.consent) ? raw.consent : {};
6759
+ const rawConsent = raw2.consent && typeof raw2.consent === "object" && !Array.isArray(raw2.consent) ? raw2.consent : {};
6758
6760
  const consentStates = ["unknown", "granted", "denied"];
6759
6761
  const marketing = typeof rawConsent.marketing === "string" ? rawConsent.marketing : "";
6760
6762
  const discretionary = typeof rawConsent.discretionary === "string" ? rawConsent.discretionary : "";
@@ -6764,10 +6766,10 @@ function validateNetworkPersonImport(input, allowedChapterIds) {
6764
6766
  errors.push("consent must include marketing and discretionary states, recordedAt, and a bounded basis");
6765
6767
  }
6766
6768
  const identities = [];
6767
- if (!Array.isArray(raw.identities) || raw.identities.length < 1 || raw.identities.length > 8) {
6769
+ if (!Array.isArray(raw2.identities) || raw2.identities.length < 1 || raw2.identities.length > 8) {
6768
6770
  errors.push("one to eight verified identities are required");
6769
6771
  } else {
6770
- for (const item of raw.identities) {
6772
+ for (const item of raw2.identities) {
6771
6773
  if (!item || typeof item !== "object" || Array.isArray(item)) {
6772
6774
  errors.push("identity must be an object");
6773
6775
  continue;
@@ -6799,9 +6801,9 @@ function validateNetworkPersonImport(input, allowedChapterIds) {
6799
6801
  seen.add(key);
6800
6802
  }
6801
6803
  const chapterLinks = [];
6802
- if (raw.chapterLinks !== void 0) {
6803
- if (!Array.isArray(raw.chapterLinks) || raw.chapterLinks.length > allowedChapterIds.length) errors.push("chapterLinks are invalid or too numerous");
6804
- else for (const item of raw.chapterLinks) {
6804
+ if (raw2.chapterLinks !== void 0) {
6805
+ if (!Array.isArray(raw2.chapterLinks) || raw2.chapterLinks.length > allowedChapterIds.length) errors.push("chapterLinks are invalid or too numerous");
6806
+ else for (const item of raw2.chapterLinks) {
6805
6807
  if (!item || typeof item !== "object" || Array.isArray(item)) {
6806
6808
  errors.push("chapter link must be an object");
6807
6809
  continue;
@@ -6837,9 +6839,9 @@ async function sha256(value) {
6837
6839
  }
6838
6840
  async function networkIdentityClaim(input) {
6839
6841
  const normalized = normalizeNetworkIdentity(input.kind, input.value);
6840
- const digest = await sha256(`${input.kind}\0${normalized}`);
6842
+ const digest2 = await sha256(`${input.kind}\0${normalized}`);
6841
6843
  const hint = input.kind === "email" ? `${normalized.slice(0, 1)}***@${normalized.split("@")[1]}` : input.kind === "phone" ? `***${normalized.slice(-4)}` : void 0;
6842
- return { id: `identity_${digest}`, kind: input.kind, value: normalized, ...hint ? { hint } : {}, verifiedAt: input.verifiedAt };
6844
+ return { id: `identity_${digest2}`, kind: input.kind, value: normalized, ...hint ? { hint } : {}, verifiedAt: input.verifiedAt };
6843
6845
  }
6844
6846
  async function networkSourceKey(sourceSystem, sourceRecordId) {
6845
6847
  return `alias_${await sha256(`${sourceSystem}\0${sourceRecordId}`)}`;
@@ -7181,9 +7183,9 @@ var handleAdminNetworkPersonRegistry = async (req, url, env, ctx) => {
7181
7183
  if (req.method === "POST") {
7182
7184
  let body;
7183
7185
  try {
7184
- const text4 = await req.text();
7185
- if (new TextEncoder().encode(text4).byteLength > MAX_BODY2) return json({ error: "request is too large" }, 413);
7186
- const parsed = JSON.parse(text4);
7186
+ const text6 = await req.text();
7187
+ if (new TextEncoder().encode(text6).byteLength > MAX_BODY2) return json({ error: "request is too large" }, 413);
7188
+ const parsed = JSON.parse(text6);
7187
7189
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("invalid");
7188
7190
  body = parsed;
7189
7191
  } catch {
@@ -7315,6 +7317,898 @@ var handleAdminNetworkPersonRegistry = async (req, url, env, ctx) => {
7315
7317
  });
7316
7318
  };
7317
7319
 
7320
+ // src/worker-routes-network-commercial.ts
7321
+ import { constructStripeEvent, initStripe, StripeError } from "@odla-ai/stripe";
7322
+
7323
+ // src/config-network-commercial.ts
7324
+ function networkCommercialSourceKey(source) {
7325
+ return [source.stripeMode, source.appId, source.environment, source.dataEnvironment, source.runtime, source.chapterId].join(":");
7326
+ }
7327
+
7328
+ // src/network-commercial.ts
7329
+ var raw = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
7330
+ var text3 = (value) => typeof value === "string" && value.length > 0 && value.length <= 255 ? value : void 0;
7331
+ var integer2 = (value) => typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
7332
+ var bool = (value) => typeof value === "boolean" ? value : void 0;
7333
+ var providerId = (value, prefix) => {
7334
+ const valueText = text3(value);
7335
+ return valueText && (!prefix || valueText.startsWith(prefix)) ? valueText : void 0;
7336
+ };
7337
+ function metadataCandidates(object) {
7338
+ const parent = raw(object.parent);
7339
+ const subscriptionDetails = raw(object.subscription_details);
7340
+ const parentSubscription = raw(parent.subscription_details);
7341
+ return [raw(object.metadata), raw(subscriptionDetails.metadata), raw(parentSubscription.metadata)];
7342
+ }
7343
+ function metadataValue(candidates, key) {
7344
+ return candidates.map((candidate) => text3(candidate[key])).find(Boolean);
7345
+ }
7346
+ function refId(value, prefix) {
7347
+ if (typeof value === "string") return providerId(value, prefix);
7348
+ return providerId(raw(value).id, prefix);
7349
+ }
7350
+ function subscriptionRef(object) {
7351
+ return refId(object.subscription, "sub_") ?? refId(raw(object.subscription_details).subscription, "sub_") ?? refId(raw(raw(object.parent).subscription_details).subscription, "sub_");
7352
+ }
7353
+ function firstPrice(object) {
7354
+ const data = raw(object.items).data;
7355
+ const item = Array.isArray(data) ? raw(data[0]) : {};
7356
+ const priceId = refId(item.price, "price_");
7357
+ const quantity = integer2(item.quantity);
7358
+ return { ...priceId ? { priceId } : {}, ...quantity !== void 0 ? { quantity } : {} };
7359
+ }
7360
+ function normalizeNetworkCommercialStripeEvent(input, digest2) {
7361
+ const event = raw(input);
7362
+ const eventId = providerId(event.id, "evt_") ?? "";
7363
+ const eventType = text3(event.type) ?? "";
7364
+ const object = raw(raw(event.data).object);
7365
+ const objectId = providerId(object.id) ?? "";
7366
+ const candidates = metadataCandidates(object);
7367
+ const identity = {};
7368
+ const identityKeys = {
7369
+ appId: "odlaAppId",
7370
+ chapterId: "chapterId",
7371
+ environment: "odlaEnvironment",
7372
+ dataEnvironment: "odlaDataEnvironment",
7373
+ runtime: "odlaRuntime"
7374
+ };
7375
+ for (const [target, source] of Object.entries(identityKeys)) {
7376
+ const value = metadataValue(candidates, source);
7377
+ if (value) identity[target] = value;
7378
+ }
7379
+ let objectKind = "ignored";
7380
+ if (eventType.startsWith("customer.subscription.")) objectKind = "subscription";
7381
+ else if (eventType.startsWith("customer.")) objectKind = "customer";
7382
+ else if (eventType.startsWith("invoice.")) objectKind = "invoice";
7383
+ else if (eventType.startsWith("refund.")) objectKind = "refund";
7384
+ else if (eventType === "charge.refunded") objectKind = "refund";
7385
+ const customerId = refId(object.customer, "cus_");
7386
+ const subscriptionId = objectKind === "subscription" ? providerId(objectId, "sub_") : subscriptionRef(object);
7387
+ const invoiceId = objectKind === "invoice" ? providerId(objectId, "in_") : refId(object.invoice, "in_");
7388
+ const chargeId = eventType === "charge.refunded" ? providerId(objectId, "ch_") : refId(object.charge, "ch_");
7389
+ const paymentIntentId = refId(object.payment_intent, "pi_");
7390
+ const price = firstPrice(object);
7391
+ const amountCents = integer2(eventType === "charge.refunded" ? object.amount_refunded : object.amount);
7392
+ const currency = text3(object.currency)?.toLowerCase();
7393
+ const status = text3(object.status);
7394
+ return {
7395
+ eventId,
7396
+ digest: digest2,
7397
+ eventType,
7398
+ eventCreated: integer2(event.created) ?? 0,
7399
+ livemode: event.livemode === true,
7400
+ objectKind,
7401
+ objectId,
7402
+ identity,
7403
+ ...metadataValue(candidates, "applicationId") ? { applicationId: metadataValue(candidates, "applicationId") } : {},
7404
+ ...metadataValue(candidates, "personId") ? { personId: metadataValue(candidates, "personId") } : {},
7405
+ ...metadataValue(candidates, "membershipId") ? { membershipId: metadataValue(candidates, "membershipId") } : {},
7406
+ ...customerId ? { customerId } : {},
7407
+ ...subscriptionId ? { subscriptionId } : {},
7408
+ ...invoiceId ? { invoiceId } : {},
7409
+ ...chargeId ? { chargeId } : {},
7410
+ ...paymentIntentId ? { paymentIntentId } : {},
7411
+ ...price,
7412
+ ...status ? { status } : {},
7413
+ ...currency ? { currency } : {},
7414
+ ...amountCents !== void 0 ? { amountCents } : {},
7415
+ ...integer2(object.amount_paid) !== void 0 ? { amountPaidCents: integer2(object.amount_paid) } : {},
7416
+ ...integer2(object.amount_remaining) !== void 0 ? { amountRemainingCents: integer2(object.amount_remaining) } : {},
7417
+ ...integer2(object.current_period_start) !== void 0 ? { currentPeriodStart: integer2(object.current_period_start) } : {},
7418
+ ...integer2(object.current_period_end) !== void 0 ? { currentPeriodEnd: integer2(object.current_period_end) } : {},
7419
+ ...bool(object.cancel_at_period_end) !== void 0 ? { cancelAtPeriodEnd: bool(object.cancel_at_period_end) } : {}
7420
+ };
7421
+ }
7422
+ var identityFields = ["appId", "chapterId", "environment", "dataEnvironment", "runtime"];
7423
+ function directSource(fact, sources) {
7424
+ const mode = fact.livemode ? "live" : "test";
7425
+ const supplied = identityFields.filter((field) => fact.identity[field] !== void 0);
7426
+ const candidates = sources.filter((source) => source.stripeMode === mode && supplied.every((field) => source[field] === fact.identity[field]));
7427
+ if (supplied.length === identityFields.length) return candidates.length === 1 ? candidates[0] : "wrong";
7428
+ if (supplied.length > 0 && candidates.length === 0) return "wrong";
7429
+ return null;
7430
+ }
7431
+ var objectRefs = (fact) => [
7432
+ fact.customerId && `customer:${fact.customerId}`,
7433
+ fact.subscriptionId && `subscription:${fact.subscriptionId}`,
7434
+ fact.invoiceId && `invoice:${fact.invoiceId}`,
7435
+ fact.chargeId && `charge:${fact.chargeId}`,
7436
+ fact.paymentIntentId && `payment_intent:${fact.paymentIntentId}`
7437
+ ].filter((value) => Boolean(value));
7438
+ function ownRef(fact) {
7439
+ if (!fact.objectId) return null;
7440
+ if (fact.objectKind === "customer") return `customer:${fact.objectId}`;
7441
+ if (fact.objectKind === "subscription") return `subscription:${fact.objectId}`;
7442
+ if (fact.objectKind === "invoice") return `invoice:${fact.objectId}`;
7443
+ if (fact.eventType.startsWith("refund.")) return `refund:${fact.objectId}`;
7444
+ return fact.eventType === "charge.refunded" ? `charge:${fact.objectId}` : null;
7445
+ }
7446
+ var order = (left, right) => left.eventCreated - right.eventCreated || left.eventId.localeCompare(right.eventId);
7447
+ function projectNetworkCommercialEvents(input, sources) {
7448
+ const facts2 = [...input].sort(order);
7449
+ const exceptions = [];
7450
+ const unique = [];
7451
+ const digests = /* @__PURE__ */ new Map();
7452
+ for (const fact of facts2) {
7453
+ const prior = digests.get(fact.eventId);
7454
+ if (prior === void 0) {
7455
+ digests.set(fact.eventId, fact.digest);
7456
+ unique.push(fact);
7457
+ } else if (prior !== fact.digest) exceptions.push({ eventId: fact.eventId, kind: "collision", detail: { eventType: fact.eventType, objectId: fact.objectId } });
7458
+ }
7459
+ const assignments = /* @__PURE__ */ new Map();
7460
+ const refs = /* @__PURE__ */ new Map();
7461
+ for (const fact of unique) {
7462
+ const direct = directSource(fact, sources);
7463
+ if (direct && direct !== "wrong") assignments.set(fact.eventId, direct);
7464
+ }
7465
+ let changed = true;
7466
+ while (changed) {
7467
+ changed = false;
7468
+ for (const fact of unique) {
7469
+ const assigned = assignments.get(fact.eventId);
7470
+ if (assigned) {
7471
+ const key = networkCommercialSourceKey(assigned);
7472
+ for (const ref of [...objectRefs(fact), ownRef(fact)].filter((value) => Boolean(value))) {
7473
+ const keys2 = refs.get(ref) ?? /* @__PURE__ */ new Set();
7474
+ const before = keys2.size;
7475
+ keys2.add(key);
7476
+ refs.set(ref, keys2);
7477
+ if (keys2.size !== before) changed = true;
7478
+ }
7479
+ continue;
7480
+ }
7481
+ if (directSource(fact, sources) === "wrong") continue;
7482
+ const keys = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
7483
+ if (keys.size !== 1) continue;
7484
+ const candidate = sources.find((source) => networkCommercialSourceKey(source) === [...keys][0]);
7485
+ if (candidate && directSource({ ...fact, identity: fact.identity }, [candidate]) !== "wrong") {
7486
+ assignments.set(fact.eventId, candidate);
7487
+ changed = true;
7488
+ }
7489
+ }
7490
+ }
7491
+ const latest = /* @__PURE__ */ new Map();
7492
+ for (const fact of unique) {
7493
+ if (fact.objectKind === "ignored" || !fact.eventId || !fact.objectId) {
7494
+ exceptions.push({ eventId: fact.eventId, kind: "unsupported", detail: { eventType: fact.eventType, objectId: fact.objectId } });
7495
+ continue;
7496
+ }
7497
+ const source = assignments.get(fact.eventId);
7498
+ if (!source) {
7499
+ const direct = directSource(fact, sources);
7500
+ const keys = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
7501
+ exceptions.push({ eventId: fact.eventId, kind: direct === "wrong" ? "wrong_partition" : keys.size > 1 ? "ambiguous_partition" : "unmatched_partition", detail: { eventType: fact.eventType, objectId: fact.objectId } });
7502
+ continue;
7503
+ }
7504
+ const row = { ...fact, sourceKey: networkCommercialSourceKey(source) };
7505
+ const key = `${fact.objectKind}:${fact.objectId}`;
7506
+ const prior = latest.get(key);
7507
+ if (!prior || order(prior, fact) <= 0) latest.set(key, row);
7508
+ }
7509
+ const rows6 = [...latest.values()];
7510
+ const customers = rows6.filter((row) => row.objectKind === "customer");
7511
+ const subscriptions = rows6.filter((row) => row.objectKind === "subscription");
7512
+ const invoices = rows6.filter((row) => row.objectKind === "invoice");
7513
+ const refunds = rows6.filter((row) => row.objectKind === "refund" && row.eventType.startsWith("refund."));
7514
+ for (const charge of rows6.filter((row) => row.eventType === "charge.refunded")) {
7515
+ if (!refunds.some((refund) => refund.chargeId === charge.objectId)) {
7516
+ exceptions.push({ eventId: charge.eventId, kind: "charge_refund_without_refund_object", sourceKey: charge.sourceKey, detail: { eventType: charge.eventType, objectId: charge.objectId } });
7517
+ }
7518
+ }
7519
+ const totalsMap = /* @__PURE__ */ new Map();
7520
+ const add = (row, field, amount) => {
7521
+ if (!row.currency) return;
7522
+ const key = `${row.sourceKey}:${row.currency}`;
7523
+ const total = totalsMap.get(key) ?? { sourceKey: row.sourceKey, currency: row.currency, paidCents: 0, refundedCents: 0, netCents: 0 };
7524
+ total[field] += amount;
7525
+ total.netCents = total.paidCents - total.refundedCents;
7526
+ totalsMap.set(key, total);
7527
+ };
7528
+ for (const row of invoices) add(row, "paidCents", row.amountPaidCents ?? 0);
7529
+ for (const row of refunds) add(row, "refundedCents", row.amountCents ?? 0);
7530
+ return { customers, subscriptions, invoices, refunds, exceptions, totals: [...totalsMap.values()].sort((a, b) => `${a.sourceKey}:${a.currency}`.localeCompare(`${b.sourceKey}:${b.currency}`)) };
7531
+ }
7532
+
7533
+ // src/network-commercial-store.ts
7534
+ var eventRowId = (eventId) => `network-commercial-event:${eventId}`;
7535
+ var projectionId = (kind, sourceKey, providerId2) => `network-commercial:${kind}:${sourceKey}:${providerId2}`;
7536
+ var exceptionId = (eventId, kind) => `network-commercial-exception:${eventId}:${kind}`;
7537
+ var materializedNamespaces = [
7538
+ "networkCommercialCustomers",
7539
+ "networkCommercialSubscriptions",
7540
+ "networkCommercialInvoices",
7541
+ "networkCommercialRefunds"
7542
+ ];
7543
+ async function rows5(db, ns) {
7544
+ const all = [];
7545
+ const pageSize = 500;
7546
+ for (let offset = 0; ; offset += pageSize) {
7547
+ const result = await db.query({ [ns]: { $: { limit: pageSize, offset } } });
7548
+ const page = Array.isArray(result[ns]) ? result[ns] : [];
7549
+ all.push(...page);
7550
+ if (page.length < pageSize) return all;
7551
+ }
7552
+ }
7553
+ async function loadNetworkCommercialProjection(db, sources) {
7554
+ const stored = await rows5(db, "networkCommercialEvents");
7555
+ const projection = projectNetworkCommercialEvents(stored.map((row) => row.fact), sources);
7556
+ const persisted = await rows5(db, "networkCommercialExceptions");
7557
+ for (const row of persisted) {
7558
+ if (row.status !== "active" || row.kind !== "collision") continue;
7559
+ if (projection.exceptions.some((item) => item.eventId === row.eventId && item.kind === "collision")) continue;
7560
+ projection.exceptions.push({
7561
+ eventId: String(row.eventId),
7562
+ kind: "collision",
7563
+ ...typeof row.sourceKey === "string" ? { sourceKey: row.sourceKey } : {},
7564
+ detail: row.detail
7565
+ });
7566
+ }
7567
+ return projection;
7568
+ }
7569
+ function projectionOps(projection, now) {
7570
+ const groups = [
7571
+ ["networkCommercialCustomers", "customer", projection.customers],
7572
+ ["networkCommercialSubscriptions", "subscription", projection.subscriptions],
7573
+ ["networkCommercialInvoices", "invoice", projection.invoices],
7574
+ ["networkCommercialRefunds", "refund", projection.refunds]
7575
+ ];
7576
+ return groups.flatMap(([ns, kind, projected]) => projected.map((snapshot) => ({
7577
+ t: "update",
7578
+ ns,
7579
+ id: projectionId(kind, snapshot.sourceKey, snapshot.objectId),
7580
+ attrs: {
7581
+ id: projectionId(kind, snapshot.sourceKey, snapshot.objectId),
7582
+ providerId: snapshot.objectId,
7583
+ sourceKey: snapshot.sourceKey,
7584
+ status: "active",
7585
+ snapshot,
7586
+ eventCreated: snapshot.eventCreated,
7587
+ updatedAt: now
7588
+ }
7589
+ })));
7590
+ }
7591
+ async function rebuildNetworkCommercialProjection(db, sources, mutationId, now = Date.now()) {
7592
+ const projection = await loadNetworkCommercialProjection(db, sources);
7593
+ const [existing, ...materialized] = await Promise.all([
7594
+ rows5(db, "networkCommercialExceptions"),
7595
+ ...materializedNamespaces.map((ns) => rows5(db, ns))
7596
+ ]);
7597
+ const activeIds = new Set(projection.exceptions.map((item) => exceptionId(item.eventId, item.kind)));
7598
+ const projectedIds = new Set(projectionOps(projection, now).map((op) => String(op.id)));
7599
+ const ops = [
7600
+ ...projectionOps(projection, now),
7601
+ ...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 } }))),
7602
+ ...existing.filter((row) => row.status === "active" && !activeIds.has(String(row.id))).map((row) => ({
7603
+ t: "update",
7604
+ ns: "networkCommercialExceptions",
7605
+ id: String(row.id),
7606
+ attrs: { status: "resolved", updatedAt: now }
7607
+ })),
7608
+ ...projection.exceptions.map((item) => ({
7609
+ t: "update",
7610
+ ns: "networkCommercialExceptions",
7611
+ id: exceptionId(item.eventId, item.kind),
7612
+ attrs: {
7613
+ id: exceptionId(item.eventId, item.kind),
7614
+ eventId: item.eventId,
7615
+ kind: item.kind,
7616
+ ...item.sourceKey ? { sourceKey: item.sourceKey } : {},
7617
+ status: "active",
7618
+ detail: item.detail,
7619
+ updatedAt: now
7620
+ }
7621
+ }))
7622
+ ];
7623
+ if (ops.length > 0) await db.transact(ops, { mutationId });
7624
+ return projection;
7625
+ }
7626
+ async function ingestNetworkCommercialFact(db, sources, fact, now = Date.now()) {
7627
+ const found = await db.query({ networkCommercialEvents: { $: { where: { eventId: fact.eventId }, limit: 1 } } });
7628
+ const existing = found.networkCommercialEvents?.[0];
7629
+ if (existing) {
7630
+ const projection2 = await loadNetworkCommercialProjection(db, sources);
7631
+ if (existing.digest === fact.digest) return { status: "replayed", projection: projection2 };
7632
+ const id2 = exceptionId(fact.eventId, "collision");
7633
+ await db.transact([{ t: "update", ns: "networkCommercialExceptions", id: id2, attrs: {
7634
+ id: id2,
7635
+ eventId: fact.eventId,
7636
+ kind: "collision",
7637
+ status: "active",
7638
+ detail: { eventType: fact.eventType, objectId: fact.objectId },
7639
+ updatedAt: now
7640
+ } }], { mutationId: `network-commercial-collision:${fact.eventId}:${fact.digest}` });
7641
+ return { status: "collision", projection: await loadNetworkCommercialProjection(db, sources) };
7642
+ }
7643
+ const id = eventRowId(fact.eventId);
7644
+ let lostRace = false;
7645
+ let duplicate = false;
7646
+ try {
7647
+ const write = await db.transact([{ t: "update", ns: "networkCommercialEvents", id, attrs: {
7648
+ id,
7649
+ eventId: fact.eventId,
7650
+ digest: fact.digest,
7651
+ eventType: fact.eventType,
7652
+ eventCreated: fact.eventCreated,
7653
+ livemode: fact.livemode,
7654
+ fact,
7655
+ receivedAt: now
7656
+ } }], {
7657
+ mutationId: `network-commercial-ingest:${fact.eventId}:${fact.digest}`,
7658
+ guards: [{ ns: "networkCommercialEvents", id, exists: false }]
7659
+ });
7660
+ duplicate = write.duplicate;
7661
+ } catch (error) {
7662
+ if (error.code !== "transact_guard_failed") throw error;
7663
+ lostRace = true;
7664
+ }
7665
+ if (duplicate || lostRace) {
7666
+ const raced = await db.query({ networkCommercialEvents: { $: { where: { eventId: fact.eventId }, limit: 1 } } });
7667
+ const row = raced.networkCommercialEvents?.[0];
7668
+ const projection2 = await loadNetworkCommercialProjection(db, sources);
7669
+ if (row?.digest === fact.digest) return { status: "replayed", projection: projection2 };
7670
+ const id2 = exceptionId(fact.eventId, "collision");
7671
+ await db.transact([{ t: "update", ns: "networkCommercialExceptions", id: id2, attrs: {
7672
+ id: id2,
7673
+ eventId: fact.eventId,
7674
+ kind: "collision",
7675
+ status: "active",
7676
+ detail: { eventType: fact.eventType, objectId: fact.objectId },
7677
+ updatedAt: now
7678
+ } }], { mutationId: `network-commercial-collision:${fact.eventId}:${fact.digest}` });
7679
+ return { status: "collision", projection: await loadNetworkCommercialProjection(db, sources) };
7680
+ }
7681
+ const projection = await rebuildNetworkCommercialProjection(
7682
+ db,
7683
+ sources,
7684
+ `network-commercial-project:${fact.eventId}:${fact.digest}`,
7685
+ now
7686
+ );
7687
+ return { status: "stored", projection };
7688
+ }
7689
+
7690
+ // src/network-commercial-reconcile.ts
7691
+ var asRaw = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
7692
+ async function listStripeObjects(client, path, params = {}, maxPages = 20) {
7693
+ const objects = [];
7694
+ let startingAfter;
7695
+ for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
7696
+ const page = await client.request("GET", path, {
7697
+ ...params,
7698
+ limit: 100,
7699
+ ...startingAfter ? { starting_after: startingAfter } : {}
7700
+ });
7701
+ if (!Array.isArray(page.data) || typeof page.has_more !== "boolean") {
7702
+ throw new Error("Stripe returned an invalid commercial list response");
7703
+ }
7704
+ const rows6 = page.data.map(asRaw).filter((row) => row !== null);
7705
+ if (rows6.length !== page.data.length) throw new Error("Stripe returned a malformed commercial object");
7706
+ objects.push(...rows6);
7707
+ if (!page.has_more) return { objects, complete: true };
7708
+ const cursor = rows6.at(-1)?.id;
7709
+ if (typeof cursor !== "string" || !cursor) throw new Error("Stripe pagination did not provide a cursor");
7710
+ startingAfter = cursor;
7711
+ }
7712
+ return { objects, complete: false };
7713
+ }
7714
+ function facts(snapshots) {
7715
+ const groups = [
7716
+ ["customer.updated", snapshots.customers.objects],
7717
+ ["customer.subscription.updated", snapshots.subscriptions.objects],
7718
+ ["invoice.updated", snapshots.invoices.objects],
7719
+ ["refund.updated", snapshots.refunds.objects]
7720
+ ];
7721
+ return groups.flatMap(([eventType, objects]) => objects.map((object) => {
7722
+ const id = typeof object.id === "string" ? object.id : "invalid";
7723
+ return normalizeNetworkCommercialStripeEvent({
7724
+ id: `evt_snapshot_${eventType.replaceAll(".", "_")}_${id}`,
7725
+ type: eventType,
7726
+ created: typeof object.created === "number" ? object.created : 0,
7727
+ livemode: object.livemode === true,
7728
+ data: { object }
7729
+ }, `stripe-snapshot:${eventType}:${id}`);
7730
+ }));
7731
+ }
7732
+ var selected = (row) => ({
7733
+ status: row.status ?? null,
7734
+ currency: row.currency ?? null,
7735
+ amountCents: row.amountCents ?? null,
7736
+ amountPaidCents: row.amountPaidCents ?? null,
7737
+ amountRemainingCents: row.amountRemainingCents ?? null,
7738
+ customerId: row.customerId ?? null,
7739
+ subscriptionId: row.subscriptionId ?? null,
7740
+ invoiceId: row.invoiceId ?? null,
7741
+ priceId: row.priceId ?? null,
7742
+ quantity: row.quantity ?? null,
7743
+ applicationId: row.applicationId ?? null,
7744
+ personId: row.personId ?? null,
7745
+ membershipId: row.membershipId ?? null,
7746
+ currentPeriodStart: row.currentPeriodStart ?? null,
7747
+ currentPeriodEnd: row.currentPeriodEnd ?? null,
7748
+ cancelAtPeriodEnd: row.cancelAtPeriodEnd ?? null
7749
+ });
7750
+ var index = (projection) => new Map(
7751
+ [...projection.customers, ...projection.subscriptions, ...projection.invoices, ...projection.refunds].map((row) => [`${row.sourceKey}:${row.objectKind}:${row.objectId}`, row])
7752
+ );
7753
+ function reconcileStripeSnapshot(shadow, snapshots, sources) {
7754
+ const stripe = projectNetworkCommercialEvents(facts(snapshots), sources);
7755
+ const shadowRows = index(shadow);
7756
+ const stripeRows = index(stripe);
7757
+ const exceptions = [];
7758
+ const complete = Object.values(snapshots).every((snapshot) => snapshot.complete);
7759
+ if (!complete) exceptions.push({ kind: "stripe_snapshot_truncated", detail: {} });
7760
+ for (const [key, row] of shadowRows) {
7761
+ const other = stripeRows.get(key);
7762
+ if (!other) {
7763
+ exceptions.push({ kind: "missing_from_stripe", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: {} });
7764
+ } else if (JSON.stringify(selected(row)) !== JSON.stringify(selected(other))) {
7765
+ exceptions.push({ kind: "object_mismatch", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: { shadow: selected(row), stripe: selected(other) } });
7766
+ }
7767
+ }
7768
+ for (const [key, row] of stripeRows) {
7769
+ if (!shadowRows.has(key)) exceptions.push({ kind: "missing_from_shadow", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: {} });
7770
+ }
7771
+ if (JSON.stringify(shadow.totals) !== JSON.stringify(stripe.totals)) {
7772
+ exceptions.push({ kind: "totals_mismatch", detail: { shadow: shadow.totals, stripe: stripe.totals } });
7773
+ }
7774
+ return {
7775
+ ok: exceptions.length === 0,
7776
+ complete,
7777
+ ignoredObjects: stripe.exceptions.filter((item) => item.kind === "wrong_partition" || item.kind === "unmatched_partition").length,
7778
+ exceptions,
7779
+ stripeTotals: stripe.totals
7780
+ };
7781
+ }
7782
+
7783
+ // src/worker-routes-network-commercial-parity.ts
7784
+ import { signFederatedRequest as signFederatedRequest6, verifyFederatedRequest as verifyFederatedRequest8 } from "@odla-ai/db";
7785
+
7786
+ // src/network-commercial-parity.ts
7787
+ var DIGEST = /^[0-9a-f]{64}$/;
7788
+ var MAX_RECORDS = 500;
7789
+ var text4 = (value, max = 255) => typeof value === "string" && value.length > 0 && value.length <= max ? value : null;
7790
+ function parityMessage(partition, kind, value) {
7791
+ return [
7792
+ "chapter-commercial-parity",
7793
+ "v1",
7794
+ partition.appId,
7795
+ partition.chapterId,
7796
+ partition.environment,
7797
+ partition.dataEnvironment,
7798
+ partition.runtime,
7799
+ kind,
7800
+ value
7801
+ ].join("");
7802
+ }
7803
+ async function parityKey(secret) {
7804
+ const encoder4 = new TextEncoder();
7805
+ return crypto.subtle.importKey(
7806
+ "raw",
7807
+ encoder4.encode(secret),
7808
+ { name: "HMAC", hash: "SHA-256" },
7809
+ false,
7810
+ ["sign"]
7811
+ );
7812
+ }
7813
+ async function digestWithKey(key, partition, kind, value) {
7814
+ const encoder4 = new TextEncoder();
7815
+ const signed = await crypto.subtle.sign("HMAC", key, encoder4.encode(parityMessage(partition, kind, value)));
7816
+ return [...new Uint8Array(signed)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
7817
+ }
7818
+ async function buildChapterCommercialParitySnapshot(input) {
7819
+ const requestedMax = input.maxRecords ?? MAX_RECORDS;
7820
+ const maxRecords = Number.isSafeInteger(requestedMax) ? Math.max(0, Math.min(requestedMax, MAX_RECORDS)) : MAX_RECORDS;
7821
+ const eligible = input.rows.filter((row) => text4(row.id) && (text4(row.stripeCustomerId) || text4(row.stripeSubscriptionId)));
7822
+ const selected2 = eligible.slice(0, maxRecords);
7823
+ const key = await parityKey(input.secret);
7824
+ const records = await Promise.all(selected2.map(async (row) => {
7825
+ const applicationId = text4(row.id);
7826
+ const customerId = text4(row.stripeCustomerId);
7827
+ const subscriptionId = text4(row.stripeSubscriptionId);
7828
+ return {
7829
+ applicationDigest: await digestWithKey(key, input.partition, "application", applicationId),
7830
+ customerDigest: customerId ? await digestWithKey(key, input.partition, "customer", customerId) : null,
7831
+ subscriptionDigest: subscriptionId ? await digestWithKey(key, input.partition, "subscription", subscriptionId) : null,
7832
+ status: text4(row.status, 64),
7833
+ canceled: row.canceled === true
7834
+ };
7835
+ }));
7836
+ records.sort((left, right) => left.applicationDigest.localeCompare(right.applicationDigest));
7837
+ const truncated = input.scanTruncated || eligible.length > maxRecords;
7838
+ return {
7839
+ version: 1,
7840
+ site: { id: input.siteId },
7841
+ partition: input.partition,
7842
+ generatedAt: input.now ?? Date.now(),
7843
+ complete: !truncated,
7844
+ truncated,
7845
+ counts: { scanned: input.scanned, eligible: eligible.length, returned: records.length },
7846
+ records
7847
+ };
7848
+ }
7849
+ function recordKey(record4) {
7850
+ return `${record4.applicationDigest}:${record4.customerDigest ?? "-"}:${record4.subscriptionDigest ?? "-"}`;
7851
+ }
7852
+ function validSnapshot(snapshot, source) {
7853
+ const partition = snapshot?.partition;
7854
+ const records = snapshot?.records;
7855
+ if (snapshot?.version !== 1 || snapshot.site?.id !== source.chapterId || !partition || !Array.isArray(records) || records.length > MAX_RECORDS || partition.appId !== source.appId || partition.chapterId !== source.chapterId || partition.environment !== source.environment || partition.dataEnvironment !== source.dataEnvironment || partition.runtime !== source.runtime || typeof snapshot.complete !== "boolean" || typeof snapshot.truncated !== "boolean" || !snapshot.counts) return false;
7856
+ const seen = /* @__PURE__ */ new Set();
7857
+ for (const record4 of records) {
7858
+ if (!DIGEST.test(record4.applicationDigest) || record4.customerDigest !== null && !DIGEST.test(record4.customerDigest) || record4.subscriptionDigest !== null && !DIGEST.test(record4.subscriptionDigest) || record4.status !== null && text4(record4.status, 64) === null || typeof record4.canceled !== "boolean") return false;
7859
+ const key = recordKey(record4);
7860
+ if (seen.has(key)) return false;
7861
+ seen.add(key);
7862
+ }
7863
+ return Number.isSafeInteger(snapshot.counts.scanned) && Number.isSafeInteger(snapshot.counts.eligible) && Number.isSafeInteger(snapshot.counts.returned) && snapshot.counts.returned === records.length && snapshot.counts.scanned >= snapshot.counts.eligible && snapshot.counts.eligible >= snapshot.counts.returned;
7864
+ }
7865
+ async function reconcileCommercialParitySnapshot(input) {
7866
+ if (!validSnapshot(input.snapshot, input.source)) throw new Error("invalid chapter commercial parity snapshot");
7867
+ const sourceKey = networkCommercialSourceKey(input.source);
7868
+ const key = await parityKey(input.secret);
7869
+ const shadow = await Promise.all(input.projection.subscriptions.filter((row) => row.sourceKey === sourceKey && text4(row.applicationId) && text4(row.objectId)).map(async (row) => {
7870
+ const customerId = text4(row.customerId);
7871
+ const subscriptionId = text4(row.subscriptionId) ?? text4(row.objectId);
7872
+ return {
7873
+ applicationDigest: await digestWithKey(key, input.snapshot.partition, "application", row.applicationId),
7874
+ customerDigest: customerId ? await digestWithKey(key, input.snapshot.partition, "customer", customerId) : null,
7875
+ subscriptionDigest: subscriptionId ? await digestWithKey(key, input.snapshot.partition, "subscription", subscriptionId) : null,
7876
+ status: null,
7877
+ canceled: false,
7878
+ stripeStatus: text4(row.status, 64),
7879
+ stripeCanceled: row.status === "canceled"
7880
+ };
7881
+ }));
7882
+ const chapterByKey = new Map(input.snapshot.records.map((record4) => [recordKey(record4), record4]));
7883
+ const shadowByKey = new Map(shadow.map((record4) => [recordKey(record4), record4]));
7884
+ const exceptions = [];
7885
+ const records = [];
7886
+ for (const [key2, chapter] of chapterByKey) {
7887
+ const stripe = shadowByKey.get(key2);
7888
+ if (!stripe) {
7889
+ exceptions.push({ kind: "chapter_only", applicationDigest: chapter.applicationDigest, detail: { chapterStatus: chapter.status } });
7890
+ continue;
7891
+ }
7892
+ const compared = { ...chapter, stripeStatus: stripe.stripeStatus, stripeCanceled: stripe.stripeCanceled };
7893
+ records.push(compared);
7894
+ if (chapter.canceled !== stripe.stripeCanceled) {
7895
+ exceptions.push({
7896
+ kind: "cancellation_mismatch",
7897
+ applicationDigest: chapter.applicationDigest,
7898
+ detail: { chapterCanceled: chapter.canceled, stripeCanceled: stripe.stripeCanceled }
7899
+ });
7900
+ }
7901
+ }
7902
+ for (const [key2, stripe] of shadowByKey) {
7903
+ if (!chapterByKey.has(key2)) {
7904
+ exceptions.push({ kind: "shadow_only", applicationDigest: stripe.applicationDigest, detail: { stripeStatus: stripe.stripeStatus } });
7905
+ }
7906
+ }
7907
+ if (!input.snapshot.complete || input.snapshot.truncated) {
7908
+ exceptions.push({
7909
+ kind: "chapter_truncated",
7910
+ detail: { scanned: input.snapshot.counts.scanned, eligible: input.snapshot.counts.eligible, returned: input.snapshot.counts.returned }
7911
+ });
7912
+ }
7913
+ if (chapterByKey.size !== shadowByKey.size) {
7914
+ exceptions.push({ kind: "count_mismatch", detail: { chapter: chapterByKey.size, shadow: shadowByKey.size } });
7915
+ }
7916
+ records.sort((left, right) => left.applicationDigest.localeCompare(right.applicationDigest));
7917
+ return {
7918
+ ok: input.snapshot.complete && exceptions.length === 0,
7919
+ complete: input.snapshot.complete,
7920
+ counts: { chapter: chapterByKey.size, shadow: shadowByKey.size, matched: records.length },
7921
+ records,
7922
+ exceptions
7923
+ };
7924
+ }
7925
+
7926
+ // src/network-commercial-parity-response.ts
7927
+ var MAX_RESPONSE_BYTES = 512e3;
7928
+ async function readBoundedCommercialParityResponse(response) {
7929
+ const declared = Number(response.headers.get("content-length") ?? 0);
7930
+ if (declared > MAX_RESPONSE_BYTES) return null;
7931
+ if (!response.body) return "";
7932
+ const reader = response.body.getReader();
7933
+ const chunks = [];
7934
+ let total = 0;
7935
+ for (; ; ) {
7936
+ const next = await reader.read();
7937
+ if (next.done) break;
7938
+ total += next.value.byteLength;
7939
+ if (total > MAX_RESPONSE_BYTES) {
7940
+ await reader.cancel();
7941
+ return null;
7942
+ }
7943
+ chunks.push(next.value);
7944
+ }
7945
+ const body = new Uint8Array(total);
7946
+ let offset = 0;
7947
+ for (const chunk of chunks) {
7948
+ body.set(chunk, offset);
7949
+ offset += chunk.byteLength;
7950
+ }
7951
+ return new TextDecoder().decode(body);
7952
+ }
7953
+
7954
+ // src/worker-routes-network-commercial-parity.ts
7955
+ var PARITY_PATH = "/api/network/commercial-parity";
7956
+ var PAGE2 = 500;
7957
+ var MAX_SCAN = 5e3;
7958
+ var MAX_RECORDS2 = 500;
7959
+ async function localCommercialRows(db) {
7960
+ const rows6 = [];
7961
+ for (let offset = 0; offset < MAX_SCAN; offset += PAGE2) {
7962
+ const result = await db.query({ applications: { $: { limit: PAGE2, offset, order: { createdAt: "asc" } } } });
7963
+ const page = Array.isArray(result.applications) ? result.applications : [];
7964
+ rows6.push(...page.slice(0, MAX_SCAN - rows6.length));
7965
+ if (page.length < PAGE2) return { rows: rows6, truncated: false };
7966
+ }
7967
+ return { rows: rows6, truncated: true };
7968
+ }
7969
+ var handleNetworkCommercialParity = async (req, url, env, ctx) => {
7970
+ if (req.method !== "GET" || url.pathname !== PARITY_PATH) return null;
7971
+ if (ctx.chapter.mode !== "chapter") return json({ error: "not found" }, 404);
7972
+ const readers = ctx.chapter.network.readers.filter((reader) => reader.commercialParity);
7973
+ if (readers.length === 0) return json({ error: "unauthorized" }, 401);
7974
+ const db = ctx.makeDb(env);
7975
+ const secret = await getVaultSecret(db, "network_share_secret");
7976
+ if (!secret) return json({ error: "unauthorized" }, 401);
7977
+ const verified = await verifyFederatedRequest8(req, { secret, senders: readers.map((reader) => reader.id) });
7978
+ if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
7979
+ const partition = {
7980
+ appId: env.ODLA_APP_ID,
7981
+ chapterId: ctx.chapter.id,
7982
+ environment: env.ODLA_ENV,
7983
+ dataEnvironment: env.ODLA_ENV,
7984
+ runtime: env.ODLA_RUNTIME?.trim() || "default"
7985
+ };
7986
+ try {
7987
+ const local3 = await localCommercialRows(db);
7988
+ return json(await buildChapterCommercialParitySnapshot({
7989
+ rows: local3.rows,
7990
+ secret,
7991
+ partition,
7992
+ siteId: ctx.chapter.id,
7993
+ scanned: local3.rows.length,
7994
+ scanTruncated: local3.truncated,
7995
+ maxRecords: MAX_RECORDS2
7996
+ }));
7997
+ } catch {
7998
+ return json({ error: "commercial parity unavailable" }, 503);
7999
+ }
8000
+ };
8001
+ async function reconcileTarget(db, env, ctx, target, projection) {
8002
+ const base = { targetId: target.id, name: target.name, url: target.url };
8003
+ const secret = await getVaultSecret(db, target.secretName);
8004
+ if (!secret) return { ...base, available: false, ok: false, error: { code: "edge_secret_missing" } };
8005
+ const destination = new URL(PARITY_PATH, target.url);
8006
+ try {
8007
+ const transport = resolveNetworkTargetTransport(env, target);
8008
+ const headers = await signFederatedRequest6({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
8009
+ const response = await transport.fetch(destination, { headers, signal: AbortSignal.timeout(1e4) });
8010
+ const responseBody = await readBoundedCommercialParityResponse(response);
8011
+ const snapshot = responseBody !== null ? (() => {
8012
+ try {
8013
+ return JSON.parse(responseBody);
8014
+ } catch {
8015
+ return null;
8016
+ }
8017
+ })() : null;
8018
+ if (!response.ok || !snapshot) {
8019
+ return {
8020
+ ...base,
8021
+ available: false,
8022
+ ok: false,
8023
+ transport: transport.kind,
8024
+ error: { code: "chapter_response_failed", status: response.status }
8025
+ };
8026
+ }
8027
+ const sources = ctx.chapter.networkCommercialReadModel?.sources ?? [];
8028
+ const matchingSources = sources.filter((candidate) => candidate.appId === snapshot.partition?.appId && candidate.chapterId === snapshot.partition?.chapterId && candidate.environment === snapshot.partition?.environment && candidate.dataEnvironment === snapshot.partition?.dataEnvironment && candidate.runtime === snapshot.partition?.runtime);
8029
+ if (matchingSources.length !== 1) {
8030
+ return {
8031
+ ...base,
8032
+ available: true,
8033
+ ok: false,
8034
+ transport: transport.kind,
8035
+ error: { code: matchingSources.length === 0 ? "source_not_configured" : "source_ambiguous" }
8036
+ };
8037
+ }
8038
+ return {
8039
+ ...base,
8040
+ available: true,
8041
+ transport: transport.kind,
8042
+ generatedAt: snapshot.generatedAt,
8043
+ ...await reconcileCommercialParitySnapshot({
8044
+ projection,
8045
+ source: matchingSources[0],
8046
+ snapshot,
8047
+ secret
8048
+ })
8049
+ };
8050
+ } catch (error) {
8051
+ return {
8052
+ ...base,
8053
+ available: false,
8054
+ ok: false,
8055
+ error: {
8056
+ code: error instanceof NetworkTargetTransportError ? "binding_unavailable" : error instanceof Error && error.message === "invalid chapter commercial parity snapshot" ? "invalid_chapter_snapshot" : "chapter_request_failed"
8057
+ }
8058
+ };
8059
+ }
8060
+ }
8061
+ async function reconcileChapterCommercialTargets(db, env, ctx, projection) {
8062
+ const targets = ctx.chapter.network.targets.filter((target) => target.commercialParity);
8063
+ const results = await Promise.all(targets.map((target) => reconcileTarget(db, env, ctx, target, projection)));
8064
+ return {
8065
+ reconciled: true,
8066
+ provider: "chapters",
8067
+ writable: false,
8068
+ configured: targets.length,
8069
+ available: results.filter((result) => result.available).length,
8070
+ ok: results.every((result) => result.ok),
8071
+ targets: results
8072
+ };
8073
+ }
8074
+
8075
+ // src/worker-routes-network-commercial.ts
8076
+ var ADMIN_PATH2 = "/api/admin/network-commercial-read-model";
8077
+ var WEBHOOK_PATH = "/api/network-commercial/stripe-webhook";
8078
+ var MAX_BODY3 = 512e3;
8079
+ var MUTATION_ID5 = /^[A-Za-z0-9_-]{16,128}$/;
8080
+ async function digest(body) {
8081
+ const value = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(body));
8082
+ return [...new Uint8Array(value)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
8083
+ }
8084
+ async function boundedBody(req) {
8085
+ if (Number(req.headers.get("content-length") ?? 0) > MAX_BODY3) return null;
8086
+ const body = await req.text();
8087
+ return new TextEncoder().encode(body).byteLength <= MAX_BODY3 ? body : null;
8088
+ }
8089
+ async function gate11(req, env, ctx) {
8090
+ const db = ctx.makeDb(env);
8091
+ const user = await ctx.verifyUser(req, env);
8092
+ if (!user) return { response: json({ error: "unauthorized" }, 401) };
8093
+ if (!await ctx.isSuperAdminEmail(db, user.email)) return { response: json({ error: "forbidden" }, 403) };
8094
+ return { db, user };
8095
+ }
8096
+ function safeProjection(projection) {
8097
+ const row = (item) => ({
8098
+ sourceKey: item.sourceKey,
8099
+ providerId: item.objectId,
8100
+ eventType: item.eventType,
8101
+ eventCreated: item.eventCreated,
8102
+ status: item.status ?? null,
8103
+ currency: item.currency ?? null,
8104
+ amountCents: item.amountCents ?? null,
8105
+ amountPaidCents: item.amountPaidCents ?? null,
8106
+ amountRemainingCents: item.amountRemainingCents ?? null,
8107
+ customerId: item.customerId ?? null,
8108
+ subscriptionId: item.subscriptionId ?? null,
8109
+ invoiceId: item.invoiceId ?? null,
8110
+ priceId: item.priceId ?? null,
8111
+ quantity: item.quantity ?? null,
8112
+ applicationId: item.applicationId ?? null,
8113
+ personId: item.personId ?? null,
8114
+ membershipId: item.membershipId ?? null,
8115
+ currentPeriodStart: item.currentPeriodStart ?? null,
8116
+ currentPeriodEnd: item.currentPeriodEnd ?? null,
8117
+ cancelAtPeriodEnd: item.cancelAtPeriodEnd ?? null
8118
+ });
8119
+ return {
8120
+ version: 1,
8121
+ mode: "shadow",
8122
+ writable: false,
8123
+ authoritativeForCharges: false,
8124
+ totals: projection.totals,
8125
+ customers: projection.customers.map(row),
8126
+ subscriptions: projection.subscriptions.map(row),
8127
+ invoices: projection.invoices.map(row),
8128
+ refunds: projection.refunds.map(row),
8129
+ exceptions: projection.exceptions
8130
+ };
8131
+ }
8132
+ var handleNetworkCommercialStripeWebhook = async (req, url, env, ctx) => {
8133
+ if (url.pathname !== WEBHOOK_PATH) return null;
8134
+ if (!ctx.chapter.networkCommercialReadModel) return json({ error: "not found" }, 404);
8135
+ if (req.method !== "POST") return json({ error: "method not allowed" }, 405);
8136
+ const body = await boundedBody(req);
8137
+ if (body === null) return json({ error: "request is too large" }, 413);
8138
+ const signature = req.headers.get("stripe-signature") ?? "";
8139
+ const db = ctx.makeDb(env);
8140
+ let secret;
8141
+ try {
8142
+ secret = await db.secrets.get("network_commercial_stripe_webhook_secret");
8143
+ } catch {
8144
+ return json({ error: "commercial webhook is not configured" }, 503);
8145
+ }
8146
+ try {
8147
+ const event = await constructStripeEvent(body, signature, secret);
8148
+ const fact = normalizeNetworkCommercialStripeEvent(event, await digest(body));
8149
+ if (!fact.eventId || !fact.eventType || !fact.objectId) return json({ error: "Stripe event identity is invalid" }, 400);
8150
+ const result = await ingestNetworkCommercialFact(db, ctx.chapter.networkCommercialReadModel.sources, fact);
8151
+ if (result.status === "collision") {
8152
+ console.error("network commercial event collision", JSON.stringify({ eventId: fact.eventId, eventType: fact.eventType }));
8153
+ }
8154
+ return json({ ok: true, status: result.status, eventId: fact.eventId });
8155
+ } catch (error) {
8156
+ if (error instanceof StripeError) return json({ error: error.message, code: error.code }, 400);
8157
+ throw error;
8158
+ }
8159
+ };
8160
+ var handleAdminNetworkCommercialReadModel = async (req, url, env, ctx) => {
8161
+ if (url.pathname !== ADMIN_PATH2) return null;
8162
+ if (!ctx.chapter.networkCommercialReadModel) return json({ error: "not found" }, 404);
8163
+ if (req.method !== "GET" && req.method !== "POST") return json({ error: "method not allowed" }, 405);
8164
+ const got = await gate11(req, env, ctx);
8165
+ if ("response" in got) return got.response;
8166
+ const db = got.db;
8167
+ if (req.method === "POST") {
8168
+ const body = await boundedBody(req);
8169
+ if (body === null) return json({ error: "request is too large" }, 413);
8170
+ let parsed;
8171
+ try {
8172
+ const value = JSON.parse(body);
8173
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("shape");
8174
+ parsed = value;
8175
+ } catch {
8176
+ return json({ error: "invalid JSON body" }, 400);
8177
+ }
8178
+ if (parsed.command === "reconcile-chapters") {
8179
+ const projection2 = await loadNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources);
8180
+ return json(await reconcileChapterCommercialTargets(db, env, ctx, projection2));
8181
+ }
8182
+ if (parsed.command === "reconcile-stripe") {
8183
+ const secretKey = await getVaultSecret(db, "stripe_secret_key");
8184
+ if (!secretKey) return json({ error: "Stripe reconciliation is not configured" }, 503);
8185
+ try {
8186
+ const stripe = initStripe({ secretKey });
8187
+ const [customers, subscriptions, invoices, refunds] = await Promise.all([
8188
+ listStripeObjects(stripe, "/v1/customers"),
8189
+ listStripeObjects(stripe, "/v1/subscriptions", { status: "all" }),
8190
+ listStripeObjects(stripe, "/v1/invoices"),
8191
+ listStripeObjects(stripe, "/v1/refunds")
8192
+ ]);
8193
+ const projection2 = await loadNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources);
8194
+ return json({
8195
+ reconciled: true,
8196
+ ...reconcileStripeSnapshot(projection2, { customers, subscriptions, invoices, refunds }, ctx.chapter.networkCommercialReadModel.sources)
8197
+ });
8198
+ } catch (error) {
8199
+ if (error instanceof StripeError) return json({ error: "Stripe reconciliation failed", code: error.code }, 502);
8200
+ return json({ error: "Stripe reconciliation failed" }, 502);
8201
+ }
8202
+ }
8203
+ if (parsed.command !== "rebuild" || typeof parsed.mutationId !== "string" || !MUTATION_ID5.test(parsed.mutationId)) {
8204
+ return json({ error: "rebuild, reconcile-stripe, or reconcile-chapters command is required; rebuild also requires a valid mutationId" }, 400);
8205
+ }
8206
+ const projection = await rebuildNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources, parsed.mutationId);
8207
+ return json({ rebuilt: true, ...safeProjection(projection) });
8208
+ }
8209
+ return json(safeProjection(await loadNetworkCommercialProjection(db, ctx.chapter.networkCommercialReadModel.sources)));
8210
+ };
8211
+
7318
8212
  // src/worker-routes-formation.ts
7319
8213
  import { createRecord as createRecord3 } from "@odla-ai/crm";
7320
8214
  var SUBMISSION = /^[A-Za-z0-9_-]{8,128}$/;
@@ -7331,11 +8225,11 @@ var handleFormation = async (req, url, env, ctx) => {
7331
8225
  });
7332
8226
  }
7333
8227
  if (req.method !== "POST" || url.pathname !== "/api/formation/applications") return null;
7334
- const raw = await req.text();
7335
- if (raw.length > formation.bodyCap) return json({ error: "request body too large" }, 413);
8228
+ const raw2 = await req.text();
8229
+ if (raw2.length > formation.bodyCap) return json({ error: "request body too large" }, 413);
7336
8230
  let body;
7337
8231
  try {
7338
- body = JSON.parse(raw);
8232
+ body = JSON.parse(raw2);
7339
8233
  } catch {
7340
8234
  return json({ error: "invalid JSON body" }, 400);
7341
8235
  }
@@ -7396,7 +8290,7 @@ var handleFormation = async (req, url, env, ctx) => {
7396
8290
  };
7397
8291
 
7398
8292
  // src/worker-routes-builder-library.ts
7399
- import { verifyFederatedRequest as verifyFederatedRequest8 } from "@odla-ai/db";
8293
+ import { verifyFederatedRequest as verifyFederatedRequest9 } from "@odla-ai/db";
7400
8294
 
7401
8295
  // src/builder-library-validation.ts
7402
8296
  var ID4 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -7419,7 +8313,7 @@ var record3 = (value) => value !== null && typeof value === "object" && !Array.i
7419
8313
  function unknownKeys3(value, allowed2, path, errors) {
7420
8314
  for (const key of Object.keys(value)) if (!allowed2.has(key)) errors.push(`${path}.${key} is not supported`);
7421
8315
  }
7422
- function text3(value, path, errors, max = MAX_TEXT2, required = true) {
8316
+ function text5(value, path, errors, max = MAX_TEXT2, required = true) {
7423
8317
  if (value === void 0 && !required) return void 0;
7424
8318
  if (typeof value !== "string" || required && !value.trim()) {
7425
8319
  errors.push(`${path} must be a non-empty string`);
@@ -7429,23 +8323,23 @@ function text3(value, path, errors, max = MAX_TEXT2, required = true) {
7429
8323
  if (result.length > max) errors.push(`${path} must be at most ${max} characters`);
7430
8324
  return result;
7431
8325
  }
7432
- function integer2(value, path, errors, minimum = 1) {
8326
+ function integer3(value, path, errors, minimum = 1) {
7433
8327
  if (!Number.isSafeInteger(value) || Number(value) < minimum) errors.push(`${path} must be an integer >= ${minimum}`);
7434
8328
  return Number(value);
7435
8329
  }
7436
8330
  function scopeValue(value, path, errors) {
7437
- const raw = record3(value);
7438
- if (!raw) {
8331
+ const raw2 = record3(value);
8332
+ if (!raw2) {
7439
8333
  errors.push(`${path} must be an object`);
7440
8334
  return null;
7441
8335
  }
7442
- unknownKeys3(raw, SCOPE_KEYS, path, errors);
7443
- if (raw.kind === "global" || raw.kind === "built_not_found") {
7444
- if (raw.chapterId !== void 0) errors.push(`${path}.chapterId is allowed only for chapter scope`);
7445
- return { kind: raw.kind };
8336
+ unknownKeys3(raw2, SCOPE_KEYS, path, errors);
8337
+ if (raw2.kind === "global" || raw2.kind === "built_not_found") {
8338
+ if (raw2.chapterId !== void 0) errors.push(`${path}.chapterId is allowed only for chapter scope`);
8339
+ return { kind: raw2.kind };
7446
8340
  }
7447
- if (raw.kind === "chapter") {
7448
- const chapterId = text3(raw.chapterId, `${path}.chapterId`, errors, 63) ?? "";
8341
+ if (raw2.kind === "chapter") {
8342
+ const chapterId = text5(raw2.chapterId, `${path}.chapterId`, errors, 63) ?? "";
7449
8343
  if (chapterId && !APP_ID.test(chapterId)) errors.push(`${path}.chapterId must be a lowercase app slug`);
7450
8344
  return { kind: "chapter", chapterId };
7451
8345
  }
@@ -7453,28 +8347,28 @@ function scopeValue(value, path, errors) {
7453
8347
  return null;
7454
8348
  }
7455
8349
  function resourceValue(value, path, errors) {
7456
- const raw = record3(value);
7457
- if (!raw) {
8350
+ const raw2 = record3(value);
8351
+ if (!raw2) {
7458
8352
  errors.push(`${path} must be an object`);
7459
8353
  return null;
7460
8354
  }
7461
- unknownKeys3(raw, RESOURCE_KEYS, path, errors);
7462
- const id = text3(raw.id, `${path}.id`, errors, 80) ?? "";
8355
+ unknownKeys3(raw2, RESOURCE_KEYS, path, errors);
8356
+ const id = text5(raw2.id, `${path}.id`, errors, 80) ?? "";
7463
8357
  if (id && !ID4.test(id)) errors.push(`${path}.id must be lowercase kebab-case`);
7464
- const kind = text3(raw.kind, `${path}.kind`, errors) ?? "";
8358
+ const kind = text5(raw2.kind, `${path}.kind`, errors) ?? "";
7465
8359
  if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
7466
- const title = text3(raw.title, `${path}.title`, errors, 240) ?? "";
7467
- const summary = text3(raw.summary, `${path}.summary`, errors, 1e3, false);
7468
- const status = text3(raw.status, `${path}.status`, errors) ?? "";
8360
+ const title = text5(raw2.title, `${path}.title`, errors, 240) ?? "";
8361
+ const summary = text5(raw2.summary, `${path}.summary`, errors, 1e3, false);
8362
+ const status = text5(raw2.status, `${path}.status`, errors) ?? "";
7469
8363
  if (!STATUSES.has(status)) errors.push(`${path}.status must be draft, published, or archived`);
7470
- const revision = integer2(raw.revision, `${path}.revision`, errors);
7471
- const scope = scopeValue(raw.scope, `${path}.scope`, errors) ?? { kind: "global" };
7472
- const tags = Array.isArray(raw.tags) ? raw.tags.map((tag, index) => text3(tag, `${path}.tags[${index}]`, errors, 80) ?? "") : [];
7473
- if (!Array.isArray(raw.tags)) errors.push(`${path}.tags must be an array`);
8364
+ const revision = integer3(raw2.revision, `${path}.revision`, errors);
8365
+ const scope = scopeValue(raw2.scope, `${path}.scope`, errors) ?? { kind: "global" };
8366
+ const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) => text5(tag, `${path}.tags[${index2}]`, errors, 80) ?? "") : [];
8367
+ if (!Array.isArray(raw2.tags)) errors.push(`${path}.tags must be an array`);
7474
8368
  if (tags.length > MAX_TAGS) errors.push(`${path}.tags may contain at most ${MAX_TAGS} items`);
7475
8369
  if (new Set(tags).size !== tags.length) errors.push(`${path}.tags must be unique`);
7476
- const bodyMarkdown = text3(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN3, false);
7477
- const url = text3(raw.url, `${path}.url`, errors, 2048, false);
8370
+ const bodyMarkdown = text5(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN3, false);
8371
+ const url = text5(raw2.url, `${path}.url`, errors, 2048, false);
7478
8372
  if (url) {
7479
8373
  try {
7480
8374
  if (new URL(url).protocol !== "https:") errors.push(`${path}.url must use https`);
@@ -7482,13 +8376,13 @@ function resourceValue(value, path, errors) {
7482
8376
  errors.push(`${path}.url must be a valid https URL`);
7483
8377
  }
7484
8378
  }
7485
- const asset = raw.brandAsset === void 0 ? null : record3(raw.brandAsset);
7486
- if (raw.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
8379
+ const asset = raw2.brandAsset === void 0 ? null : record3(raw2.brandAsset);
8380
+ if (raw2.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
7487
8381
  if (asset) unknownKeys3(asset, ASSET_KEYS, `${path}.brandAsset`, errors);
7488
8382
  const brandAsset = asset ? {
7489
- bookId: text3(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
7490
- assetId: text3(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
7491
- ...asset.filename === void 0 ? {} : { filename: text3(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
8383
+ bookId: text5(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
8384
+ assetId: text5(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
8385
+ ...asset.filename === void 0 ? {} : { filename: text5(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
7492
8386
  } : void 0;
7493
8387
  if ((kind === "document" || kind === "note") && !bodyMarkdown) errors.push(`${path}.bodyMarkdown is required for ${kind}`);
7494
8388
  if ((kind === "link" || kind === "inspiration") && !url) errors.push(`${path}.url is required for ${kind}`);
@@ -7496,58 +8390,58 @@ function resourceValue(value, path, errors) {
7496
8390
  if (!["document", "note"].includes(kind) && bodyMarkdown) errors.push(`${path}.bodyMarkdown is not allowed for ${kind}`);
7497
8391
  if (!["link", "inspiration"].includes(kind) && url) errors.push(`${path}.url is not allowed for ${kind}`);
7498
8392
  if (!["image", "file"].includes(kind) && brandAsset) errors.push(`${path}.brandAsset is not allowed for ${kind}`);
7499
- const provenanceRaw = record3(raw.provenance);
8393
+ const provenanceRaw = record3(raw2.provenance);
7500
8394
  if (!provenanceRaw) errors.push(`${path}.provenance must be an object`);
7501
8395
  if (provenanceRaw) unknownKeys3(provenanceRaw, PROVENANCE_KEYS, `${path}.provenance`, errors);
7502
8396
  const promotedRaw = provenanceRaw?.promotedFrom === void 0 ? null : record3(provenanceRaw.promotedFrom);
7503
8397
  if (provenanceRaw?.promotedFrom !== void 0 && !promotedRaw) errors.push(`${path}.provenance.promotedFrom must be an object`);
7504
8398
  if (promotedRaw) unknownKeys3(promotedRaw, PROMOTION_KEYS, `${path}.provenance.promotedFrom`, errors);
7505
8399
  const promotedFrom = promotedRaw ? {
7506
- chapterId: text3(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
7507
- resourceId: text3(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
7508
- resourceRevision: integer2(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
7509
- documentRevision: integer2(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
7510
- promotedBy: text3(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
7511
- promotedAt: integer2(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
8400
+ chapterId: text5(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
8401
+ resourceId: text5(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
8402
+ resourceRevision: integer3(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
8403
+ documentRevision: integer3(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
8404
+ promotedBy: text5(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
8405
+ promotedAt: integer3(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
7512
8406
  } : void 0;
7513
8407
  const provenance = {
7514
- createdBy: text3(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
7515
- createdAt: integer2(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
7516
- updatedBy: text3(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
7517
- updatedAt: integer2(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
8408
+ createdBy: text5(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
8409
+ createdAt: integer3(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
8410
+ updatedBy: text5(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
8411
+ updatedAt: integer3(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
7518
8412
  ...promotedFrom ? { promotedFrom } : {}
7519
8413
  };
7520
8414
  return { id, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
7521
8415
  }
7522
8416
  function validateBuilderLibraryDocument(value, options = {}) {
7523
8417
  const errors = [];
7524
- const raw = record3(value);
7525
- if (!raw) return { ok: false, errors: ["document must be an object"] };
7526
- unknownKeys3(raw, ROOT_KEYS4, "document", errors);
7527
- if (raw.schemaVersion !== BUILDER_LIBRARY_SCHEMA_VERSION) errors.push("document.schemaVersion must be 1");
7528
- const revision = integer2(raw.revision, "document.revision", errors);
7529
- const generatedAt = integer2(raw.generatedAt, "document.generatedAt", errors, 0);
7530
- const sourceRaw = record3(raw.source);
8418
+ const raw2 = record3(value);
8419
+ if (!raw2) return { ok: false, errors: ["document must be an object"] };
8420
+ unknownKeys3(raw2, ROOT_KEYS4, "document", errors);
8421
+ if (raw2.schemaVersion !== BUILDER_LIBRARY_SCHEMA_VERSION) errors.push("document.schemaVersion must be 1");
8422
+ const revision = integer3(raw2.revision, "document.revision", errors);
8423
+ const generatedAt = integer3(raw2.generatedAt, "document.generatedAt", errors, 0);
8424
+ const sourceRaw = record3(raw2.source);
7531
8425
  if (!sourceRaw) errors.push("document.source must be an object");
7532
8426
  if (sourceRaw) unknownKeys3(sourceRaw, SOURCE_KEYS2, "document.source", errors);
7533
8427
  const sourceType = sourceRaw?.type;
7534
8428
  if (sourceType !== "chapter" && sourceType !== "built_not_found") errors.push("document.source.type must be chapter or built_not_found");
7535
- const sourceAppId = text3(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
8429
+ const sourceAppId = text5(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
7536
8430
  if (sourceAppId && !APP_ID.test(sourceAppId)) errors.push("document.source.appId must be a lowercase app slug");
7537
8431
  if (options.sourceType && sourceType !== options.sourceType) errors.push(`document.source.type must be ${options.sourceType}`);
7538
8432
  if (options.sourceAppId && sourceAppId !== options.sourceAppId) errors.push(`document.source.appId must be ${options.sourceAppId}`);
7539
- const targetRaw = raw.target === void 0 ? null : record3(raw.target);
7540
- if (raw.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
8433
+ const targetRaw = raw2.target === void 0 ? null : record3(raw2.target);
8434
+ if (raw2.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
7541
8435
  if (targetRaw) unknownKeys3(targetRaw, TARGET_KEYS2, "document.target", errors);
7542
8436
  const target = targetRaw ? {
7543
- appId: text3(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
7544
- environment: text3(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
8437
+ appId: text5(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
8438
+ environment: text5(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
7545
8439
  } : void 0;
7546
8440
  if (options.targetAppId && target?.appId !== options.targetAppId) errors.push(`document.target.appId must be ${options.targetAppId}`);
7547
- if (!Array.isArray(raw.resources)) errors.push("document.resources must be an array");
7548
- const inputs = Array.isArray(raw.resources) ? raw.resources : [];
8441
+ if (!Array.isArray(raw2.resources)) errors.push("document.resources must be an array");
8442
+ const inputs = Array.isArray(raw2.resources) ? raw2.resources : [];
7549
8443
  if (inputs.length > MAX_RESOURCES) errors.push(`document.resources may contain at most ${MAX_RESOURCES} items`);
7550
- const resources = inputs.map((item, index) => resourceValue(item, `document.resources[${index}]`, errors)).filter(Boolean);
8444
+ const resources = inputs.map((item, index2) => resourceValue(item, `document.resources[${index2}]`, errors)).filter(Boolean);
7551
8445
  const ids = resources.map(({ id }) => id);
7552
8446
  if (new Set(ids).size !== ids.length) errors.push("document.resources ids must be unique");
7553
8447
  for (const resource of resources) {
@@ -7621,28 +8515,28 @@ function canonicalBuilderLibrary(value) {
7621
8515
  return canonicalValue3(value);
7622
8516
  }
7623
8517
  async function builderLibraryDigest(document) {
7624
- const digest = await crypto.subtle.digest("SHA-256", encoder3.encode(canonicalBuilderLibrary(document)));
7625
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
8518
+ const digest2 = await crypto.subtle.digest("SHA-256", encoder3.encode(canonicalBuilderLibrary(document)));
8519
+ return `sha256:${Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
7626
8520
  }
7627
8521
 
7628
8522
  // src/builder-library-store.ts
7629
8523
  var control2 = (row) => row?.document && typeof row.document === "object" ? row : null;
7630
8524
  var local2 = (row) => row?.document && typeof row.document === "object" ? row : null;
7631
8525
  async function loadActiveBuilderLibraryControls(db, targetId, environment) {
7632
- const rows5 = (await db.query({ builderLibraryControls: { $: { where: { targetId, environment, active: true }, limit: 500 } } })).builderLibraryControls;
7633
- return Array.isArray(rows5) ? rows5.map(control2).filter(Boolean) : [];
8526
+ const rows6 = (await db.query({ builderLibraryControls: { $: { where: { targetId, environment, active: true }, limit: 500 } } })).builderLibraryControls;
8527
+ return Array.isArray(rows6) ? rows6.map(control2).filter(Boolean) : [];
7634
8528
  }
7635
8529
  async function loadLatestBuilderLibraryControl(db, sourceId, targetId, environment) {
7636
- const rows5 = (await db.query({ builderLibraryControls: { $: { where: { sourceId, targetId, environment }, order: { revision: "desc" }, limit: 20 } } })).builderLibraryControls;
7637
- return control2(Array.isArray(rows5) ? [...rows5].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
8530
+ const rows6 = (await db.query({ builderLibraryControls: { $: { where: { sourceId, targetId, environment }, order: { revision: "desc" }, limit: 20 } } })).builderLibraryControls;
8531
+ return control2(Array.isArray(rows6) ? [...rows6].sort((a, b) => Number(b.revision) - Number(a.revision))[0] : void 0);
7638
8532
  }
7639
8533
  async function loadLocalBuilderLibrary(db, appId, environment) {
7640
8534
  const id = `builder-library-local:${environment}`;
7641
- const rows5 = (await db.query({ builderLibraryLocal: { $: { where: { id }, limit: 1 } } })).builderLibraryLocal;
7642
- return local2(Array.isArray(rows5) ? rows5[0] : void 0);
8535
+ const rows6 = (await db.query({ builderLibraryLocal: { $: { where: { id }, limit: 1 } } })).builderLibraryLocal;
8536
+ return local2(Array.isArray(rows6) ? rows6[0] : void 0);
7643
8537
  }
7644
8538
  async function materializeBuilderLibraryControl(db, envelope, previous, receivedAt) {
7645
- const { document, digest } = envelope;
8539
+ const { document, digest: digest2 } = envelope;
7646
8540
  const target = document.target;
7647
8541
  const id = `builder-library-control:${document.source.appId}:${target.environment}:${document.revision}`;
7648
8542
  const ops = [];
@@ -7653,12 +8547,12 @@ async function materializeBuilderLibraryControl(db, envelope, previous, received
7653
8547
  targetId: target.appId,
7654
8548
  environment: target.environment,
7655
8549
  revision: document.revision,
7656
- digest,
8550
+ digest: digest2,
7657
8551
  document,
7658
8552
  active: true,
7659
8553
  receivedAt
7660
8554
  } });
7661
- await db.transact(ops, { mutationId: `builder-library-control:${document.source.appId}:${target.environment}:${document.revision}:${digest}` });
8555
+ await db.transact(ops, { mutationId: `builder-library-control:${document.source.appId}:${target.environment}:${document.revision}:${digest2}` });
7662
8556
  }
7663
8557
  async function saveLocalBuilderLibrary(db, document, environment, expectedRevision, updatedAt, updatedBy) {
7664
8558
  const id = `builder-library-local:${environment}`;
@@ -7679,7 +8573,7 @@ async function verifySource(req, db, ctx) {
7679
8573
  if (ctx.chapter.mode === "chapter") {
7680
8574
  const secret = await getVaultSecret(db, "network_share_secret");
7681
8575
  if (!secret) return null;
7682
- const verified = await verifyFederatedRequest8(req, {
8576
+ const verified = await verifyFederatedRequest9(req, {
7683
8577
  secret,
7684
8578
  senders: sources.map((source2) => source2.sourceId)
7685
8579
  });
@@ -7692,7 +8586,7 @@ async function verifySource(req, db, ctx) {
7692
8586
  if (!target) continue;
7693
8587
  const secret = await getVaultSecret(db, target.secretName);
7694
8588
  if (!secret) continue;
7695
- const verified = await verifyFederatedRequest8(req.clone(), { secret, senders: [source.sourceId] });
8589
+ const verified = await verifyFederatedRequest9(req.clone(), { secret, senders: [source.sourceId] });
7696
8590
  if (verified.ok) return { source, body: verified.body };
7697
8591
  }
7698
8592
  return null;
@@ -7711,7 +8605,7 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
7711
8605
  } catch {
7712
8606
  return json({ ok: false, reason: "invalid_document", errors: ["request body must be JSON"] }, 400);
7713
8607
  }
7714
- const digest = typeof input.digest === "string" ? input.digest : "";
8608
+ const digest2 = typeof input.digest === "string" ? input.digest : "";
7715
8609
  const sourceType = ctx.chapter.mode === "hub" ? "chapter" : "built_not_found";
7716
8610
  const checked = validateBuilderLibraryDocument(input.document, {
7717
8611
  sourceType,
@@ -7725,29 +8619,29 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
7725
8619
  return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global or this Chapter's resources"] }, 400);
7726
8620
  }
7727
8621
  if (!document.target || document.target.environment !== env.ODLA_ENV) return json({ ok: false, reason: "wrong_environment" }, 409);
7728
- if (!digest || await builderLibraryDigest(document) !== digest) return json({ ok: false, reason: "invalid_digest" }, 400);
8622
+ if (!digest2 || await builderLibraryDigest(document) !== digest2) return json({ ok: false, reason: "invalid_digest" }, 400);
7729
8623
  const latest = await loadLatestBuilderLibraryControl(db, document.source.appId, ctx.chapter.id, env.ODLA_ENV);
7730
8624
  if (latest && document.revision < latest.revision) return json({ ok: false, reason: "stale_revision" }, 409);
7731
8625
  if (latest && document.revision === latest.revision) {
7732
- if (latest.digest !== digest) return json({ ok: false, reason: "conflicting_revision" }, 409);
8626
+ if (latest.digest !== digest2) return json({ ok: false, reason: "conflicting_revision" }, 409);
7733
8627
  return json({ ok: true, target: document.target, revision: latest.revision, digest: latest.digest, status: "replayed", acknowledgedAt: latest.receivedAt });
7734
8628
  }
7735
8629
  const receivedAt = Date.now();
7736
8630
  try {
7737
- await materializeBuilderLibraryControl(db, { document, digest }, latest, receivedAt);
8631
+ await materializeBuilderLibraryControl(db, { document, digest: digest2 }, latest, receivedAt);
7738
8632
  } catch {
7739
8633
  return json({ ok: false, reason: "materialization_failed" }, 503);
7740
8634
  }
7741
- return json({ ok: true, target: document.target, revision: document.revision, digest, status: "accepted", acknowledgedAt: receivedAt });
8635
+ return json({ ok: true, target: document.target, revision: document.revision, digest: digest2, status: "accepted", acknowledgedAt: receivedAt });
7742
8636
  };
7743
8637
  function actorFor(user) {
7744
8638
  return user.email ?? user.userId;
7745
8639
  }
7746
8640
  function withLocalAuthority2(value, mode, appId, revision, now, actor, current) {
7747
8641
  if (!value || typeof value !== "object" || Array.isArray(value)) return value;
7748
- const raw = value;
8642
+ const raw2 = value;
7749
8643
  const currentById = new Map((current?.resources ?? []).map((item) => [item.id, item]));
7750
- const resources = Array.isArray(raw.resources) ? raw.resources.map((input) => {
8644
+ const resources = Array.isArray(raw2.resources) ? raw2.resources.map((input) => {
7751
8645
  if (!input || typeof input !== "object" || Array.isArray(input)) return input;
7752
8646
  const item = input;
7753
8647
  const existing = typeof item.id === "string" ? currentById.get(item.id) : void 0;
@@ -7762,9 +8656,9 @@ function withLocalAuthority2(value, mode, appId, revision, now, actor, current)
7762
8656
  ...existing?.provenance.promotedFrom ? { promotedFrom: existing.provenance.promotedFrom } : {}
7763
8657
  }
7764
8658
  };
7765
- }) : raw.resources;
8659
+ }) : raw2.resources;
7766
8660
  return {
7767
- ...raw,
8661
+ ...raw2,
7768
8662
  schemaVersion: 1,
7769
8663
  revision,
7770
8664
  generatedAt: now,
@@ -7845,7 +8739,7 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
7845
8739
  return json({
7846
8740
  mode: ctx.chapter.mode,
7847
8741
  local: local3 ? { revision: local3.revision, document: local3.document } : null,
7848
- sources: controls.map(({ sourceId, revision, digest, document, receivedAt }) => ({ sourceId, revision, digest, document, receivedAt })),
8742
+ sources: controls.map(({ sourceId, revision, digest: digest2, document, receivedAt }) => ({ sourceId, revision, digest: digest2, document, receivedAt })),
7849
8743
  localAuthoring: ctx.chapter.builderLibrary.localAuthoring,
7850
8744
  chapterTargets: ctx.chapter.mode === "hub" ? ctx.chapter.builderLibrary.sources.map((source) => ({ chapterId: source.sourceId })) : [{ chapterId: ctx.chapter.id }],
7851
8745
  visibility: { builtNotFoundSeesChapterSources: ctx.chapter.mode === "hub", chaptersSeeSiblingSources: false }
@@ -7853,11 +8747,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
7853
8747
  }
7854
8748
  if (req.method !== "PUT") return json({ error: "method not allowed" }, 405);
7855
8749
  if (!ctx.chapter.builderLibrary.localAuthoring) return json({ error: "local authoring is disabled" }, 403);
7856
- const raw = await req.text();
7857
- if (raw.length > BODY_CAP2) return json({ error: "request body too large" }, 413);
8750
+ const raw2 = await req.text();
8751
+ if (raw2.length > BODY_CAP2) return json({ error: "request body too large" }, 413);
7858
8752
  let input;
7859
8753
  try {
7860
- input = JSON.parse(raw);
8754
+ input = JSON.parse(raw2);
7861
8755
  } catch {
7862
8756
  return json({ error: "invalid JSON body" }, 400);
7863
8757
  }
@@ -7882,11 +8776,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
7882
8776
  };
7883
8777
 
7884
8778
  // src/signup-control-delivery.ts
7885
- import { signFederatedRequest as signFederatedRequest6 } from "@odla-ai/db";
8779
+ import { signFederatedRequest as signFederatedRequest7 } from "@odla-ai/db";
7886
8780
  async function deliverSignupControl(options) {
7887
8781
  const destination = new URL(SIGNUP_CONTROL_PATH, options.targetUrl);
7888
8782
  const body = JSON.stringify(options.envelope);
7889
- const signed = await signFederatedRequest6({
8783
+ const signed = await signFederatedRequest7({
7890
8784
  secret: options.secret,
7891
8785
  sender: options.sender,
7892
8786
  method: "POST",
@@ -7907,11 +8801,11 @@ async function deliverSignupControl(options) {
7907
8801
  }
7908
8802
 
7909
8803
  // src/member-content-delivery.ts
7910
- import { signFederatedRequest as signFederatedRequest7 } from "@odla-ai/db";
8804
+ import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
7911
8805
  async function deliverMemberContent(options) {
7912
8806
  const destination = new URL(MEMBER_CONTENT_CONTROL_PATH, options.targetUrl);
7913
8807
  const body = JSON.stringify(options.envelope);
7914
- const signed = await signFederatedRequest7({
8808
+ const signed = await signFederatedRequest8({
7915
8809
  secret: options.secret,
7916
8810
  sender: options.sender,
7917
8811
  method: "POST",
@@ -7932,11 +8826,11 @@ async function deliverMemberContent(options) {
7932
8826
  }
7933
8827
 
7934
8828
  // src/builder-library-delivery.ts
7935
- import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
8829
+ import { signFederatedRequest as signFederatedRequest9 } from "@odla-ai/db";
7936
8830
  async function deliverBuilderLibrary(options) {
7937
8831
  const destination = new URL(BUILDER_LIBRARY_CONTROL_PATH, options.targetUrl);
7938
8832
  const body = JSON.stringify(options.envelope);
7939
- const signed = await signFederatedRequest8({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
8833
+ const signed = await signFederatedRequest9({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
7940
8834
  const response = await (options.fetch ?? globalThis.fetch)(destination, { method: "POST", headers: { ...signed, "content-type": "application/json" }, body });
7941
8835
  const result = await response.json();
7942
8836
  if (!response.ok || !("ok" in result) || result.ok !== true) {
@@ -7950,7 +8844,7 @@ async function deliverBuilderLibrary(options) {
7950
8844
  var SIGNUP_CONTROL_THEME_MARKER = "<!--odla:signup-control-theme-->";
7951
8845
  var SIGNUP_CONTROL_THEME_STYLE_ID = "odla-signup-control-theme";
7952
8846
  var TOKEN_NAME = /^--ui-[a-z0-9][a-z0-9-]{0,94}$/;
7953
- var DIGEST = /^sha256:[0-9a-f]{64}$/;
8847
+ var DIGEST2 = /^sha256:[0-9a-f]{64}$/;
7954
8848
  var UNSAFE_VALUE = /[\u0000-\u001f\u007f\\<>{};]|\/\*|\*\/|(?:^|[^a-z])(?:url|expression)\s*\(|@import/i;
7955
8849
  var MAX_TOKENS = 256;
7956
8850
  var MAX_VALUE_LENGTH = 1e3;
@@ -7962,14 +8856,14 @@ function validatedTokens(input) {
7962
8856
  }
7963
8857
  const tokens = {};
7964
8858
  let serializedLength = 0;
7965
- for (const [name, raw] of entries.sort(([left], [right]) => left.localeCompare(right))) {
8859
+ for (const [name, raw2] of entries.sort(([left], [right]) => left.localeCompare(right))) {
7966
8860
  if (!TOKEN_NAME.test(name)) {
7967
8861
  throw new TypeError(`signup-control theme token "${name}" must be an allowlisted --ui-* property`);
7968
8862
  }
7969
- if (typeof raw !== "string" && typeof raw !== "number" || typeof raw === "number" && !Number.isFinite(raw)) {
8863
+ if (typeof raw2 !== "string" && typeof raw2 !== "number" || typeof raw2 === "number" && !Number.isFinite(raw2)) {
7970
8864
  throw new TypeError(`signup-control theme token "${name}" must be a finite string or number`);
7971
8865
  }
7972
- const value = String(raw).trim();
8866
+ const value = String(raw2).trim();
7973
8867
  if (!value || value.length > MAX_VALUE_LENGTH || UNSAFE_VALUE.test(value)) {
7974
8868
  throw new TypeError(`signup-control theme token "${name}" has an unsafe or oversized value`);
7975
8869
  }
@@ -7994,7 +8888,7 @@ function stateFor(revision, options) {
7994
8888
  metadata: { source: "bootstrap", revision: null, digest: null, cacheKey: "signup-control:bootstrap" }
7995
8889
  };
7996
8890
  }
7997
- if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !DIGEST.test(revision.digest)) {
8891
+ if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !DIGEST2.test(revision.digest)) {
7998
8892
  throw new TypeError("signup-control theme revision metadata is invalid");
7999
8893
  }
8000
8894
  return {
@@ -8091,6 +8985,8 @@ var BUILTIN_ROUTES = [
8091
8985
  handleNetworkSnapshot,
8092
8986
  handleNetworkRecords,
8093
8987
  handleNetworkSharedNotes,
8988
+ handleNetworkCommercialParity,
8989
+ handleNetworkCommercialStripeWebhook,
8094
8990
  handleFormation,
8095
8991
  handleMember,
8096
8992
  handleMemberContent,
@@ -8131,6 +9027,7 @@ var BUILTIN_ROUTES = [
8131
9027
  handleAdminNetworkNotes,
8132
9028
  handleAdminNetworkSharedNotes,
8133
9029
  handleAdminNetworkPersonRegistry,
9030
+ handleAdminNetworkCommercialReadModel,
8134
9031
  // API requests must never fall through to an SPA asset response. Hosts still
8135
9032
  // get first refusal through options.routes, then this terminates unknown API
8136
9033
  // paths with an explicit machine-readable 404.