@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.
package/dist/index.cjs CHANGED
@@ -122,6 +122,7 @@ __export(index_exports, {
122
122
  joinConfig: () => joinConfig,
123
123
  joinConfigTiers: () => joinConfigTiers,
124
124
  leaderCrmConfig: () => leaderCrmConfig,
125
+ listStripeObjects: () => listStripeObjects,
125
126
  loadLatestAdmissionRevision: () => loadLatestAdmissionRevision,
126
127
  loadOfferableTiers: () => loadOfferableTiers,
127
128
  loadTiers: () => loadTiers,
@@ -136,9 +137,11 @@ __export(index_exports, {
136
137
  memberSystemRollbackPlan: () => memberSystemRollbackPlan,
137
138
  membershipPageDocument: () => membershipPageDocument,
138
139
  membershipTierPresentationFromMarkdown: () => membershipTierPresentationFromMarkdown,
140
+ networkCommercialSourceKey: () => networkCommercialSourceKey,
139
141
  networkSourceTag: () => networkSourceTag,
140
142
  normalizeAdmissionEmail: () => normalizeAdmissionEmail,
141
143
  normalizeChapterEmail: () => normalizeChapterEmail,
144
+ normalizeNetworkCommercialStripeEvent: () => normalizeNetworkCommercialStripeEvent,
142
145
  normalizeSharedRecord: () => normalizeSharedRecord,
143
146
  normalizeWebhookEvent: () => normalizeWebhookEvent,
144
147
  offerableTiers: () => offerableTiers,
@@ -151,10 +154,12 @@ __export(index_exports, {
151
154
  projectBuilderLibraryDelivery: () => projectBuilderLibraryDelivery,
152
155
  projectMemberContent: () => projectMemberContent,
153
156
  projectMemberSelf: () => projectMemberSelf,
157
+ projectNetworkCommercialEvents: () => projectNetworkCommercialEvents,
154
158
  projectSharedRecord: () => projectSharedRecord,
155
159
  promoteBuilderLibraryResource: () => promoteBuilderLibraryResource,
156
160
  publicGiftOffers: () => publicGiftOffers,
157
161
  reconcileMeetings: () => reconcileMeetings,
162
+ reconcileStripeSnapshot: () => reconcileStripeSnapshot,
158
163
  refundedPatch: () => refundedPatch,
159
164
  render: () => render,
160
165
  renderSignupControlThemeHead: () => renderSignupControlThemeHead,
@@ -168,6 +173,8 @@ __export(index_exports, {
168
173
  resolveJoinContinuation: () => resolveJoinContinuation,
169
174
  resolveLeaderFormation: () => resolveLeaderFormation,
170
175
  resolveMemberContent: () => resolveMemberContent,
176
+ resolveNetworkCommercialReadModel: () => resolveNetworkCommercialReadModel,
177
+ resolveNetworkPersonRegistry: () => resolveNetworkPersonRegistry,
171
178
  resolvePipeline: () => resolvePipeline,
172
179
  resolveScheduling: () => resolveScheduling,
173
180
  resolveSignupControl: () => resolveSignupControl,
@@ -240,7 +247,7 @@ function resolveApplication(a) {
240
247
  throw new Error(`defineChapter.application.${name}: field "${unsafe}" must match ${SCHEMA_FIELD.source}`);
241
248
  }
242
249
  }
243
- const duplicate = [...required, ...optional].find((field, index, fields) => fields.indexOf(field) !== index);
250
+ const duplicate = [...required, ...optional].find((field, index2, fields) => fields.indexOf(field) !== index2);
244
251
  if (duplicate) {
245
252
  throw new Error(`defineChapter.application: duplicate field "${duplicate}" across required/optional lists`);
246
253
  }
@@ -319,8 +326,8 @@ async function submitApplication(db, chapter, fields, opts) {
319
326
  if (app.requireDisclaimerAck && !acked) {
320
327
  return { ok: false, error: "disclaimerAck is required" };
321
328
  }
322
- const id4 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
323
- const row = { id: id4, status: chapter.pipeline.initial, createdAt: opts.now };
329
+ const id6 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
330
+ const row = { id: id6, status: chapter.pipeline.initial, createdAt: opts.now };
324
331
  for (const f of [...app.required, ...app.optional]) {
325
332
  if (fieldStates[f]?.visible === false) continue;
326
333
  if (typeof fields[f] === "string") row[f] = fields[f].trim();
@@ -344,19 +351,19 @@ async function submitApplication(db, chapter, fields, opts) {
344
351
  }
345
352
  if (acked) row.disclaimerAckAt = opts.now;
346
353
  const ops = [
347
- { t: "update", ns: "applications", id: id4, attrs: row }
354
+ { t: "update", ns: "applications", id: id6, attrs: row }
348
355
  ];
349
356
  if (opts.admissionGrant?.decision === "waive_interview") {
350
357
  ops.push({ t: "update", ns: "admissionGrants", id: opts.admissionGrant.id, attrs: {
351
358
  consumedAt: opts.now,
352
- consumedApplicationId: id4
359
+ consumedApplicationId: id6
353
360
  } });
354
361
  }
355
362
  const { duplicate } = await db.transact(
356
363
  ops,
357
364
  opts.submissionId ? { mutationId: `join:${opts.submissionId}` } : void 0
358
365
  );
359
- return { ok: true, id: id4, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
366
+ return { ok: true, id: id6, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
360
367
  }
361
368
  function joinConfig(group, paymentsReady2, tiers2 = [], conditions = {}) {
362
369
  return {
@@ -670,6 +677,146 @@ var stripeEventReceiptsEntity = {
670
677
  }
671
678
  };
672
679
 
680
+ // src/schema-network-person.ts
681
+ var string = (flags = {}) => ({
682
+ type: "string",
683
+ unique: flags.unique ?? false,
684
+ indexed: flags.indexed ?? false,
685
+ optional: flags.optional ?? false
686
+ });
687
+ var number = (flags = {}) => ({
688
+ type: "number",
689
+ unique: false,
690
+ indexed: flags.indexed ?? false,
691
+ optional: flags.optional ?? false
692
+ });
693
+ var json = (flags = {}) => ({
694
+ type: "json",
695
+ unique: false,
696
+ indexed: false,
697
+ optional: flags.optional ?? false
698
+ });
699
+ var id3 = () => string({ unique: true, indexed: true });
700
+ var networkPersonsEntity = { attrs: {
701
+ id: id3(),
702
+ displayName: string({ indexed: true }),
703
+ state: string({ indexed: true }),
704
+ mergedIntoId: string({ indexed: true, optional: true }),
705
+ contactPolicy: json(),
706
+ revision: number({ indexed: true }),
707
+ createdAt: number({ indexed: true }),
708
+ updatedAt: number({ indexed: true })
709
+ } };
710
+ var networkIdentityClaimsEntity = { attrs: {
711
+ id: id3(),
712
+ personId: string({ indexed: true }),
713
+ kind: string({ indexed: true }),
714
+ value: string(),
715
+ hint: string({ optional: true }),
716
+ verifiedAt: number({ indexed: true }),
717
+ createdAt: number({ indexed: true })
718
+ } };
719
+ var networkPersonAliasesEntity = { attrs: {
720
+ id: id3(),
721
+ sourceSystem: string({ indexed: true }),
722
+ sourceRecordId: string({ indexed: true }),
723
+ sourceKey: string({ unique: true, indexed: true }),
724
+ displayName: string({ indexed: true }),
725
+ provenance: string(),
726
+ consent: json(),
727
+ status: string({ indexed: true }),
728
+ personId: string({ indexed: true, optional: true }),
729
+ candidatePersonIds: json({ optional: true }),
730
+ identityClaims: json(),
731
+ chapterLinks: json(),
732
+ revision: number({ indexed: true }),
733
+ createdAt: number({ indexed: true }),
734
+ updatedAt: number({ indexed: true })
735
+ } };
736
+ var networkChapterPersonLinksEntity = { attrs: {
737
+ id: id3(),
738
+ personId: string({ indexed: true }),
739
+ chapterId: string({ indexed: true }),
740
+ chapterPersonId: string({ indexed: true }),
741
+ state: string({ indexed: true }),
742
+ createdAt: number({ indexed: true })
743
+ } };
744
+ var networkPersonEventsEntity = { attrs: {
745
+ id: id3(),
746
+ kind: string({ indexed: true }),
747
+ personId: string({ indexed: true, optional: true }),
748
+ aliasId: string({ indexed: true, optional: true }),
749
+ actorId: string({ indexed: true }),
750
+ mutationId: string({ unique: true, indexed: true }),
751
+ detail: json(),
752
+ createdAt: number({ indexed: true })
753
+ } };
754
+ var networkPersonEntities = {
755
+ networkPersons: networkPersonsEntity,
756
+ networkIdentityClaims: networkIdentityClaimsEntity,
757
+ networkPersonAliases: networkPersonAliasesEntity,
758
+ networkChapterPersonLinks: networkChapterPersonLinksEntity,
759
+ networkPersonEvents: networkPersonEventsEntity
760
+ };
761
+
762
+ // src/schema-network-commercial.ts
763
+ var string2 = (flags = {}) => ({
764
+ type: "string",
765
+ unique: flags.unique ?? false,
766
+ indexed: flags.indexed ?? false,
767
+ optional: flags.optional ?? false
768
+ });
769
+ var number2 = (flags = {}) => ({
770
+ type: "number",
771
+ unique: false,
772
+ indexed: flags.indexed ?? false,
773
+ optional: flags.optional ?? false
774
+ });
775
+ var boolean = (flags = {}) => ({
776
+ type: "boolean",
777
+ unique: false,
778
+ indexed: flags.indexed ?? false,
779
+ optional: false
780
+ });
781
+ var json2 = () => ({ type: "json", unique: false, indexed: false, optional: false });
782
+ var id4 = () => string2({ unique: true, indexed: true });
783
+ var events = { attrs: {
784
+ id: id4(),
785
+ eventId: string2({ unique: true, indexed: true }),
786
+ digest: string2({ indexed: true }),
787
+ eventType: string2({ indexed: true }),
788
+ eventCreated: number2({ indexed: true }),
789
+ livemode: boolean({ indexed: true }),
790
+ fact: json2(),
791
+ receivedAt: number2({ indexed: true })
792
+ } };
793
+ var projection = () => ({ attrs: {
794
+ id: id4(),
795
+ providerId: string2({ indexed: true }),
796
+ sourceKey: string2({ indexed: true }),
797
+ status: string2({ indexed: true }),
798
+ snapshot: json2(),
799
+ eventCreated: number2({ indexed: true }),
800
+ updatedAt: number2({ indexed: true })
801
+ } });
802
+ var exceptions = { attrs: {
803
+ id: id4(),
804
+ eventId: string2({ indexed: true }),
805
+ kind: string2({ indexed: true }),
806
+ sourceKey: string2({ indexed: true, optional: true }),
807
+ status: string2({ indexed: true }),
808
+ detail: json2(),
809
+ updatedAt: number2({ indexed: true })
810
+ } };
811
+ var networkCommercialEntities = {
812
+ networkCommercialEvents: events,
813
+ networkCommercialCustomers: projection(),
814
+ networkCommercialSubscriptions: projection(),
815
+ networkCommercialInvoices: projection(),
816
+ networkCommercialRefunds: projection(),
817
+ networkCommercialExceptions: exceptions
818
+ };
819
+
673
820
  // src/schema.ts
674
821
  function attr5(type, flags = {}) {
675
822
  return {
@@ -679,10 +826,10 @@ function attr5(type, flags = {}) {
679
826
  optional: flags.optional ?? false
680
827
  };
681
828
  }
682
- var id3 = () => attr5("string", { unique: true, indexed: true });
829
+ var id5 = () => attr5("string", { unique: true, indexed: true });
683
830
  var admins = {
684
831
  attrs: {
685
- id: id3(),
832
+ id: id5(),
686
833
  email: attr5("string", { unique: true, indexed: true }),
687
834
  name: attr5("string", { optional: true }),
688
835
  note: attr5("string", { optional: true })
@@ -690,7 +837,7 @@ var admins = {
690
837
  };
691
838
  var superAdmins = {
692
839
  attrs: {
693
- id: id3(),
840
+ id: id5(),
694
841
  email: attr5("string", { unique: true, indexed: true }),
695
842
  note: attr5("string", { optional: true }),
696
843
  createdAt: attr5("number", { indexed: true })
@@ -698,7 +845,7 @@ var superAdmins = {
698
845
  };
699
846
  var networkNotes = {
700
847
  attrs: {
701
- id: id3(),
848
+ id: id5(),
702
849
  recordKey: attr5("string", { indexed: true }),
703
850
  targetId: attr5("string", { indexed: true }),
704
851
  recordType: attr5("string", { indexed: true }),
@@ -711,7 +858,7 @@ var networkNotes = {
711
858
  };
712
859
  var signupControls = {
713
860
  attrs: {
714
- id: id3(),
861
+ id: id5(),
715
862
  sourceId: attr5("string", { indexed: true }),
716
863
  targetId: attr5("string", { indexed: true }),
717
864
  environment: attr5("string", { indexed: true }),
@@ -724,7 +871,7 @@ var signupControls = {
724
871
  };
725
872
  var memberContentControls = {
726
873
  attrs: {
727
- id: id3(),
874
+ id: id5(),
728
875
  sourceId: attr5("string", { indexed: true }),
729
876
  targetId: attr5("string", { indexed: true }),
730
877
  environment: attr5("string", { indexed: true }),
@@ -737,7 +884,7 @@ var memberContentControls = {
737
884
  };
738
885
  var memberContentLocal = {
739
886
  attrs: {
740
- id: id3(),
887
+ id: id5(),
741
888
  chapterId: attr5("string", { indexed: true }),
742
889
  environment: attr5("string", { indexed: true }),
743
890
  revision: attr5("number", { indexed: true }),
@@ -748,7 +895,7 @@ var memberContentLocal = {
748
895
  };
749
896
  var builderLibraryControls = {
750
897
  attrs: {
751
- id: id3(),
898
+ id: id5(),
752
899
  sourceId: attr5("string", { indexed: true }),
753
900
  targetId: attr5("string", { indexed: true }),
754
901
  environment: attr5("string", { indexed: true }),
@@ -761,7 +908,7 @@ var builderLibraryControls = {
761
908
  };
762
909
  var builderLibraryLocal = {
763
910
  attrs: {
764
- id: id3(),
911
+ id: id5(),
765
912
  appId: attr5("string", { indexed: true }),
766
913
  environment: attr5("string", { indexed: true }),
767
914
  revision: attr5("number", { indexed: true }),
@@ -772,7 +919,7 @@ var builderLibraryLocal = {
772
919
  };
773
920
  var groups = {
774
921
  attrs: {
775
- id: id3(),
922
+ id: id5(),
776
923
  name: attr5("string"),
777
924
  standardPriceCents: attr5("number"),
778
925
  foundingDiscountCents: attr5("number"),
@@ -795,7 +942,7 @@ var groups = {
795
942
  };
796
943
  var tiers = {
797
944
  attrs: {
798
- id: id3(),
945
+ id: id5(),
799
946
  groupId: attr5("string", { indexed: true }),
800
947
  name: attr5("string"),
801
948
  priceCents: attr5("number"),
@@ -808,7 +955,7 @@ var tiers = {
808
955
  };
809
956
  var meetings = {
810
957
  attrs: {
811
- id: id3(),
958
+ id: id5(),
812
959
  applicationId: attr5("string", { indexed: true }),
813
960
  groupId: attr5("string", { indexed: true }),
814
961
  startAt: attr5("number", { indexed: true }),
@@ -827,7 +974,7 @@ var meetings = {
827
974
  };
828
975
  var emailLog = {
829
976
  attrs: {
830
- id: id3(),
977
+ id: id5(),
831
978
  groupId: attr5("string", { indexed: true }),
832
979
  applicationId: attr5("string", { indexed: true, optional: true }),
833
980
  to: attr5("string", { indexed: true }),
@@ -842,7 +989,7 @@ var emailLog = {
842
989
  sentAt: attr5("number", { indexed: true })
843
990
  }
844
991
  };
845
- function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false) {
992
+ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false, includeNetworkPersonRegistry = false, includeNetworkCommercialReadModel = false) {
846
993
  const entities = {};
847
994
  if (mode === "chapter") {
848
995
  entities.applications = applicationsFor(application);
@@ -876,6 +1023,10 @@ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolv
876
1023
  entities.leaderAdmissionDeliveries = leaderAdmissionDeliveriesEntity;
877
1024
  entities.leaderAdmissionMutations = leaderAdmissionMutationsEntity;
878
1025
  }
1026
+ if (includeNetworkPersonRegistry) {
1027
+ Object.assign(entities, networkPersonEntities);
1028
+ }
1029
+ if (includeNetworkCommercialReadModel) Object.assign(entities, networkCommercialEntities);
879
1030
  const schema = { entities, links: {} };
880
1031
  const rules = {};
881
1032
  for (const ns of Object.keys(entities)) {
@@ -1047,8 +1198,8 @@ function resolveAuth(mode, auth) {
1047
1198
  return { source, claim, ladder, adminRole, superAdmins: superAdmins2 };
1048
1199
  }
1049
1200
  function roleFromClaim(payload, auth) {
1050
- const raw = payload[auth.claim];
1051
- return typeof raw === "string" && auth.ladder.includes(raw) ? raw : auth.ladder[0];
1201
+ const raw2 = payload[auth.claim];
1202
+ return typeof raw2 === "string" && auth.ladder.includes(raw2) ? raw2 : auth.ladder[0];
1052
1203
  }
1053
1204
  function isAdminRole(role, auth) {
1054
1205
  return role === auth.adminRole;
@@ -1527,9 +1678,9 @@ var APPLICATION_LABELS = {
1527
1678
  phone: "Phone",
1528
1679
  state: "State"
1529
1680
  };
1530
- function humanize(id4) {
1531
- const text4 = id4.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1532
- return text4 ? `${text4[0].toUpperCase()}${text4.slice(1)}` : id4;
1681
+ function humanize(id6) {
1682
+ const text5 = id6.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1683
+ return text5 ? `${text5[0].toUpperCase()}${text5.slice(1)}` : id6;
1533
1684
  }
1534
1685
  function applicationDefs(chapter) {
1535
1686
  try {
@@ -1538,23 +1689,23 @@ function applicationDefs(chapter) {
1538
1689
  return {};
1539
1690
  }
1540
1691
  }
1541
- function applicationType(id4, def) {
1692
+ function applicationType(id6, def) {
1542
1693
  if (def && ["string", "email", "url", "enum"].includes(def.type)) return def.type;
1543
- return id4 === "email" ? "email" : id4 === "linkedin" ? "url" : "string";
1694
+ return id6 === "email" ? "email" : id6 === "linkedin" ? "url" : "string";
1544
1695
  }
1545
1696
  function applicationFlowFields(chapter, application = chapter.application) {
1546
1697
  const defs = applicationDefs(chapter);
1547
- return [...application.required, ...application.optional].map((id4, order) => {
1548
- const def = defs[id4];
1549
- const conditions = application.conditions[id4];
1550
- const type = applicationType(id4, def);
1698
+ return [...application.required, ...application.optional].map((id6, order2) => {
1699
+ const def = defs[id6];
1700
+ const conditions = application.conditions[id6];
1701
+ const type = applicationType(id6, def);
1551
1702
  return {
1552
- id: id4,
1553
- label: def?.label ?? APPLICATION_LABELS[id4] ?? humanize(id4),
1703
+ id: id6,
1704
+ label: def?.label ?? APPLICATION_LABELS[id6] ?? humanize(id6),
1554
1705
  type,
1555
- required: application.required.includes(id4),
1556
- order,
1557
- maxLength: application.maxLen[id4] ?? application.defaultMaxLen,
1706
+ required: application.required.includes(id6),
1707
+ order: order2,
1708
+ maxLength: application.maxLen[id6] ?? application.defaultMaxLen,
1558
1709
  ...type === "enum" && def?.options ? { options: [...def.options] } : {},
1559
1710
  ...conditions ? { conditions: { ...conditions } } : {},
1560
1711
  ...type === "email" ? { emailIdentity: "literal" } : {}
@@ -1564,17 +1715,17 @@ function applicationFlowFields(chapter, application = chapter.application) {
1564
1715
  function resolveFormationFlowFields(crm, formation) {
1565
1716
  if (!formation.enabled) return [];
1566
1717
  const def = crm.type(formation.type);
1567
- return [...formation.required, ...formation.optional].map((id4, order) => {
1568
- const field = def.fields[id4];
1718
+ return [...formation.required, ...formation.optional].map((id6, order2) => {
1719
+ const field = def.fields[id6];
1569
1720
  return {
1570
- id: id4,
1571
- label: field.label ?? humanize(id4),
1721
+ id: id6,
1722
+ label: field.label ?? humanize(id6),
1572
1723
  type: field.type,
1573
- required: formation.required.includes(id4),
1574
- order,
1575
- maxLength: formation.maxLen[id4] ?? formation.defaultMaxLen,
1724
+ required: formation.required.includes(id6),
1725
+ order: order2,
1726
+ maxLength: formation.maxLen[id6] ?? formation.defaultMaxLen,
1576
1727
  ...field.options ? { options: [...field.options] } : {},
1577
- ...id4 === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
1728
+ ...id6 === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
1578
1729
  };
1579
1730
  });
1580
1731
  }
@@ -1614,7 +1765,7 @@ function resolveLeaderFormation(input, crm) {
1614
1765
  throw new Error(`defineChapter.formation.${name}: must be an array of field-name strings`);
1615
1766
  }
1616
1767
  }
1617
- const duplicates = [...required, ...optional].filter((field, index, fields) => fields.indexOf(field) !== index);
1768
+ const duplicates = [...required, ...optional].filter((field, index2, fields) => fields.indexOf(field) !== index2);
1618
1769
  if (duplicates.length) throw new Error(`defineChapter.formation: duplicate public field "${duplicates[0]}"`);
1619
1770
  const unknown = [...required, ...optional].filter((field) => !def.fields[field]);
1620
1771
  if (unknown.length) throw new Error(`defineChapter.formation: field "${unknown[0]}" is not declared on CRM type "${type}"`);
@@ -1665,7 +1816,7 @@ var SLUG = /^[a-z0-9][a-z0-9-]{1,62}$/;
1665
1816
  var FIELD = /^[a-z][a-zA-Z0-9_]*$/;
1666
1817
  var BINDING = /^[A-Z][A-Z0-9_]{0,127}$/;
1667
1818
  function validateSharedNotes(opts) {
1668
- const duplicate = opts.values.some((type, index) => opts.values.indexOf(type) !== index);
1819
+ const duplicate = opts.values.some((type, index2) => opts.values.indexOf(type) !== index2);
1669
1820
  const invalid = opts.values.some((type) => !FIELD.test(type) || !opts.crm.config.types[type] || (opts.fields ? !opts.fields[type] : false));
1670
1821
  if (duplicate || invalid) {
1671
1822
  throw new Error(`${opts.path}: must contain unique CRM types allowed by fields`);
@@ -1675,7 +1826,7 @@ function validateEditableFields(opts) {
1675
1826
  for (const [type, names] of Object.entries(opts.values)) {
1676
1827
  const readable = opts.readable?.[type];
1677
1828
  const def = opts.crm.config.types[type];
1678
- const duplicate = names.some((name, index) => names.indexOf(name) !== index);
1829
+ const duplicate = names.some((name, index2) => names.indexOf(name) !== index2);
1679
1830
  const unknown = names.some((name) => !FIELD.test(name) || !def?.fields[name]);
1680
1831
  const hidden = opts.readable ? !readable || names.some((name) => !readable.includes(name)) : false;
1681
1832
  if (!FIELD.test(type) || names.length === 0 || duplicate || !def || unknown || hidden) {
@@ -1686,7 +1837,7 @@ function validateEditableFields(opts) {
1686
1837
  }
1687
1838
  }
1688
1839
  function validateStageTransitions(opts) {
1689
- const duplicate = opts.values.some((type, index) => opts.values.indexOf(type) !== index);
1840
+ const duplicate = opts.values.some((type, index2) => opts.values.indexOf(type) !== index2);
1690
1841
  const invalid = opts.values.some((type) => !FIELD.test(type) || !opts.crm.config.types[type]?.pipeline || (opts.readable ? !opts.readable[type] : false));
1691
1842
  if (duplicate || invalid) {
1692
1843
  throw new Error(`${opts.path}: must contain unique, readable CRM types with pipelines`);
@@ -1868,12 +2019,58 @@ function resolveNetwork(config, crm) {
1868
2019
  return { targets, readers };
1869
2020
  }
1870
2021
 
2022
+ // src/config-network-person.ts
2023
+ var SLUG2 = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
2024
+ function resolveNetworkPersonRegistry(mode, input, targetIds) {
2025
+ if (!input) return null;
2026
+ if (mode !== "hub") throw new Error("defineChapter.networkPersonRegistry: only hub mode may own network Person authority");
2027
+ if (input.mode !== "shadow") throw new Error('defineChapter.networkPersonRegistry.mode: must be "shadow"');
2028
+ const chapterIds = input.chapterIds ? [...input.chapterIds] : [...targetIds];
2029
+ if (chapterIds.length === 0) throw new Error("defineChapter.networkPersonRegistry.chapterIds: at least one chapter is required");
2030
+ if (new Set(chapterIds).size !== chapterIds.length || chapterIds.some((id6) => !SLUG2.test(id6))) {
2031
+ throw new Error("defineChapter.networkPersonRegistry.chapterIds: expected distinct lowercase slugs");
2032
+ }
2033
+ const unknown = chapterIds.filter((id6) => !targetIds.includes(id6));
2034
+ if (unknown.length) {
2035
+ throw new Error(`defineChapter.networkPersonRegistry.chapterIds: ${unknown.map((id6) => `"${id6}"`).join(", ")} are not configured network targets`);
2036
+ }
2037
+ return { mode: "shadow", chapterIds };
2038
+ }
2039
+
2040
+ // src/config-network-commercial.ts
2041
+ var SLUG3 = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
2042
+ var TOKEN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
2043
+ function networkCommercialSourceKey(source) {
2044
+ return [source.stripeMode, source.appId, source.environment, source.dataEnvironment, source.runtime, source.chapterId].join(":");
2045
+ }
2046
+ function resolveNetworkCommercialReadModel(mode, input, targetIds) {
2047
+ if (!input) return null;
2048
+ if (mode !== "hub") throw new Error("defineChapter.networkCommercialReadModel: only hub mode may own the network commercial projection");
2049
+ if (input.mode !== "shadow") throw new Error('defineChapter.networkCommercialReadModel.mode: must be "shadow"');
2050
+ if (!Array.isArray(input.sources) || input.sources.length === 0) {
2051
+ throw new Error("defineChapter.networkCommercialReadModel.sources: at least one exact source is required");
2052
+ }
2053
+ const sources = input.sources.map((source) => ({ ...source }));
2054
+ for (const source of sources) {
2055
+ if (!SLUG3.test(source.appId) || !SLUG3.test(source.chapterId) || !TOKEN.test(source.environment) || !TOKEN.test(source.dataEnvironment) || !TOKEN.test(source.runtime) || source.stripeMode !== "test" && source.stripeMode !== "live") {
2056
+ throw new Error("defineChapter.networkCommercialReadModel.sources: every source requires valid app, chapter, environment, data-environment, runtime, and Stripe mode values");
2057
+ }
2058
+ if (!targetIds.includes(source.chapterId)) {
2059
+ throw new Error(`defineChapter.networkCommercialReadModel.sources: chapter "${source.chapterId}" is not a configured network target`);
2060
+ }
2061
+ }
2062
+ if (new Set(sources.map(networkCommercialSourceKey)).size !== sources.length) {
2063
+ throw new Error("defineChapter.networkCommercialReadModel.sources: exact source partitions must be distinct");
2064
+ }
2065
+ return { mode: "shadow", sources };
2066
+ }
2067
+
1871
2068
  // src/config-signup-control.ts
1872
- var SLUG2 = /^[a-z0-9][a-z0-9-]{1,62}$/;
2069
+ var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1873
2070
  var SECRET_NAME = /^[a-z][a-z0-9_]{1,127}$/;
1874
2071
  function resolveSignupControl(input) {
1875
2072
  if (!input) return null;
1876
- if (!SLUG2.test(input.sourceId)) {
2073
+ if (!SLUG4.test(input.sourceId)) {
1877
2074
  throw new Error("defineChapter.signupControl.sourceId: must be a lowercase app slug");
1878
2075
  }
1879
2076
  if (input.stripeMode !== "test" && input.stripeMode !== "live") {
@@ -1887,12 +2084,12 @@ function resolveSignupControl(input) {
1887
2084
  }
1888
2085
 
1889
2086
  // src/config-member-content.ts
1890
- var SLUG3 = /^[a-z0-9][a-z0-9-]{1,62}$/;
2087
+ var SLUG5 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1891
2088
  var SECRET_NAME2 = /^[a-z][a-z0-9_]{1,127}$/;
1892
2089
  function resolveMemberContent(input) {
1893
2090
  if (!input) return null;
1894
2091
  const parent = input.parent;
1895
- if (parent && !SLUG3.test(parent.sourceId)) {
2092
+ if (parent && !SLUG5.test(parent.sourceId)) {
1896
2093
  throw new Error("defineChapter.memberContent.parent.sourceId: must be a lowercase app slug");
1897
2094
  }
1898
2095
  const secretName = parent?.secretName?.trim() || "member_content_control_secret";
@@ -1907,22 +2104,22 @@ function resolveMemberContent(input) {
1907
2104
  }
1908
2105
 
1909
2106
  // src/config-builder-library.ts
1910
- var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
2107
+ var SLUG6 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1911
2108
  function resolveBuilderLibrary(mode, input, network = { targets: [], readers: [] }) {
1912
2109
  if (!input) return null;
1913
2110
  if (input.sources !== void 0 && !Array.isArray(input.sources)) {
1914
2111
  throw new Error("defineChapter.builderLibrary.sources: must be an array");
1915
2112
  }
1916
2113
  const seen = /* @__PURE__ */ new Set();
1917
- const sources = (input.sources ?? []).map((source, index) => {
1918
- if (!source || typeof source !== "object" || !SLUG4.test(source.sourceId)) {
1919
- throw new Error(`defineChapter.builderLibrary.sources[${index}].sourceId: must be a lowercase app slug`);
2114
+ const sources = (input.sources ?? []).map((source, index2) => {
2115
+ if (!source || typeof source !== "object" || !SLUG6.test(source.sourceId)) {
2116
+ throw new Error(`defineChapter.builderLibrary.sources[${index2}].sourceId: must be a lowercase app slug`);
1920
2117
  }
1921
2118
  if (source.sourceId === "built-not-found" && mode === "hub") {
1922
- throw new Error(`defineChapter.builderLibrary.sources[${index}]: a hub accepts Chapter sources, not itself`);
2119
+ throw new Error(`defineChapter.builderLibrary.sources[${index2}]: a hub accepts Chapter sources, not itself`);
1923
2120
  }
1924
2121
  if (mode === "chapter" && source.sourceId !== "built-not-found") {
1925
- throw new Error(`defineChapter.builderLibrary.sources[${index}]: Chapters may accept only the built-not-found source`);
2122
+ throw new Error(`defineChapter.builderLibrary.sources[${index2}]: Chapters may accept only the built-not-found source`);
1926
2123
  }
1927
2124
  if (seen.has(source.sourceId)) {
1928
2125
  throw new Error(`defineChapter.builderLibrary.sources: duplicate source "${source.sourceId}"`);
@@ -1930,14 +2127,14 @@ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: []
1930
2127
  seen.add(source.sourceId);
1931
2128
  if ("secretName" in source) {
1932
2129
  throw new Error(
1933
- `defineChapter.builderLibrary.sources[${index}].secretName: remove this field; Builder Library reuses the Chapter network edge`
2130
+ `defineChapter.builderLibrary.sources[${index2}].secretName: remove this field; Builder Library reuses the Chapter network edge`
1934
2131
  );
1935
2132
  }
1936
2133
  const networkPeer = mode === "hub" ? network.targets.some((target) => target.id === source.sourceId) : network.readers.some((reader) => reader.id === source.sourceId);
1937
2134
  if (!networkPeer) {
1938
2135
  const side = mode === "hub" ? "targets" : "readers";
1939
2136
  throw new Error(
1940
- `defineChapter.builderLibrary.sources[${index}]: "${source.sourceId}" must already be configured in network.${side}`
2137
+ `defineChapter.builderLibrary.sources[${index2}]: "${source.sourceId}" must already be configured in network.${side}`
1941
2138
  );
1942
2139
  }
1943
2140
  return { sourceId: source.sourceId };
@@ -1946,15 +2143,15 @@ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: []
1946
2143
  }
1947
2144
 
1948
2145
  // src/config.ts
1949
- var SLUG5 = /^[a-z0-9][a-z0-9-]{1,62}$/;
2146
+ var SLUG7 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1950
2147
  function isResolvedCrm(x) {
1951
2148
  return !!x && typeof x === "object" && "prepare" in x && typeof x.prepare === "function";
1952
2149
  }
1953
2150
  function defineChapter(config) {
1954
2151
  if (!config || typeof config !== "object") throw new Error("defineChapter: a config object is required");
1955
- const { id: id4, name } = config;
1956
- if (typeof id4 !== "string" || !SLUG5.test(id4)) {
1957
- throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id4)}`);
2152
+ const { id: id6, name } = config;
2153
+ if (typeof id6 !== "string" || !SLUG7.test(id6)) {
2154
+ throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id6)}`);
1958
2155
  }
1959
2156
  if (typeof name !== "string" || name.trim() === "") throw new Error("defineChapter.name: a non-empty name is required");
1960
2157
  const mode = config.mode ?? "chapter";
@@ -1977,6 +2174,16 @@ function defineChapter(config) {
1977
2174
  const brand = { ...config.brand, wordmark: config.brand?.wordmark ?? name };
1978
2175
  const copy = resolveChapterCopy(config.copy);
1979
2176
  const network = resolveNetwork(config, crm);
2177
+ const networkPersonRegistry = resolveNetworkPersonRegistry(
2178
+ mode,
2179
+ config.networkPersonRegistry,
2180
+ network.targets.map((target) => target.id)
2181
+ );
2182
+ const networkCommercialReadModel = resolveNetworkCommercialReadModel(
2183
+ mode,
2184
+ config.networkCommercialReadModel,
2185
+ network.targets.map((target) => target.id)
2186
+ );
1980
2187
  const signupControl = resolveSignupControl(config.signupControl);
1981
2188
  const memberContent = resolveMemberContent(config.memberContent);
1982
2189
  const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary, network);
@@ -1995,7 +2202,9 @@ function defineChapter(config) {
1995
2202
  Boolean(signupControl),
1996
2203
  Boolean(memberContent),
1997
2204
  Boolean(builderLibrary),
1998
- network.targets.some((target) => target.admissionGrants)
2205
+ network.targets.some((target) => target.admissionGrants),
2206
+ Boolean(networkPersonRegistry),
2207
+ Boolean(networkCommercialReadModel)
1999
2208
  );
2000
2209
  const services = config.services ?? ["db", "calendar", "o11y"];
2001
2210
  const account = config.account ?? "none";
@@ -2045,7 +2254,7 @@ function defineChapter(config) {
2045
2254
  };
2046
2255
  const chapter = {
2047
2256
  config,
2048
- id: id4,
2257
+ id: id6,
2049
2258
  name,
2050
2259
  mode,
2051
2260
  brand,
@@ -2054,6 +2263,8 @@ function defineChapter(config) {
2054
2263
  signupControl,
2055
2264
  memberContent,
2056
2265
  builderLibrary,
2266
+ networkPersonRegistry,
2267
+ networkCommercialReadModel,
2057
2268
  formation,
2058
2269
  crm,
2059
2270
  auth,
@@ -2071,6 +2282,304 @@ function defineChapter(config) {
2071
2282
  return chapter;
2072
2283
  }
2073
2284
 
2285
+ // src/network-commercial.ts
2286
+ var raw = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
2287
+ var text = (value) => typeof value === "string" && value.length > 0 && value.length <= 255 ? value : void 0;
2288
+ var integer = (value) => typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
2289
+ var bool = (value) => typeof value === "boolean" ? value : void 0;
2290
+ var providerId = (value, prefix) => {
2291
+ const valueText = text(value);
2292
+ return valueText && (!prefix || valueText.startsWith(prefix)) ? valueText : void 0;
2293
+ };
2294
+ function metadataCandidates(object) {
2295
+ const parent = raw(object.parent);
2296
+ const subscriptionDetails = raw(object.subscription_details);
2297
+ const parentSubscription = raw(parent.subscription_details);
2298
+ return [raw(object.metadata), raw(subscriptionDetails.metadata), raw(parentSubscription.metadata)];
2299
+ }
2300
+ function metadataValue(candidates, key) {
2301
+ return candidates.map((candidate) => text(candidate[key])).find(Boolean);
2302
+ }
2303
+ function refId(value, prefix) {
2304
+ if (typeof value === "string") return providerId(value, prefix);
2305
+ return providerId(raw(value).id, prefix);
2306
+ }
2307
+ function subscriptionRef(object) {
2308
+ return refId(object.subscription, "sub_") ?? refId(raw(object.subscription_details).subscription, "sub_") ?? refId(raw(raw(object.parent).subscription_details).subscription, "sub_");
2309
+ }
2310
+ function firstPrice(object) {
2311
+ const data = raw(object.items).data;
2312
+ const item = Array.isArray(data) ? raw(data[0]) : {};
2313
+ const priceId = refId(item.price, "price_");
2314
+ const quantity = integer(item.quantity);
2315
+ return { ...priceId ? { priceId } : {}, ...quantity !== void 0 ? { quantity } : {} };
2316
+ }
2317
+ function normalizeNetworkCommercialStripeEvent(input, digest) {
2318
+ const event = raw(input);
2319
+ const eventId = providerId(event.id, "evt_") ?? "";
2320
+ const eventType = text(event.type) ?? "";
2321
+ const object = raw(raw(event.data).object);
2322
+ const objectId = providerId(object.id) ?? "";
2323
+ const candidates = metadataCandidates(object);
2324
+ const identity = {};
2325
+ const identityKeys = {
2326
+ appId: "odlaAppId",
2327
+ chapterId: "chapterId",
2328
+ environment: "odlaEnvironment",
2329
+ dataEnvironment: "odlaDataEnvironment",
2330
+ runtime: "odlaRuntime"
2331
+ };
2332
+ for (const [target, source] of Object.entries(identityKeys)) {
2333
+ const value = metadataValue(candidates, source);
2334
+ if (value) identity[target] = value;
2335
+ }
2336
+ let objectKind = "ignored";
2337
+ if (eventType.startsWith("customer.subscription.")) objectKind = "subscription";
2338
+ else if (eventType.startsWith("customer.")) objectKind = "customer";
2339
+ else if (eventType.startsWith("invoice.")) objectKind = "invoice";
2340
+ else if (eventType.startsWith("refund.")) objectKind = "refund";
2341
+ else if (eventType === "charge.refunded") objectKind = "refund";
2342
+ const customerId = refId(object.customer, "cus_");
2343
+ const subscriptionId = objectKind === "subscription" ? providerId(objectId, "sub_") : subscriptionRef(object);
2344
+ const invoiceId = objectKind === "invoice" ? providerId(objectId, "in_") : refId(object.invoice, "in_");
2345
+ const chargeId = eventType === "charge.refunded" ? providerId(objectId, "ch_") : refId(object.charge, "ch_");
2346
+ const paymentIntentId = refId(object.payment_intent, "pi_");
2347
+ const price = firstPrice(object);
2348
+ const amountCents = integer(eventType === "charge.refunded" ? object.amount_refunded : object.amount);
2349
+ const currency = text(object.currency)?.toLowerCase();
2350
+ const status = text(object.status);
2351
+ return {
2352
+ eventId,
2353
+ digest,
2354
+ eventType,
2355
+ eventCreated: integer(event.created) ?? 0,
2356
+ livemode: event.livemode === true,
2357
+ objectKind,
2358
+ objectId,
2359
+ identity,
2360
+ ...metadataValue(candidates, "applicationId") ? { applicationId: metadataValue(candidates, "applicationId") } : {},
2361
+ ...metadataValue(candidates, "personId") ? { personId: metadataValue(candidates, "personId") } : {},
2362
+ ...metadataValue(candidates, "membershipId") ? { membershipId: metadataValue(candidates, "membershipId") } : {},
2363
+ ...customerId ? { customerId } : {},
2364
+ ...subscriptionId ? { subscriptionId } : {},
2365
+ ...invoiceId ? { invoiceId } : {},
2366
+ ...chargeId ? { chargeId } : {},
2367
+ ...paymentIntentId ? { paymentIntentId } : {},
2368
+ ...price,
2369
+ ...status ? { status } : {},
2370
+ ...currency ? { currency } : {},
2371
+ ...amountCents !== void 0 ? { amountCents } : {},
2372
+ ...integer(object.amount_paid) !== void 0 ? { amountPaidCents: integer(object.amount_paid) } : {},
2373
+ ...integer(object.amount_remaining) !== void 0 ? { amountRemainingCents: integer(object.amount_remaining) } : {},
2374
+ ...integer(object.current_period_start) !== void 0 ? { currentPeriodStart: integer(object.current_period_start) } : {},
2375
+ ...integer(object.current_period_end) !== void 0 ? { currentPeriodEnd: integer(object.current_period_end) } : {},
2376
+ ...bool(object.cancel_at_period_end) !== void 0 ? { cancelAtPeriodEnd: bool(object.cancel_at_period_end) } : {}
2377
+ };
2378
+ }
2379
+ var identityFields = ["appId", "chapterId", "environment", "dataEnvironment", "runtime"];
2380
+ function directSource(fact, sources) {
2381
+ const mode = fact.livemode ? "live" : "test";
2382
+ const supplied = identityFields.filter((field) => fact.identity[field] !== void 0);
2383
+ const candidates = sources.filter((source) => source.stripeMode === mode && supplied.every((field) => source[field] === fact.identity[field]));
2384
+ if (supplied.length === identityFields.length) return candidates.length === 1 ? candidates[0] : "wrong";
2385
+ if (supplied.length > 0 && candidates.length === 0) return "wrong";
2386
+ return null;
2387
+ }
2388
+ var objectRefs = (fact) => [
2389
+ fact.customerId && `customer:${fact.customerId}`,
2390
+ fact.subscriptionId && `subscription:${fact.subscriptionId}`,
2391
+ fact.invoiceId && `invoice:${fact.invoiceId}`,
2392
+ fact.chargeId && `charge:${fact.chargeId}`,
2393
+ fact.paymentIntentId && `payment_intent:${fact.paymentIntentId}`
2394
+ ].filter((value) => Boolean(value));
2395
+ function ownRef(fact) {
2396
+ if (!fact.objectId) return null;
2397
+ if (fact.objectKind === "customer") return `customer:${fact.objectId}`;
2398
+ if (fact.objectKind === "subscription") return `subscription:${fact.objectId}`;
2399
+ if (fact.objectKind === "invoice") return `invoice:${fact.objectId}`;
2400
+ if (fact.eventType.startsWith("refund.")) return `refund:${fact.objectId}`;
2401
+ return fact.eventType === "charge.refunded" ? `charge:${fact.objectId}` : null;
2402
+ }
2403
+ var order = (left, right) => left.eventCreated - right.eventCreated || left.eventId.localeCompare(right.eventId);
2404
+ function projectNetworkCommercialEvents(input, sources) {
2405
+ const facts2 = [...input].sort(order);
2406
+ const exceptions2 = [];
2407
+ const unique = [];
2408
+ const digests = /* @__PURE__ */ new Map();
2409
+ for (const fact of facts2) {
2410
+ const prior = digests.get(fact.eventId);
2411
+ if (prior === void 0) {
2412
+ digests.set(fact.eventId, fact.digest);
2413
+ unique.push(fact);
2414
+ } else if (prior !== fact.digest) exceptions2.push({ eventId: fact.eventId, kind: "collision", detail: { eventType: fact.eventType, objectId: fact.objectId } });
2415
+ }
2416
+ const assignments = /* @__PURE__ */ new Map();
2417
+ const refs = /* @__PURE__ */ new Map();
2418
+ for (const fact of unique) {
2419
+ const direct = directSource(fact, sources);
2420
+ if (direct && direct !== "wrong") assignments.set(fact.eventId, direct);
2421
+ }
2422
+ let changed = true;
2423
+ while (changed) {
2424
+ changed = false;
2425
+ for (const fact of unique) {
2426
+ const assigned = assignments.get(fact.eventId);
2427
+ if (assigned) {
2428
+ const key = networkCommercialSourceKey(assigned);
2429
+ for (const ref of [...objectRefs(fact), ownRef(fact)].filter((value) => Boolean(value))) {
2430
+ const keys2 = refs.get(ref) ?? /* @__PURE__ */ new Set();
2431
+ const before = keys2.size;
2432
+ keys2.add(key);
2433
+ refs.set(ref, keys2);
2434
+ if (keys2.size !== before) changed = true;
2435
+ }
2436
+ continue;
2437
+ }
2438
+ if (directSource(fact, sources) === "wrong") continue;
2439
+ const keys = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
2440
+ if (keys.size !== 1) continue;
2441
+ const candidate = sources.find((source) => networkCommercialSourceKey(source) === [...keys][0]);
2442
+ if (candidate && directSource({ ...fact, identity: fact.identity }, [candidate]) !== "wrong") {
2443
+ assignments.set(fact.eventId, candidate);
2444
+ changed = true;
2445
+ }
2446
+ }
2447
+ }
2448
+ const latest = /* @__PURE__ */ new Map();
2449
+ for (const fact of unique) {
2450
+ if (fact.objectKind === "ignored" || !fact.eventId || !fact.objectId) {
2451
+ exceptions2.push({ eventId: fact.eventId, kind: "unsupported", detail: { eventType: fact.eventType, objectId: fact.objectId } });
2452
+ continue;
2453
+ }
2454
+ const source = assignments.get(fact.eventId);
2455
+ if (!source) {
2456
+ const direct = directSource(fact, sources);
2457
+ const keys = new Set(objectRefs(fact).flatMap((ref) => [...refs.get(ref) ?? []]));
2458
+ exceptions2.push({ eventId: fact.eventId, kind: direct === "wrong" ? "wrong_partition" : keys.size > 1 ? "ambiguous_partition" : "unmatched_partition", detail: { eventType: fact.eventType, objectId: fact.objectId } });
2459
+ continue;
2460
+ }
2461
+ const row = { ...fact, sourceKey: networkCommercialSourceKey(source) };
2462
+ const key = `${fact.objectKind}:${fact.objectId}`;
2463
+ const prior = latest.get(key);
2464
+ if (!prior || order(prior, fact) <= 0) latest.set(key, row);
2465
+ }
2466
+ const rows3 = [...latest.values()];
2467
+ const customers = rows3.filter((row) => row.objectKind === "customer");
2468
+ const subscriptions = rows3.filter((row) => row.objectKind === "subscription");
2469
+ const invoices = rows3.filter((row) => row.objectKind === "invoice");
2470
+ const refunds = rows3.filter((row) => row.objectKind === "refund" && row.eventType.startsWith("refund."));
2471
+ for (const charge of rows3.filter((row) => row.eventType === "charge.refunded")) {
2472
+ if (!refunds.some((refund) => refund.chargeId === charge.objectId)) {
2473
+ exceptions2.push({ eventId: charge.eventId, kind: "charge_refund_without_refund_object", sourceKey: charge.sourceKey, detail: { eventType: charge.eventType, objectId: charge.objectId } });
2474
+ }
2475
+ }
2476
+ const totalsMap = /* @__PURE__ */ new Map();
2477
+ const add = (row, field, amount) => {
2478
+ if (!row.currency) return;
2479
+ const key = `${row.sourceKey}:${row.currency}`;
2480
+ const total = totalsMap.get(key) ?? { sourceKey: row.sourceKey, currency: row.currency, paidCents: 0, refundedCents: 0, netCents: 0 };
2481
+ total[field] += amount;
2482
+ total.netCents = total.paidCents - total.refundedCents;
2483
+ totalsMap.set(key, total);
2484
+ };
2485
+ for (const row of invoices) add(row, "paidCents", row.amountPaidCents ?? 0);
2486
+ for (const row of refunds) add(row, "refundedCents", row.amountCents ?? 0);
2487
+ return { customers, subscriptions, invoices, refunds, exceptions: exceptions2, totals: [...totalsMap.values()].sort((a, b) => `${a.sourceKey}:${a.currency}`.localeCompare(`${b.sourceKey}:${b.currency}`)) };
2488
+ }
2489
+
2490
+ // src/network-commercial-reconcile.ts
2491
+ var asRaw = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
2492
+ async function listStripeObjects(client, path, params = {}, maxPages = 20) {
2493
+ const objects = [];
2494
+ let startingAfter;
2495
+ for (let pageNumber = 0; pageNumber < maxPages; pageNumber += 1) {
2496
+ const page = await client.request("GET", path, {
2497
+ ...params,
2498
+ limit: 100,
2499
+ ...startingAfter ? { starting_after: startingAfter } : {}
2500
+ });
2501
+ if (!Array.isArray(page.data) || typeof page.has_more !== "boolean") {
2502
+ throw new Error("Stripe returned an invalid commercial list response");
2503
+ }
2504
+ const rows3 = page.data.map(asRaw).filter((row) => row !== null);
2505
+ if (rows3.length !== page.data.length) throw new Error("Stripe returned a malformed commercial object");
2506
+ objects.push(...rows3);
2507
+ if (!page.has_more) return { objects, complete: true };
2508
+ const cursor = rows3.at(-1)?.id;
2509
+ if (typeof cursor !== "string" || !cursor) throw new Error("Stripe pagination did not provide a cursor");
2510
+ startingAfter = cursor;
2511
+ }
2512
+ return { objects, complete: false };
2513
+ }
2514
+ function facts(snapshots) {
2515
+ const groups2 = [
2516
+ ["customer.updated", snapshots.customers.objects],
2517
+ ["customer.subscription.updated", snapshots.subscriptions.objects],
2518
+ ["invoice.updated", snapshots.invoices.objects],
2519
+ ["refund.updated", snapshots.refunds.objects]
2520
+ ];
2521
+ return groups2.flatMap(([eventType, objects]) => objects.map((object) => {
2522
+ const id6 = typeof object.id === "string" ? object.id : "invalid";
2523
+ return normalizeNetworkCommercialStripeEvent({
2524
+ id: `evt_snapshot_${eventType.replaceAll(".", "_")}_${id6}`,
2525
+ type: eventType,
2526
+ created: typeof object.created === "number" ? object.created : 0,
2527
+ livemode: object.livemode === true,
2528
+ data: { object }
2529
+ }, `stripe-snapshot:${eventType}:${id6}`);
2530
+ }));
2531
+ }
2532
+ var selected = (row) => ({
2533
+ status: row.status ?? null,
2534
+ currency: row.currency ?? null,
2535
+ amountCents: row.amountCents ?? null,
2536
+ amountPaidCents: row.amountPaidCents ?? null,
2537
+ amountRemainingCents: row.amountRemainingCents ?? null,
2538
+ customerId: row.customerId ?? null,
2539
+ subscriptionId: row.subscriptionId ?? null,
2540
+ invoiceId: row.invoiceId ?? null,
2541
+ priceId: row.priceId ?? null,
2542
+ quantity: row.quantity ?? null,
2543
+ applicationId: row.applicationId ?? null,
2544
+ personId: row.personId ?? null,
2545
+ membershipId: row.membershipId ?? null,
2546
+ currentPeriodStart: row.currentPeriodStart ?? null,
2547
+ currentPeriodEnd: row.currentPeriodEnd ?? null,
2548
+ cancelAtPeriodEnd: row.cancelAtPeriodEnd ?? null
2549
+ });
2550
+ var index = (projection2) => new Map(
2551
+ [...projection2.customers, ...projection2.subscriptions, ...projection2.invoices, ...projection2.refunds].map((row) => [`${row.sourceKey}:${row.objectKind}:${row.objectId}`, row])
2552
+ );
2553
+ function reconcileStripeSnapshot(shadow, snapshots, sources) {
2554
+ const stripe = projectNetworkCommercialEvents(facts(snapshots), sources);
2555
+ const shadowRows = index(shadow);
2556
+ const stripeRows = index(stripe);
2557
+ const exceptions2 = [];
2558
+ const complete = Object.values(snapshots).every((snapshot) => snapshot.complete);
2559
+ if (!complete) exceptions2.push({ kind: "stripe_snapshot_truncated", detail: {} });
2560
+ for (const [key, row] of shadowRows) {
2561
+ const other = stripeRows.get(key);
2562
+ if (!other) {
2563
+ exceptions2.push({ kind: "missing_from_stripe", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: {} });
2564
+ } else if (JSON.stringify(selected(row)) !== JSON.stringify(selected(other))) {
2565
+ exceptions2.push({ kind: "object_mismatch", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: { shadow: selected(row), stripe: selected(other) } });
2566
+ }
2567
+ }
2568
+ for (const [key, row] of stripeRows) {
2569
+ if (!shadowRows.has(key)) exceptions2.push({ kind: "missing_from_shadow", sourceKey: row.sourceKey, objectKind: row.objectKind, providerId: row.objectId, detail: {} });
2570
+ }
2571
+ if (JSON.stringify(shadow.totals) !== JSON.stringify(stripe.totals)) {
2572
+ exceptions2.push({ kind: "totals_mismatch", detail: { shadow: shadow.totals, stripe: stripe.totals } });
2573
+ }
2574
+ return {
2575
+ ok: exceptions2.length === 0,
2576
+ complete,
2577
+ ignoredObjects: stripe.exceptions.filter((item) => item.kind === "wrong_partition" || item.kind === "unmatched_partition").length,
2578
+ exceptions: exceptions2,
2579
+ stripeTotals: stripe.totals
2580
+ };
2581
+ }
2582
+
2074
2583
  // src/leader.ts
2075
2584
  var import_crm3 = require("@odla-ai/crm");
2076
2585
  function leaderCrmConfig(options = {}) {
@@ -2239,14 +2748,14 @@ var import_crm4 = require("@odla-ai/crm");
2239
2748
 
2240
2749
  // src/tiers.ts
2241
2750
  function buildTierSeeds(groupId, configured = [], now = 0) {
2242
- return configured.map((tier, index) => ({
2751
+ return configured.map((tier, index2) => ({
2243
2752
  id: tier.id,
2244
2753
  groupId,
2245
2754
  name: tier.name,
2246
2755
  priceCents: tier.priceCents,
2247
2756
  ...tier.stripePriceId ? { stripePriceId: tier.stripePriceId } : {},
2248
2757
  ...tier.blurb ? { blurb: tier.blurb } : {},
2249
- sortOrder: tier.sortOrder ?? index,
2758
+ sortOrder: tier.sortOrder ?? index2,
2250
2759
  active: tier.active !== false,
2251
2760
  createdAt: now
2252
2761
  }));
@@ -2319,13 +2828,26 @@ function createChapterIntegration(chapter, options = {}) {
2319
2828
  const isLeader = (chapter.config.network?.targets?.length ?? 0) > 0;
2320
2829
  const runbooks = isLeader ? {
2321
2830
  role: "leader",
2322
- slugs: ["chapter-leader", "chapter-network", "crm"],
2831
+ slugs: ["chapter-leader", "chapter-network", "crm", ...chapter.networkCommercialReadModel ? ["stripe-network-shadow"] : []],
2323
2832
  guidance: `${chapter.name} is a LEADER hub: it holds the CRM and shares records with its chapters. Read it with \`odla-ai runbook get chapter-leader\`.`
2324
2833
  } : {
2325
2834
  role: "follower",
2326
2835
  slugs: ["chapter-follower", "chapter-network"],
2327
2836
  guidance: `${chapter.name} is a FOLLOWER site: it receives people and records from a leader hub. Read it with \`odla-ai runbook get chapter-follower\`.`
2328
2837
  };
2838
+ const secrets = [];
2839
+ if (chapter.account !== "none") secrets.push({
2840
+ // Released Chapter workers request this effective name. odla-db maps it to
2841
+ // the canonical reserved `$clerk_secret` value written by the CLI.
2842
+ name: "clerk_secret_key",
2843
+ description: "Clerk backend key (set with odla-ai secrets set-clerk-key) required by the configured Chapter account model.",
2844
+ required: true
2845
+ });
2846
+ if (chapter.networkCommercialReadModel) secrets.push({
2847
+ name: "network_commercial_stripe_webhook_secret",
2848
+ description: "Signing secret for the read-only network commercial Stripe webhook endpoint.",
2849
+ required: true
2850
+ });
2329
2851
  return {
2330
2852
  id: "chapter",
2331
2853
  title: `Chapter: ${chapter.name}`,
@@ -2336,14 +2858,7 @@ function createChapterIntegration(chapter, options = {}) {
2336
2858
  },
2337
2859
  rules: { ...crmDesc.rules, ...chapter.rules },
2338
2860
  seeds,
2339
- secrets: chapter.account === "none" ? [] : [{
2340
- // Released Chapter workers request this effective name. odla-db maps
2341
- // it to the canonical reserved `$clerk_secret` value written by
2342
- // `odla-ai secrets set-clerk-key`.
2343
- name: "clerk_secret_key",
2344
- description: "Clerk backend key (set with odla-ai secrets set-clerk-key) required by the configured Chapter account model.",
2345
- required: true
2346
- }],
2861
+ secrets,
2347
2862
  probes: [
2348
2863
  ...crmDesc.probes ?? [],
2349
2864
  // The snapshot route is deliberately private. A credential-free smoke
@@ -2365,6 +2880,11 @@ function createChapterIntegration(chapter, options = {}) {
2365
2880
  { path: "/api/builder-library-control", expectedStatus: 401 },
2366
2881
  { path: "/api/admin/builder-library", expectedStatus: 401 },
2367
2882
  { path: "/api/admin/builder-library/manifest", expectedStatus: 401 }
2883
+ ] : [],
2884
+ ...chapter.networkPersonRegistry ? [{ path: "/api/admin/network-person-registry", expectedStatus: 401 }] : [],
2885
+ ...chapter.networkCommercialReadModel ? [
2886
+ { path: "/api/network-commercial/stripe-webhook", expectedStatus: 405 },
2887
+ { path: "/api/admin/network-commercial-read-model", expectedStatus: 401 }
2368
2888
  ] : []
2369
2889
  ],
2370
2890
  runbooks
@@ -2373,7 +2893,7 @@ function createChapterIntegration(chapter, options = {}) {
2373
2893
 
2374
2894
  // src/member-content-validation-helpers.ts
2375
2895
  var ID = /^[a-z][a-z0-9-]{0,79}$/;
2376
- var SLUG6 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2896
+ var SLUG8 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2377
2897
  var ROOT_KEYS = /* @__PURE__ */ new Set(["schemaVersion", "revision", "generatedAt", "source", "target", "pages", "upsellOverrides"]);
2378
2898
  var SOURCE_KEYS = /* @__PURE__ */ new Set(["type", "appId"]);
2379
2899
  var TARGET_KEYS = /* @__PURE__ */ new Set(["chapterId", "environment"]);
@@ -2405,21 +2925,21 @@ function textValue(value, path, errors, max = MAX_TEXT, required = false) {
2405
2925
  return valueText;
2406
2926
  }
2407
2927
  function offerValue(value, path, errors, tiers2, audience) {
2408
- const raw = record(value);
2409
- if (!raw) {
2928
+ const raw2 = record(value);
2929
+ if (!raw2) {
2410
2930
  errors.push(`${path} must be an object`);
2411
2931
  return void 0;
2412
2932
  }
2413
- unknownKeys(raw, OFFER_KEYS, path, errors);
2414
- const tierId = textValue(raw.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2933
+ unknownKeys(raw2, OFFER_KEYS, path, errors);
2934
+ const tierId = textValue(raw2.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2415
2935
  if (tierId && !ID.test(tierId)) errors.push(`${path}.tierId must be a lowercase kebab-case id`);
2416
2936
  if (tiers2.size && !tiers2.has(tierId)) errors.push(`${path}.tierId must reference a declared tier`);
2417
2937
  if (audience?.kind === "tiers" && tierId && !audience.tierIds.includes(tierId)) {
2418
2938
  errors.push(`${path}.tierId must grant access to this page`);
2419
2939
  }
2420
- const label = textValue(raw.label, `${path}.label`, errors);
2421
- const bodyMarkdown = textValue(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN);
2422
- const ctaLabel = textValue(raw.ctaLabel, `${path}.ctaLabel`, errors);
2940
+ const label = textValue(raw2.label, `${path}.label`, errors);
2941
+ const bodyMarkdown = textValue(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN);
2942
+ const ctaLabel = textValue(raw2.ctaLabel, `${path}.ctaLabel`, errors);
2423
2943
  return {
2424
2944
  tierId,
2425
2945
  ...label ? { label } : {},
@@ -2471,37 +2991,37 @@ function validateMemberContentDocument(input, options = {}) {
2471
2991
  const ids = /* @__PURE__ */ new Set();
2472
2992
  const slugs = /* @__PURE__ */ new Set();
2473
2993
  const pages = [];
2474
- for (const [index, value] of rawPages.slice(0, MAX_PAGES).entries()) {
2475
- const path = `document.pages[${index}]`;
2476
- const raw = record(value);
2477
- if (!raw) {
2994
+ for (const [index2, value] of rawPages.slice(0, MAX_PAGES).entries()) {
2995
+ const path = `document.pages[${index2}]`;
2996
+ const raw2 = record(value);
2997
+ if (!raw2) {
2478
2998
  errors.push(`${path} must be an object`);
2479
2999
  continue;
2480
3000
  }
2481
- unknownKeys(raw, PAGE_KEYS, path, errors);
2482
- const id4 = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2483
- const slug = textValue(raw.slug, `${path}.slug`, errors, 120, true) ?? "";
2484
- const title = textValue(raw.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
2485
- if (id4 && !ID.test(id4)) errors.push(`${path}.id must be a lowercase kebab-case id`);
2486
- if (slug && !SLUG6.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
2487
- if (ids.has(id4) || knownParentIds.has(id4)) errors.push(`${path}.id duplicates page "${id4}"`);
3001
+ unknownKeys(raw2, PAGE_KEYS, path, errors);
3002
+ const id6 = textValue(raw2.id, `${path}.id`, errors, 80, true) ?? "";
3003
+ const slug = textValue(raw2.slug, `${path}.slug`, errors, 120, true) ?? "";
3004
+ const title = textValue(raw2.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
3005
+ if (id6 && !ID.test(id6)) errors.push(`${path}.id must be a lowercase kebab-case id`);
3006
+ if (slug && !SLUG8.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
3007
+ if (ids.has(id6) || knownParentIds.has(id6)) errors.push(`${path}.id duplicates page "${id6}"`);
2488
3008
  if (slugs.has(slug) || (options.parentPages ?? []).some((page) => page.slug === slug)) errors.push(`${path}.slug duplicates "${slug}"`);
2489
- ids.add(id4);
3009
+ ids.add(id6);
2490
3010
  slugs.add(slug);
2491
- const navRaw = record(raw.navigation);
2492
- let navigation = { section: "General", order: index };
3011
+ const navRaw = record(raw2.navigation);
3012
+ let navigation = { section: "General", order: index2 };
2493
3013
  if (!navRaw) errors.push(`${path}.navigation must be an object`);
2494
3014
  else {
2495
3015
  unknownKeys(navRaw, NAV_KEYS, `${path}.navigation`, errors);
2496
3016
  const section = textValue(navRaw.section, `${path}.navigation.section`, errors, 120, true) ?? "";
2497
- const order = navRaw.order;
2498
- if (!Number.isInteger(order) || Number(order) < 0 || Number(order) > 1e6) {
3017
+ const order2 = navRaw.order;
3018
+ if (!Number.isInteger(order2) || Number(order2) < 0 || Number(order2) > 1e6) {
2499
3019
  errors.push(`${path}.navigation.order must be an integer from 0 to 1,000,000`);
2500
3020
  }
2501
3021
  const parentId = textValue(navRaw.parentId, `${path}.navigation.parentId`, errors, 80);
2502
- navigation = { section, order: Number.isInteger(order) ? Number(order) : index, ...parentId ? { parentId } : {} };
3022
+ navigation = { section, order: Number.isInteger(order2) ? Number(order2) : index2, ...parentId ? { parentId } : {} };
2503
3023
  }
2504
- const audienceRaw = record(raw.audience);
3024
+ const audienceRaw = record(raw2.audience);
2505
3025
  let audience = { kind: "all-members" };
2506
3026
  if (!audienceRaw) errors.push(`${path}.audience must be an object`);
2507
3027
  else {
@@ -2523,12 +3043,12 @@ function validateMemberContentDocument(input, options = {}) {
2523
3043
  audience = { kind: "tiers", tierIds: normalized };
2524
3044
  } else errors.push(`${path}.audience.kind must be all-members or tiers`);
2525
3045
  }
2526
- const summary = textValue(raw.summary, `${path}.summary`, errors, 2e3);
2527
- const teaserMarkdown = textValue(raw.teaserMarkdown, `${path}.teaserMarkdown`, errors, MAX_MARKDOWN);
2528
- const bodyMarkdown = textValue(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN, true) ?? "";
3046
+ const summary = textValue(raw2.summary, `${path}.summary`, errors, 2e3);
3047
+ const teaserMarkdown = textValue(raw2.teaserMarkdown, `${path}.teaserMarkdown`, errors, MAX_MARKDOWN);
3048
+ const bodyMarkdown = textValue(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN, true) ?? "";
2529
3049
  let upsell;
2530
- if (raw.upsell !== void 0) {
2531
- const upsellRaw = record(raw.upsell);
3050
+ if (raw2.upsell !== void 0) {
3051
+ const upsellRaw = record(raw2.upsell);
2532
3052
  if (!upsellRaw) errors.push(`${path}.upsell must be an object`);
2533
3053
  else {
2534
3054
  unknownKeys(upsellRaw, UPSELL_KEYS, `${path}.upsell`, errors);
@@ -2542,13 +3062,13 @@ function validateMemberContentDocument(input, options = {}) {
2542
3062
  else if (owner === "chapter") upsell = { owner, ...offer ? { offer } : {} };
2543
3063
  }
2544
3064
  }
2545
- pages.push({ id: id4, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
3065
+ pages.push({ id: id6, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2546
3066
  }
2547
3067
  const allIds = /* @__PURE__ */ new Set([...knownParentIds, ...ids]);
2548
- for (const [index, page] of pages.entries()) {
3068
+ for (const [index2, page] of pages.entries()) {
2549
3069
  const parentId = page.navigation.parentId;
2550
- if (parentId && !allIds.has(parentId)) errors.push(`document.pages[${index}].navigation.parentId references unknown page "${parentId}"`);
2551
- if (parentId === page.id) errors.push(`document.pages[${index}].navigation.parentId cannot reference itself`);
3070
+ if (parentId && !allIds.has(parentId)) errors.push(`document.pages[${index2}].navigation.parentId references unknown page "${parentId}"`);
3071
+ if (parentId === page.id) errors.push(`document.pages[${index2}].navigation.parentId cannot reference itself`);
2552
3072
  }
2553
3073
  for (const page of pages) {
2554
3074
  const seen = /* @__PURE__ */ new Set([page.id]);
@@ -2569,21 +3089,21 @@ function validateMemberContentDocument(input, options = {}) {
2569
3089
  const values = Array.isArray(root.upsellOverrides) ? root.upsellOverrides : [];
2570
3090
  const seen = /* @__PURE__ */ new Set();
2571
3091
  upsellOverrides = [];
2572
- for (const [index, value] of values.entries()) {
2573
- const path = `document.upsellOverrides[${index}]`;
2574
- const raw = record(value);
2575
- if (!raw) {
3092
+ for (const [index2, value] of values.entries()) {
3093
+ const path = `document.upsellOverrides[${index2}]`;
3094
+ const raw2 = record(value);
3095
+ if (!raw2) {
2576
3096
  errors.push(`${path} must be an object`);
2577
3097
  continue;
2578
3098
  }
2579
- unknownKeys(raw, OVERRIDE_KEYS, path, errors);
2580
- const pageId = textValue(raw.pageId, `${path}.pageId`, errors, 80, true) ?? "";
3099
+ unknownKeys(raw2, OVERRIDE_KEYS, path, errors);
3100
+ const pageId = textValue(raw2.pageId, `${path}.pageId`, errors, 80, true) ?? "";
2581
3101
  if (seen.has(pageId)) errors.push(`${path}.pageId duplicates "${pageId}"`);
2582
3102
  seen.add(pageId);
2583
3103
  const parentPage = (options.parentPages ?? []).find((page) => page.id === pageId);
2584
3104
  if (!parentPage) errors.push(`${path}.pageId must reference a parent page`);
2585
3105
  else if (parentPage.upsell?.owner !== "chapter") errors.push(`${path}.pageId does not delegate upsell authority to the chapter`);
2586
- const offer = offerValue(raw.offer, `${path}.offer`, errors, tierIds, parentPage?.audience);
3106
+ const offer = offerValue(raw2.offer, `${path}.offer`, errors, tierIds, parentPage?.audience);
2587
3107
  if (offer) upsellOverrides.push({ pageId, offer });
2588
3108
  }
2589
3109
  }
@@ -2708,7 +3228,7 @@ var record2 = (value) => value !== null && typeof value === "object" && !Array.i
2708
3228
  function unknownKeys2(value, allowed2, path, errors) {
2709
3229
  for (const key of Object.keys(value)) if (!allowed2.has(key)) errors.push(`${path}.${key} is not supported`);
2710
3230
  }
2711
- function text(value, path, errors, max = MAX_TEXT2, required = true) {
3231
+ function text2(value, path, errors, max = MAX_TEXT2, required = true) {
2712
3232
  if (value === void 0 && !required) return void 0;
2713
3233
  if (typeof value !== "string" || required && !value.trim()) {
2714
3234
  errors.push(`${path} must be a non-empty string`);
@@ -2718,23 +3238,23 @@ function text(value, path, errors, max = MAX_TEXT2, required = true) {
2718
3238
  if (result.length > max) errors.push(`${path} must be at most ${max} characters`);
2719
3239
  return result;
2720
3240
  }
2721
- function integer(value, path, errors, minimum = 1) {
3241
+ function integer2(value, path, errors, minimum = 1) {
2722
3242
  if (!Number.isSafeInteger(value) || Number(value) < minimum) errors.push(`${path} must be an integer >= ${minimum}`);
2723
3243
  return Number(value);
2724
3244
  }
2725
3245
  function scopeValue(value, path, errors) {
2726
- const raw = record2(value);
2727
- if (!raw) {
3246
+ const raw2 = record2(value);
3247
+ if (!raw2) {
2728
3248
  errors.push(`${path} must be an object`);
2729
3249
  return null;
2730
3250
  }
2731
- unknownKeys2(raw, SCOPE_KEYS, path, errors);
2732
- if (raw.kind === "global" || raw.kind === "built_not_found") {
2733
- if (raw.chapterId !== void 0) errors.push(`${path}.chapterId is allowed only for chapter scope`);
2734
- return { kind: raw.kind };
3251
+ unknownKeys2(raw2, SCOPE_KEYS, path, errors);
3252
+ if (raw2.kind === "global" || raw2.kind === "built_not_found") {
3253
+ if (raw2.chapterId !== void 0) errors.push(`${path}.chapterId is allowed only for chapter scope`);
3254
+ return { kind: raw2.kind };
2735
3255
  }
2736
- if (raw.kind === "chapter") {
2737
- const chapterId = text(raw.chapterId, `${path}.chapterId`, errors, 63) ?? "";
3256
+ if (raw2.kind === "chapter") {
3257
+ const chapterId = text2(raw2.chapterId, `${path}.chapterId`, errors, 63) ?? "";
2738
3258
  if (chapterId && !APP_ID.test(chapterId)) errors.push(`${path}.chapterId must be a lowercase app slug`);
2739
3259
  return { kind: "chapter", chapterId };
2740
3260
  }
@@ -2742,28 +3262,28 @@ function scopeValue(value, path, errors) {
2742
3262
  return null;
2743
3263
  }
2744
3264
  function resourceValue(value, path, errors) {
2745
- const raw = record2(value);
2746
- if (!raw) {
3265
+ const raw2 = record2(value);
3266
+ if (!raw2) {
2747
3267
  errors.push(`${path} must be an object`);
2748
3268
  return null;
2749
3269
  }
2750
- unknownKeys2(raw, RESOURCE_KEYS, path, errors);
2751
- const id4 = text(raw.id, `${path}.id`, errors, 80) ?? "";
2752
- if (id4 && !ID2.test(id4)) errors.push(`${path}.id must be lowercase kebab-case`);
2753
- const kind = text(raw.kind, `${path}.kind`, errors) ?? "";
3270
+ unknownKeys2(raw2, RESOURCE_KEYS, path, errors);
3271
+ const id6 = text2(raw2.id, `${path}.id`, errors, 80) ?? "";
3272
+ if (id6 && !ID2.test(id6)) errors.push(`${path}.id must be lowercase kebab-case`);
3273
+ const kind = text2(raw2.kind, `${path}.kind`, errors) ?? "";
2754
3274
  if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
2755
- const title = text(raw.title, `${path}.title`, errors, 240) ?? "";
2756
- const summary = text(raw.summary, `${path}.summary`, errors, 1e3, false);
2757
- const status = text(raw.status, `${path}.status`, errors) ?? "";
3275
+ const title = text2(raw2.title, `${path}.title`, errors, 240) ?? "";
3276
+ const summary = text2(raw2.summary, `${path}.summary`, errors, 1e3, false);
3277
+ const status = text2(raw2.status, `${path}.status`, errors) ?? "";
2758
3278
  if (!STATUSES.has(status)) errors.push(`${path}.status must be draft, published, or archived`);
2759
- const revision = integer(raw.revision, `${path}.revision`, errors);
2760
- const scope = scopeValue(raw.scope, `${path}.scope`, errors) ?? { kind: "global" };
2761
- const tags = Array.isArray(raw.tags) ? raw.tags.map((tag, index) => text(tag, `${path}.tags[${index}]`, errors, 80) ?? "") : [];
2762
- if (!Array.isArray(raw.tags)) errors.push(`${path}.tags must be an array`);
3279
+ const revision = integer2(raw2.revision, `${path}.revision`, errors);
3280
+ const scope = scopeValue(raw2.scope, `${path}.scope`, errors) ?? { kind: "global" };
3281
+ const tags = Array.isArray(raw2.tags) ? raw2.tags.map((tag, index2) => text2(tag, `${path}.tags[${index2}]`, errors, 80) ?? "") : [];
3282
+ if (!Array.isArray(raw2.tags)) errors.push(`${path}.tags must be an array`);
2763
3283
  if (tags.length > MAX_TAGS) errors.push(`${path}.tags may contain at most ${MAX_TAGS} items`);
2764
3284
  if (new Set(tags).size !== tags.length) errors.push(`${path}.tags must be unique`);
2765
- const bodyMarkdown = text(raw.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, false);
2766
- const url = text(raw.url, `${path}.url`, errors, 2048, false);
3285
+ const bodyMarkdown = text2(raw2.bodyMarkdown, `${path}.bodyMarkdown`, errors, MAX_MARKDOWN2, false);
3286
+ const url = text2(raw2.url, `${path}.url`, errors, 2048, false);
2767
3287
  if (url) {
2768
3288
  try {
2769
3289
  if (new URL(url).protocol !== "https:") errors.push(`${path}.url must use https`);
@@ -2771,13 +3291,13 @@ function resourceValue(value, path, errors) {
2771
3291
  errors.push(`${path}.url must be a valid https URL`);
2772
3292
  }
2773
3293
  }
2774
- const asset = raw.brandAsset === void 0 ? null : record2(raw.brandAsset);
2775
- if (raw.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
3294
+ const asset = raw2.brandAsset === void 0 ? null : record2(raw2.brandAsset);
3295
+ if (raw2.brandAsset !== void 0 && !asset) errors.push(`${path}.brandAsset must be an object`);
2776
3296
  if (asset) unknownKeys2(asset, ASSET_KEYS, `${path}.brandAsset`, errors);
2777
3297
  const brandAsset = asset ? {
2778
- bookId: text(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
2779
- assetId: text(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
2780
- ...asset.filename === void 0 ? {} : { filename: text(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
3298
+ bookId: text2(asset.bookId, `${path}.brandAsset.bookId`, errors, 160) ?? "",
3299
+ assetId: text2(asset.assetId, `${path}.brandAsset.assetId`, errors, 160) ?? "",
3300
+ ...asset.filename === void 0 ? {} : { filename: text2(asset.filename, `${path}.brandAsset.filename`, errors, 240) ?? "" }
2781
3301
  } : void 0;
2782
3302
  if ((kind === "document" || kind === "note") && !bodyMarkdown) errors.push(`${path}.bodyMarkdown is required for ${kind}`);
2783
3303
  if ((kind === "link" || kind === "inspiration") && !url) errors.push(`${path}.url is required for ${kind}`);
@@ -2785,59 +3305,59 @@ function resourceValue(value, path, errors) {
2785
3305
  if (!["document", "note"].includes(kind) && bodyMarkdown) errors.push(`${path}.bodyMarkdown is not allowed for ${kind}`);
2786
3306
  if (!["link", "inspiration"].includes(kind) && url) errors.push(`${path}.url is not allowed for ${kind}`);
2787
3307
  if (!["image", "file"].includes(kind) && brandAsset) errors.push(`${path}.brandAsset is not allowed for ${kind}`);
2788
- const provenanceRaw = record2(raw.provenance);
3308
+ const provenanceRaw = record2(raw2.provenance);
2789
3309
  if (!provenanceRaw) errors.push(`${path}.provenance must be an object`);
2790
3310
  if (provenanceRaw) unknownKeys2(provenanceRaw, PROVENANCE_KEYS, `${path}.provenance`, errors);
2791
3311
  const promotedRaw = provenanceRaw?.promotedFrom === void 0 ? null : record2(provenanceRaw.promotedFrom);
2792
3312
  if (provenanceRaw?.promotedFrom !== void 0 && !promotedRaw) errors.push(`${path}.provenance.promotedFrom must be an object`);
2793
3313
  if (promotedRaw) unknownKeys2(promotedRaw, PROMOTION_KEYS, `${path}.provenance.promotedFrom`, errors);
2794
3314
  const promotedFrom = promotedRaw ? {
2795
- chapterId: text(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
2796
- resourceId: text(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
2797
- resourceRevision: integer(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
2798
- documentRevision: integer(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
2799
- promotedBy: text(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
2800
- promotedAt: integer(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
3315
+ chapterId: text2(promotedRaw.chapterId, `${path}.provenance.promotedFrom.chapterId`, errors, 63) ?? "",
3316
+ resourceId: text2(promotedRaw.resourceId, `${path}.provenance.promotedFrom.resourceId`, errors, 80) ?? "",
3317
+ resourceRevision: integer2(promotedRaw.resourceRevision, `${path}.provenance.promotedFrom.resourceRevision`, errors),
3318
+ documentRevision: integer2(promotedRaw.documentRevision, `${path}.provenance.promotedFrom.documentRevision`, errors),
3319
+ promotedBy: text2(promotedRaw.promotedBy, `${path}.provenance.promotedFrom.promotedBy`, errors, 320) ?? "",
3320
+ promotedAt: integer2(promotedRaw.promotedAt, `${path}.provenance.promotedFrom.promotedAt`, errors, 0)
2801
3321
  } : void 0;
2802
3322
  const provenance = {
2803
- createdBy: text(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
2804
- createdAt: integer(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
2805
- updatedBy: text(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
2806
- updatedAt: integer(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
3323
+ createdBy: text2(provenanceRaw?.createdBy, `${path}.provenance.createdBy`, errors, 320) ?? "",
3324
+ createdAt: integer2(provenanceRaw?.createdAt, `${path}.provenance.createdAt`, errors, 0),
3325
+ updatedBy: text2(provenanceRaw?.updatedBy, `${path}.provenance.updatedBy`, errors, 320) ?? "",
3326
+ updatedAt: integer2(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
2807
3327
  ...promotedFrom ? { promotedFrom } : {}
2808
3328
  };
2809
- return { id: id4, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
3329
+ return { id: id6, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
2810
3330
  }
2811
3331
  function validateBuilderLibraryDocument(value, options = {}) {
2812
3332
  const errors = [];
2813
- const raw = record2(value);
2814
- if (!raw) return { ok: false, errors: ["document must be an object"] };
2815
- unknownKeys2(raw, ROOT_KEYS2, "document", errors);
2816
- if (raw.schemaVersion !== BUILDER_LIBRARY_SCHEMA_VERSION) errors.push("document.schemaVersion must be 1");
2817
- const revision = integer(raw.revision, "document.revision", errors);
2818
- const generatedAt = integer(raw.generatedAt, "document.generatedAt", errors, 0);
2819
- const sourceRaw = record2(raw.source);
3333
+ const raw2 = record2(value);
3334
+ if (!raw2) return { ok: false, errors: ["document must be an object"] };
3335
+ unknownKeys2(raw2, ROOT_KEYS2, "document", errors);
3336
+ if (raw2.schemaVersion !== BUILDER_LIBRARY_SCHEMA_VERSION) errors.push("document.schemaVersion must be 1");
3337
+ const revision = integer2(raw2.revision, "document.revision", errors);
3338
+ const generatedAt = integer2(raw2.generatedAt, "document.generatedAt", errors, 0);
3339
+ const sourceRaw = record2(raw2.source);
2820
3340
  if (!sourceRaw) errors.push("document.source must be an object");
2821
3341
  if (sourceRaw) unknownKeys2(sourceRaw, SOURCE_KEYS2, "document.source", errors);
2822
3342
  const sourceType = sourceRaw?.type;
2823
3343
  if (sourceType !== "chapter" && sourceType !== "built_not_found") errors.push("document.source.type must be chapter or built_not_found");
2824
- const sourceAppId = text(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
3344
+ const sourceAppId = text2(sourceRaw?.appId, "document.source.appId", errors, 63) ?? "";
2825
3345
  if (sourceAppId && !APP_ID.test(sourceAppId)) errors.push("document.source.appId must be a lowercase app slug");
2826
3346
  if (options.sourceType && sourceType !== options.sourceType) errors.push(`document.source.type must be ${options.sourceType}`);
2827
3347
  if (options.sourceAppId && sourceAppId !== options.sourceAppId) errors.push(`document.source.appId must be ${options.sourceAppId}`);
2828
- const targetRaw = raw.target === void 0 ? null : record2(raw.target);
2829
- if (raw.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
3348
+ const targetRaw = raw2.target === void 0 ? null : record2(raw2.target);
3349
+ if (raw2.target !== void 0 && !targetRaw) errors.push("document.target must be an object");
2830
3350
  if (targetRaw) unknownKeys2(targetRaw, TARGET_KEYS2, "document.target", errors);
2831
3351
  const target = targetRaw ? {
2832
- appId: text(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
2833
- environment: text(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
3352
+ appId: text2(targetRaw.appId, "document.target.appId", errors, 63) ?? "",
3353
+ environment: text2(targetRaw.environment, "document.target.environment", errors, 80) ?? ""
2834
3354
  } : void 0;
2835
3355
  if (options.targetAppId && target?.appId !== options.targetAppId) errors.push(`document.target.appId must be ${options.targetAppId}`);
2836
- if (!Array.isArray(raw.resources)) errors.push("document.resources must be an array");
2837
- const inputs = Array.isArray(raw.resources) ? raw.resources : [];
3356
+ if (!Array.isArray(raw2.resources)) errors.push("document.resources must be an array");
3357
+ const inputs = Array.isArray(raw2.resources) ? raw2.resources : [];
2838
3358
  if (inputs.length > MAX_RESOURCES) errors.push(`document.resources may contain at most ${MAX_RESOURCES} items`);
2839
- const resources = inputs.map((item, index) => resourceValue(item, `document.resources[${index}]`, errors)).filter(Boolean);
2840
- const ids = resources.map(({ id: id4 }) => id4);
3359
+ const resources = inputs.map((item, index2) => resourceValue(item, `document.resources[${index2}]`, errors)).filter(Boolean);
3360
+ const ids = resources.map(({ id: id6 }) => id6);
2841
3361
  if (new Set(ids).size !== ids.length) errors.push("document.resources ids must be unique");
2842
3362
  for (const resource of resources) {
2843
3363
  if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
@@ -2954,10 +3474,10 @@ function planDelivery(input) {
2954
3474
  const transport = !isProd && !redirect ? "log-only" : input.cloudflareReady ? "cloudflare" : "log-only";
2955
3475
  const to = redirect ? input.group.debugEmail : input.to;
2956
3476
  const subject = (redirect ? "[dev] " : "") + render(tpl.subject, vars);
2957
- const text4 = redirect ? `(dev redirect; original recipient: ${input.to})
3477
+ const text5 = redirect ? `(dev redirect; original recipient: ${input.to})
2958
3478
 
2959
3479
  ` + render(tpl.text, vars) : render(tpl.text, vars);
2960
- return { deliver: true, transport, to, subject, text: text4, redirected: redirect };
3480
+ return { deliver: true, transport, to, subject, text: text5, redirected: redirect };
2961
3481
  }
2962
3482
 
2963
3483
  // src/notify.ts
@@ -2992,9 +3512,9 @@ async function sendTemplated(deps, input) {
2992
3512
  error = e instanceof Error ? e.message : String(e);
2993
3513
  }
2994
3514
  }
2995
- const id4 = deps.newId();
3515
+ const id6 = deps.newId();
2996
3516
  const row = {
2997
- id: id4,
3517
+ id: id6,
2998
3518
  groupId: input.group.id,
2999
3519
  to: decision.to,
3000
3520
  template: input.template,
@@ -3008,7 +3528,7 @@ async function sendTemplated(deps, input) {
3008
3528
  ...messageId ? { messageId } : {},
3009
3529
  ...error ? { error } : {}
3010
3530
  };
3011
- await deps.db.transact([{ t: "update", ns: "emailLog", id: id4, attrs: row }], error ? void 0 : { mutationId: `email:${input.dedupeKey}` });
3531
+ await deps.db.transact([{ t: "update", ns: "emailLog", id: id6, attrs: row }], error ? void 0 : { mutationId: `email:${input.dedupeKey}` });
3012
3532
  return error ? { sent: false, reason: error } : { sent: true };
3013
3533
  }
3014
3534
  function emailGroupFrom(row) {
@@ -3194,29 +3714,29 @@ function validateAdmissionGrantDocument(input) {
3194
3714
  const grants = [];
3195
3715
  const seenIds = /* @__PURE__ */ new Set();
3196
3716
  const seenEmails = /* @__PURE__ */ new Set();
3197
- for (const [index, raw] of rawGrants.slice(0, MAX_GRANTS).entries()) {
3198
- const grant = raw && typeof raw === "object" ? raw : {};
3199
- const id4 = typeof grant.id === "string" ? grant.id : "";
3717
+ for (const [index2, raw2] of rawGrants.slice(0, MAX_GRANTS).entries()) {
3718
+ const grant = raw2 && typeof raw2 === "object" ? raw2 : {};
3719
+ const id6 = typeof grant.id === "string" ? grant.id : "";
3200
3720
  const email = typeof grant.email === "string" ? grant.email.trim() : "";
3201
3721
  const normalized = normalizeAdmissionEmail(email);
3202
3722
  const decision = grant.decision;
3203
3723
  const note = typeof grant.note === "string" ? grant.note.trim() : void 0;
3204
3724
  const expiresAt = grant.expiresAt === void 0 ? void 0 : Number(grant.expiresAt);
3205
- if (!ID3.test(id4)) errors.push(`document.grants[${index}].id must be an identifier`);
3206
- if (seenIds.has(id4)) errors.push(`document.grants repeats id "${id4}"`);
3207
- if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index}].email must be valid`);
3725
+ if (!ID3.test(id6)) errors.push(`document.grants[${index2}].id must be an identifier`);
3726
+ if (seenIds.has(id6)) errors.push(`document.grants repeats id "${id6}"`);
3727
+ if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index2}].email must be valid`);
3208
3728
  if (seenEmails.has(normalized)) errors.push(`document.grants repeats email "${normalized}"`);
3209
3729
  if (decision !== "waive_interview" && decision !== "require_interview") {
3210
- errors.push(`document.grants[${index}].decision is invalid`);
3730
+ errors.push(`document.grants[${index2}].decision is invalid`);
3211
3731
  }
3212
- if (note && note.length > MAX_NOTE) errors.push(`document.grants[${index}].note is too long`);
3732
+ if (note && note.length > MAX_NOTE) errors.push(`document.grants[${index2}].note is too long`);
3213
3733
  if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= 0)) {
3214
- errors.push(`document.grants[${index}].expiresAt must be epoch milliseconds`);
3734
+ errors.push(`document.grants[${index2}].expiresAt must be epoch milliseconds`);
3215
3735
  }
3216
- seenIds.add(id4);
3736
+ seenIds.add(id6);
3217
3737
  seenEmails.add(normalized);
3218
3738
  grants.push({
3219
- id: id4,
3739
+ id: id6,
3220
3740
  email,
3221
3741
  decision,
3222
3742
  ...note ? { note } : {},
@@ -3287,8 +3807,8 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3287
3807
  const incomingIds = new Set(document.grants.map((grant) => `admission:parent:${document.source.appId}:${grant.id}`));
3288
3808
  const ops = [];
3289
3809
  for (const row of current) {
3290
- const id4 = String(row.id ?? "");
3291
- if (id4 && !incomingIds.has(id4)) ops.push({ t: "update", ns: "admissionGrants", id: id4, attrs: {
3810
+ const id6 = String(row.id ?? "");
3811
+ if (id6 && !incomingIds.has(id6)) ops.push({ t: "update", ns: "admissionGrants", id: id6, attrs: {
3292
3812
  active: false,
3293
3813
  revokedAt: receivedAt,
3294
3814
  revokedBy: document.source.actor,
@@ -3297,10 +3817,10 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3297
3817
  } });
3298
3818
  }
3299
3819
  for (const grant of document.grants) {
3300
- const id4 = `admission:parent:${document.source.appId}:${grant.id}`;
3301
- const existing = current.find((row) => row.id === id4);
3302
- ops.push({ t: "update", ns: "admissionGrants", id: id4, attrs: {
3303
- id: id4,
3820
+ const id6 = `admission:parent:${document.source.appId}:${grant.id}`;
3821
+ const existing = current.find((row) => row.id === id6);
3822
+ ops.push({ t: "update", ns: "admissionGrants", id: id6, attrs: {
3823
+ id: id6,
3304
3824
  sourceGrantId: grant.id,
3305
3825
  sourceScope: "parent",
3306
3826
  sourceId: document.source.appId,
@@ -3321,7 +3841,7 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3321
3841
  ...!grant.note && existing?.note !== void 0 ? ["note"] : [],
3322
3842
  ...grant.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
3323
3843
  ];
3324
- if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id4, attrs: retract });
3844
+ if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id6, attrs: retract });
3325
3845
  }
3326
3846
  if (previous?.active) ops.push({ t: "update", ns: "admissionGrantRevisions", id: previous.id, attrs: { active: false } });
3327
3847
  const revisionId = `admission-revision:${document.source.appId}:${document.target.environment}:${document.revision}`;
@@ -3363,7 +3883,7 @@ var ID4 = /^[a-z][a-z0-9-]{1,79}$/;
3363
3883
  var PRICE = /^price_[A-Za-z0-9]{12,}$/;
3364
3884
  var CURRENCY = /^[a-z]{3}$/;
3365
3885
  var record3 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
3366
- var text2 = (value, path, errors, max) => {
3886
+ var text3 = (value, path, errors, max) => {
3367
3887
  if (typeof value !== "string" || !value.trim()) {
3368
3888
  errors.push(`${path} is required`);
3369
3889
  return "";
@@ -3382,32 +3902,32 @@ function validateGiftOfferCatalog(input, tierIds) {
3382
3902
  if (rawOffers.length > MAX_GIFT_OFFERS) errors.push(`document.giftOffers.offers supports at most ${MAX_GIFT_OFFERS} entries`);
3383
3903
  const ids = /* @__PURE__ */ new Set();
3384
3904
  const offers = [];
3385
- for (const [index, raw] of rawOffers.slice(0, MAX_GIFT_OFFERS).entries()) {
3386
- const path = `document.giftOffers.offers[${index}]`;
3387
- const item = record3(raw);
3905
+ for (const [index2, raw2] of rawOffers.slice(0, MAX_GIFT_OFFERS).entries()) {
3906
+ const path = `document.giftOffers.offers[${index2}]`;
3907
+ const item = record3(raw2);
3388
3908
  if (!item) {
3389
3909
  errors.push(`${path} must be an object`);
3390
3910
  continue;
3391
3911
  }
3392
3912
  const allowed2 = /* @__PURE__ */ new Set(["id", "target", "billingMode", "stripePriceId", "priceCents", "currency", "redemptionLifetimeHours", "activeFrom", "activeUntil", "title", "description", "ctaLabel"]);
3393
3913
  for (const key of Object.keys(item)) if (!allowed2.has(key)) errors.push(`${path}.${key} is not supported`);
3394
- const id4 = text2(item.id, `${path}.id`, errors, 80);
3395
- if (id4 && !ID4.test(id4)) errors.push(`${path}.id must be a lowercase kebab-case id`);
3396
- if (ids.has(id4)) errors.push(`${path}.id duplicates "${id4}"`);
3397
- ids.add(id4);
3914
+ const id6 = text3(item.id, `${path}.id`, errors, 80);
3915
+ if (id6 && !ID4.test(id6)) errors.push(`${path}.id must be a lowercase kebab-case id`);
3916
+ if (ids.has(id6)) errors.push(`${path}.id duplicates "${id6}"`);
3917
+ ids.add(id6);
3398
3918
  const targetRaw = record3(item.target);
3399
3919
  let target = { type: "entitlement", entitlementKey: "invalid" };
3400
3920
  if (targetRaw?.type === "tier") {
3401
- const tierId = text2(targetRaw.tierId, `${path}.target.tierId`, errors, 80);
3921
+ const tierId = text3(targetRaw.tierId, `${path}.target.tierId`, errors, 80);
3402
3922
  if (!tierIds.has(tierId)) errors.push(`${path}.target.tierId must reference a declared tier`);
3403
3923
  target = { type: "tier", tierId };
3404
3924
  } else if (targetRaw?.type === "entitlement") {
3405
- const entitlementKey = text2(targetRaw.entitlementKey, `${path}.target.entitlementKey`, errors, 80);
3925
+ const entitlementKey = text3(targetRaw.entitlementKey, `${path}.target.entitlementKey`, errors, 80);
3406
3926
  if (entitlementKey && !ID4.test(entitlementKey)) errors.push(`${path}.target.entitlementKey must be a lowercase kebab-case id`);
3407
3927
  target = { type: "entitlement", entitlementKey };
3408
3928
  } else errors.push(`${path}.target.type must be tier or entitlement`);
3409
3929
  if (item.billingMode !== "one_time" && item.billingMode !== "subscription") errors.push(`${path}.billingMode must be one_time or subscription`);
3410
- const stripePriceId = text2(item.stripePriceId, `${path}.stripePriceId`, errors, 128);
3930
+ const stripePriceId = text3(item.stripePriceId, `${path}.stripePriceId`, errors, 128);
3411
3931
  if (stripePriceId && !PRICE.test(stripePriceId)) errors.push(`${path}.stripePriceId must be a real Stripe Price id`);
3412
3932
  if (!Number.isSafeInteger(item.priceCents) || Number(item.priceCents) < 1 || Number(item.priceCents) > 1e8) errors.push(`${path}.priceCents must be an integer from 1 to 100,000,000`);
3413
3933
  const currency = typeof item.currency === "string" ? item.currency.trim().toLowerCase() : "";
@@ -3420,7 +3940,7 @@ function validateGiftOfferCatalog(input, tierIds) {
3420
3940
  if (activeUntil !== void 0 && (!Number.isSafeInteger(activeUntil) || activeUntil < 1)) errors.push(`${path}.activeUntil must be epoch milliseconds`);
3421
3941
  if (activeFrom !== void 0 && activeUntil !== void 0 && activeUntil <= activeFrom) errors.push(`${path}.activeUntil must follow activeFrom`);
3422
3942
  offers.push({
3423
- id: id4,
3943
+ id: id6,
3424
3944
  target,
3425
3945
  billingMode: item.billingMode,
3426
3946
  stripePriceId,
@@ -3429,9 +3949,9 @@ function validateGiftOfferCatalog(input, tierIds) {
3429
3949
  redemptionLifetimeHours: lifetime,
3430
3950
  ...activeFrom !== void 0 ? { activeFrom } : {},
3431
3951
  ...activeUntil !== void 0 ? { activeUntil } : {},
3432
- title: text2(item.title, `${path}.title`, errors, 160),
3433
- description: text2(item.description, `${path}.description`, errors, 2e3),
3434
- ctaLabel: text2(item.ctaLabel, `${path}.ctaLabel`, errors, 120)
3952
+ title: text3(item.title, `${path}.title`, errors, 160),
3953
+ description: text3(item.description, `${path}.description`, errors, 2e3),
3954
+ ctaLabel: text3(item.ctaLabel, `${path}.ctaLabel`, errors, 120)
3435
3955
  });
3436
3956
  }
3437
3957
  return errors.length ? { ok: false, errors } : { ok: true, value: { schemaVersion: 1, offers } };
@@ -3471,8 +3991,8 @@ function networkSourceTag(type, hubRecordId, sourceId) {
3471
3991
  const typeKey = type.toLowerCase();
3472
3992
  const readable = /^[a-z0-9_-]+$/.test(hubRecordId);
3473
3993
  const prefix = sourceId ? `network:${sourceId.toLowerCase()}:${typeKey}` : `network:${typeKey}`;
3474
- const raw = `${prefix}:${hubRecordId}`;
3475
- if (readable && raw.length <= 64) return raw;
3994
+ const raw2 = `${prefix}:${hubRecordId}`;
3995
+ if (readable && raw2.length <= 64) return raw2;
3476
3996
  return `network:${typeKey.slice(0, 16)}:${shortHash(`${sourceId ?? ""}\0${type}\0${hubRecordId}`)}`;
3477
3997
  }
3478
3998
  function normalizeSharedRecord(record4, fallbackSourceId = "legacy-source") {
@@ -3742,14 +4262,14 @@ function periodSeries(points, now, count, periodMs) {
3742
4262
  for (const point of points) {
3743
4263
  if (!Number.isFinite(point.t) || !Number.isFinite(point.v)) continue;
3744
4264
  if (point.t >= start && point.t <= now) {
3745
- const index = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
3746
- current[index] = (current[index] ?? 0) + point.v;
4265
+ const index2 = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
4266
+ current[index2] = (current[index2] ?? 0) + point.v;
3747
4267
  } else if (point.t >= previousStart && point.t < start) {
3748
- const index = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
3749
- previous[index] = (previous[index] ?? 0) + point.v;
4268
+ const index2 = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
4269
+ previous[index2] = (previous[index2] ?? 0) + point.v;
3750
4270
  }
3751
4271
  }
3752
- const labels = current.map((_, index) => new Date(start + index * periodMs).toISOString().slice(5, 10));
4272
+ const labels = current.map((_, index2) => new Date(start + index2 * periodMs).toISOString().slice(5, 10));
3753
4273
  return { labels, current, previous };
3754
4274
  }
3755
4275
  function dashboardMetricData(points, now) {
@@ -3833,9 +4353,9 @@ async function updateClerkUserMetadataByEmail(secretKey, email, input, fetchImpl
3833
4353
  const found = await fetchImpl(`https://api.clerk.com/v1/users?email_address=${encodeURIComponent(email)}&limit=1`, { headers: auth });
3834
4354
  if (!found.ok) return false;
3835
4355
  const users = await found.json().catch(() => null);
3836
- const id4 = Array.isArray(users) && typeof users[0]?.id === "string" ? users[0].id : void 0;
3837
- if (!id4) return false;
3838
- return updateClerkUserMetadata(secretKey, id4, input, fetchImpl);
4356
+ const id6 = Array.isArray(users) && typeof users[0]?.id === "string" ? users[0].id : void 0;
4357
+ if (!id6) return false;
4358
+ return updateClerkUserMetadata(secretKey, id6, input, fetchImpl);
3839
4359
  }
3840
4360
  async function createClerkUser(secretKey, input, fetchImpl = fetch) {
3841
4361
  const { path, body } = clerkUserRequest(input);
@@ -3898,8 +4418,8 @@ async function clerkGetUserByEmail(secretKey, email, fetchImpl = fetch) {
3898
4418
  const user = Array.isArray(data) ? data[0] : void 0;
3899
4419
  return user ? toRecord(user) : null;
3900
4420
  }
3901
- async function clerkGetUser(secretKey, id4, fetchImpl = fetch) {
3902
- const data = await clerkGet(`/v1/users/${encodeURIComponent(id4)}`, secretKey, fetchImpl).catch(() => null);
4421
+ async function clerkGetUser(secretKey, id6, fetchImpl = fetch) {
4422
+ const data = await clerkGet(`/v1/users/${encodeURIComponent(id6)}`, secretKey, fetchImpl).catch(() => null);
3903
4423
  return data ? toRecord(data) : null;
3904
4424
  }
3905
4425
  async function clerkListUsers(secretKey, fetchImpl = fetch) {
@@ -3915,8 +4435,8 @@ async function clerkListUsers(secretKey, fetchImpl = fetch) {
3915
4435
  }
3916
4436
  return out;
3917
4437
  }
3918
- async function clerkSetRole(secretKey, id4, role, fetchImpl = fetch) {
3919
- const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id4)}/metadata`, {
4438
+ async function clerkSetRole(secretKey, id6, role, fetchImpl = fetch) {
4439
+ const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id6)}/metadata`, {
3920
4440
  method: "PATCH",
3921
4441
  headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
3922
4442
  body: JSON.stringify({ public_metadata: { role } })
@@ -4142,8 +4662,8 @@ function chapterBrandFromTokens(compiled, base = {}) {
4142
4662
  function isReconcilable(meeting, now) {
4143
4663
  return meeting.status === "scheduled" && Boolean(meeting.googleEventId) && (meeting.startAt ?? 0) > now - 36e5;
4144
4664
  }
4145
- function reconcileMeetings(meetings2, events, now) {
4146
- const byEvent = new Map(events.map((e) => [e.eventId, e]));
4665
+ function reconcileMeetings(meetings2, events2, now) {
4666
+ const byEvent = new Map(events2.map((e) => [e.eventId, e]));
4147
4667
  const decisions = [];
4148
4668
  for (const m of meetings2) {
4149
4669
  if (!isReconcilable(m, now) || !m.googleEventId) continue;
@@ -4353,10 +4873,10 @@ function boundedString(value, path, errors, max, required = false) {
4353
4873
  errors.push(`${path} must be a string`);
4354
4874
  return void 0;
4355
4875
  }
4356
- const text4 = value.trim();
4357
- if (required && !text4) errors.push(`${path} is required`);
4358
- if (text4.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
4359
- return text4;
4876
+ const text5 = value.trim();
4877
+ if (required && !text5) errors.push(`${path} is required`);
4878
+ if (text5.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
4879
+ return text5;
4360
4880
  }
4361
4881
  function validateMembershipPageDocument(input, options = {}) {
4362
4882
  const errors = [];
@@ -4374,9 +4894,9 @@ function validateMembershipPageDocument(input, options = {}) {
4374
4894
  const authority = new Map((options.tiers ?? []).map((tier) => [tier.id, tier]));
4375
4895
  const seen = /* @__PURE__ */ new Set();
4376
4896
  const tiers2 = [];
4377
- for (const [index, raw] of rawTiers.slice(0, MAX_TIERS).entries()) {
4378
- const path = `document.tiers[${index}]`;
4379
- const item = objectRecord(raw);
4897
+ for (const [index2, raw2] of rawTiers.slice(0, MAX_TIERS).entries()) {
4898
+ const path = `document.tiers[${index2}]`;
4899
+ const item = objectRecord(raw2);
4380
4900
  if (!item) {
4381
4901
  errors.push(`${path} must be an object`);
4382
4902
  continue;
@@ -4405,8 +4925,8 @@ function validateMembershipPageDocument(input, options = {}) {
4405
4925
  else {
4406
4926
  if (item.benefits.length > MAX_BENEFITS) errors.push(`${path}.benefits supports at most ${MAX_BENEFITS} entries`);
4407
4927
  benefits = item.benefits.slice(0, MAX_BENEFITS).flatMap((benefit, benefitIndex) => {
4408
- const text4 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
4409
- return text4 ? [text4] : [];
4928
+ const text5 = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
4929
+ return text5 ? [text5] : [];
4410
4930
  });
4411
4931
  }
4412
4932
  }
@@ -4482,7 +5002,7 @@ function membershipPageDocument(input, options = {}) {
4482
5002
  }
4483
5003
 
4484
5004
  // src/signup-control-validate.ts
4485
- var SLUG7 = /^[a-z0-9][a-z0-9-]{1,62}$/;
5005
+ var SLUG9 = /^[a-z0-9][a-z0-9-]{1,62}$/;
4486
5006
  var ENVIRONMENT = /^[a-z][a-z0-9_-]{0,62}$/;
4487
5007
  var TIER = /^[a-z][a-z0-9-]{0,79}$/;
4488
5008
  var PRICE2 = /^price_[A-Za-z0-9]{12,}$/;
@@ -4505,7 +5025,7 @@ var recordOf = (value) => value !== null && typeof value === "object" && !Array.
4505
5025
  function unknownKeys3(value, allowed2, path, errors) {
4506
5026
  for (const key of Object.keys(value)) if (!allowed2.has(key)) errors.push(`${path}.${key} is not supported`);
4507
5027
  }
4508
- function text3(value, path, errors, max = 65536) {
5028
+ function text4(value, path, errors, max = 65536) {
4509
5029
  if (typeof value !== "string" || !value.trim()) {
4510
5030
  errors.push(`${path} is required`);
4511
5031
  return "";
@@ -4520,19 +5040,19 @@ function tiersOf(value, errors) {
4520
5040
  }
4521
5041
  if (value.length > 20) errors.push("document.tiers supports at most 20 entries");
4522
5042
  const seen = /* @__PURE__ */ new Set();
4523
- return value.slice(0, 20).flatMap((raw, index) => {
4524
- const path = `document.tiers[${index}]`;
4525
- const item = recordOf(raw);
5043
+ return value.slice(0, 20).flatMap((raw2, index2) => {
5044
+ const path = `document.tiers[${index2}]`;
5045
+ const item = recordOf(raw2);
4526
5046
  if (!item) {
4527
5047
  errors.push(`${path} must be an object`);
4528
5048
  return [];
4529
5049
  }
4530
5050
  unknownKeys3(item, TIER_KEYS2, path, errors);
4531
- const id4 = text3(item.id, `${path}.id`, errors, 80);
4532
- if (id4 && !TIER.test(id4)) errors.push(`${path}.id must be a lowercase kebab-case id`);
4533
- if (seen.has(id4)) errors.push(`${path}.id duplicates "${id4}"`);
4534
- seen.add(id4);
4535
- const name = text3(item.name, `${path}.name`, errors, 160);
5051
+ const id6 = text4(item.id, `${path}.id`, errors, 80);
5052
+ if (id6 && !TIER.test(id6)) errors.push(`${path}.id must be a lowercase kebab-case id`);
5053
+ if (seen.has(id6)) errors.push(`${path}.id duplicates "${id6}"`);
5054
+ seen.add(id6);
5055
+ const name = text4(item.name, `${path}.name`, errors, 160);
4536
5056
  if (typeof item.available !== "boolean") errors.push(`${path}.available must be boolean`);
4537
5057
  if (!Number.isInteger(item.sortOrder) || Number(item.sortOrder) < 0) errors.push(`${path}.sortOrder must be a non-negative integer`);
4538
5058
  if (!Number.isInteger(item.priceCents) || Number(item.priceCents) < 0 || Number(item.priceCents) > 1e8) {
@@ -4544,9 +5064,9 @@ function tiersOf(value, errors) {
4544
5064
  if (item.available === true && Number(item.priceCents) > 0 && !item.stripePriceId) {
4545
5065
  errors.push(`${path}.stripePriceId is required for an available paid tier`);
4546
5066
  }
4547
- const blurb = item.blurb === void 0 ? void 0 : text3(item.blurb, `${path}.blurb`, errors, 4e3);
5067
+ const blurb = item.blurb === void 0 ? void 0 : text4(item.blurb, `${path}.blurb`, errors, 4e3);
4548
5068
  return [{
4549
- id: id4,
5069
+ id: id6,
4550
5070
  name,
4551
5071
  available: item.available === true,
4552
5072
  sortOrder: Number(item.sortOrder) || 0,
@@ -4600,7 +5120,7 @@ function policyOf(value, errors) {
4600
5120
  const result = {};
4601
5121
  for (const key of allowed2) {
4602
5122
  if (input[key] === void 0 && key !== "disclaimerText" && key !== "refundPolicyText") continue;
4603
- result[key] = text3(input[key], `document.policy.${key}`, errors);
5123
+ result[key] = text4(input[key], `document.policy.${key}`, errors);
4604
5124
  }
4605
5125
  return result;
4606
5126
  }
@@ -4638,18 +5158,18 @@ function validateSignupControlDocument(input) {
4638
5158
  if (!Number.isSafeInteger(root.generatedAt) || Number(root.generatedAt) < 1) errors.push("document.generatedAt must be epoch milliseconds");
4639
5159
  const source = recordOf(root.source);
4640
5160
  const target = recordOf(root.target);
4641
- const appId = text3(source?.appId, "document.source.appId", errors, 63);
4642
- const chapterId = text3(target?.chapterId, "document.target.chapterId", errors, 63);
4643
- const environment = text3(target?.environment, "document.target.environment", errors, 63);
4644
- if (appId && !SLUG7.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
4645
- if (chapterId && !SLUG7.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
5161
+ const appId = text4(source?.appId, "document.source.appId", errors, 63);
5162
+ const chapterId = text4(target?.chapterId, "document.target.chapterId", errors, 63);
5163
+ const environment = text4(target?.environment, "document.target.environment", errors, 63);
5164
+ if (appId && !SLUG9.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
5165
+ if (chapterId && !SLUG9.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
4646
5166
  if (environment && !ENVIRONMENT.test(environment)) errors.push("document.target.environment is invalid");
4647
5167
  if (target?.stripeMode !== "test" && target?.stripeMode !== "live") errors.push("document.target.stripeMode must be test or live");
4648
5168
  const tiers2 = tiersOf(root.tiers, errors);
4649
5169
  const available = tiers2.filter((tier) => tier.available).sort((a, b) => a.sortOrder - b.sortOrder);
4650
5170
  const page = validateMembershipPageDocument(root.membershipPage, { tiers: available });
4651
5171
  if (!page.ok) errors.push(...page.errors);
4652
- else if (page.value.tiers.some((tier, index) => tier.tierId !== available[index]?.id)) {
5172
+ else if (page.value.tiers.some((tier, index2) => tier.tierId !== available[index2]?.id)) {
4653
5173
  errors.push("document.membershipPage.tiers must follow the available tier display order");
4654
5174
  }
4655
5175
  const application = applicationOf(root.application, errors);
@@ -4696,14 +5216,14 @@ function validatedTokens(input) {
4696
5216
  }
4697
5217
  const tokens = {};
4698
5218
  let serializedLength = 0;
4699
- for (const [name, raw] of entries.sort(([left], [right]) => left.localeCompare(right))) {
5219
+ for (const [name, raw2] of entries.sort(([left], [right]) => left.localeCompare(right))) {
4700
5220
  if (!TOKEN_NAME.test(name)) {
4701
5221
  throw new TypeError(`signup-control theme token "${name}" must be an allowlisted --ui-* property`);
4702
5222
  }
4703
- if (typeof raw !== "string" && typeof raw !== "number" || typeof raw === "number" && !Number.isFinite(raw)) {
5223
+ if (typeof raw2 !== "string" && typeof raw2 !== "number" || typeof raw2 === "number" && !Number.isFinite(raw2)) {
4704
5224
  throw new TypeError(`signup-control theme token "${name}" must be a finite string or number`);
4705
5225
  }
4706
- const value = String(raw).trim();
5226
+ const value = String(raw2).trim();
4707
5227
  if (!value || value.length > MAX_VALUE_LENGTH || UNSAFE_VALUE.test(value)) {
4708
5228
  throw new TypeError(`signup-control theme token "${name}" has an unsafe or oversized value`);
4709
5229
  }
@@ -4810,16 +5330,16 @@ function markdownSection(rawHeading) {
4810
5330
  }
4811
5331
  function markdownBenefits(lines, path, errors) {
4812
5332
  const benefits = [];
4813
- for (const [index, line] of lines.entries()) {
5333
+ for (const [index2, line] of lines.entries()) {
4814
5334
  if (!line.trim()) continue;
4815
5335
  const match = line.match(MARKDOWN_BENEFIT);
4816
5336
  if (!match) {
4817
- errors.push(`${path} line ${index + 1} must be a Markdown bullet`);
5337
+ errors.push(`${path} line ${index2 + 1} must be a Markdown bullet`);
4818
5338
  continue;
4819
5339
  }
4820
- const text4 = (match[1] ?? "").trim();
4821
- if (text4.length > 2e3) errors.push(`${path} line ${index + 1} must be at most 2,000 characters`);
4822
- if (text4) benefits.push(text4);
5340
+ const text5 = (match[1] ?? "").trim();
5341
+ if (text5.length > 2e3) errors.push(`${path} line ${index2 + 1} must be at most 2,000 characters`);
5342
+ if (text5) benefits.push(text5);
4823
5343
  }
4824
5344
  if (benefits.length > MAX_BENEFITS2) errors.push(`${path} supports at most ${MAX_BENEFITS2} entries`);
4825
5345
  return benefits.slice(0, MAX_BENEFITS2);
@@ -4834,7 +5354,7 @@ function applyMembershipTierMarkdown(presentation, source) {
4834
5354
  const sections = /* @__PURE__ */ new Map();
4835
5355
  const prelude = [];
4836
5356
  let current = null;
4837
- for (const [index, line] of source.replace(/\r\n?/g, "\n").split("\n").entries()) {
5357
+ for (const [index2, line] of source.replace(/\r\n?/g, "\n").split("\n").entries()) {
4838
5358
  const heading = line.match(MARKDOWN_SECTION);
4839
5359
  if (heading) {
4840
5360
  const headingText = heading[1] ?? "";
@@ -4854,7 +5374,7 @@ function applyMembershipTierMarkdown(presentation, source) {
4854
5374
  continue;
4855
5375
  }
4856
5376
  if (MARKDOWN_HEADING.test(line)) {
4857
- errors.push(`membership Markdown line ${index + 1} uses an unsupported heading`);
5377
+ errors.push(`membership Markdown line ${index2 + 1} uses an unsupported heading`);
4858
5378
  continue;
4859
5379
  }
4860
5380
  if (current) sections.get(current)?.lines.push(line);
@@ -4919,7 +5439,7 @@ var exclusions = (chapterId, paidTierIds) => [
4919
5439
  id: chapterId,
4920
5440
  reason: "provider_identity",
4921
5441
  paths: [
4922
- ...paidTierIds.map((id4) => `tiers[${id4}].stripePriceId`),
5442
+ ...paidTierIds.map((id6) => `tiers[${id6}].stripePriceId`),
4923
5443
  "groups.stripePublishableKey",
4924
5444
  "applications.stripeCustomerId",
4925
5445
  "applications.stripeSubscriptionId",
@@ -4961,9 +5481,9 @@ async function exportChapterPromotion(input) {
4961
5481
  sourceIds: [`signup-control:${document.revision}`, document.source.appId, document.target.chapterId].sort(),
4962
5482
  receiptDigests: [input.digest]
4963
5483
  };
4964
- const artifact = (kind, id4, content) => ({
5484
+ const artifact = (kind, id6, content) => ({
4965
5485
  kind,
4966
- id: id4,
5486
+ id: id6,
4967
5487
  sourceRevision: revision,
4968
5488
  provenance: promotionProvenance,
4969
5489
  content: (0, import_promotion.canonicalPromotionValue)(content)
@@ -5002,7 +5522,7 @@ async function exportChapterPromotion(input) {
5002
5522
  }
5003
5523
 
5004
5524
  // src/member-system-authority.ts
5005
- var rows2 = (group) => group.fields.map((id4) => ({ ...group, id: id4 }));
5525
+ var rows2 = (group) => group.fields.map((id6) => ({ ...group, id: id6 }));
5006
5526
  var MEMBER_SYSTEM_AUTHORITY_MATRIX = [
5007
5527
  ...rows2({
5008
5528
  scope: "network",
@@ -5387,10 +5907,10 @@ function classifyMemberStripeEvent(identity, target, hasProvenLegacyLocalLink) {
5387
5907
  if (supplied.length === keys.length) return "apply";
5388
5908
  return hasProvenLegacyLocalLink ? "apply_legacy" : "quarantine_unmatched";
5389
5909
  }
5390
- function planMemberSystemEventReplay(events) {
5910
+ function planMemberSystemEventReplay(events2) {
5391
5911
  const seen = /* @__PURE__ */ new Map();
5392
5912
  const plan = { accepted: [], replayed: [], collisions: [] };
5393
- for (const event of events) {
5913
+ for (const event of events2) {
5394
5914
  const digest = seen.get(event.id);
5395
5915
  if (digest === void 0) {
5396
5916
  seen.set(event.id, event.digest);