@odla-ai/chapter 0.43.2 → 0.45.0

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