@odla-ai/chapter 0.43.1 → 0.44.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
@@ -168,6 +168,7 @@ __export(index_exports, {
168
168
  resolveJoinContinuation: () => resolveJoinContinuation,
169
169
  resolveLeaderFormation: () => resolveLeaderFormation,
170
170
  resolveMemberContent: () => resolveMemberContent,
171
+ resolveNetworkPersonRegistry: () => resolveNetworkPersonRegistry,
171
172
  resolvePipeline: () => resolvePipeline,
172
173
  resolveScheduling: () => resolveScheduling,
173
174
  resolveSignupControl: () => resolveSignupControl,
@@ -319,8 +320,8 @@ async function submitApplication(db, chapter, fields, opts) {
319
320
  if (app.requireDisclaimerAck && !acked) {
320
321
  return { ok: false, error: "disclaimerAck is required" };
321
322
  }
322
- const id4 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
323
- const row = { id: id4, status: chapter.pipeline.initial, createdAt: opts.now };
323
+ const id5 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
324
+ const row = { id: id5, status: chapter.pipeline.initial, createdAt: opts.now };
324
325
  for (const f of [...app.required, ...app.optional]) {
325
326
  if (fieldStates[f]?.visible === false) continue;
326
327
  if (typeof fields[f] === "string") row[f] = fields[f].trim();
@@ -344,19 +345,19 @@ async function submitApplication(db, chapter, fields, opts) {
344
345
  }
345
346
  if (acked) row.disclaimerAckAt = opts.now;
346
347
  const ops = [
347
- { t: "update", ns: "applications", id: id4, attrs: row }
348
+ { t: "update", ns: "applications", id: id5, attrs: row }
348
349
  ];
349
350
  if (opts.admissionGrant?.decision === "waive_interview") {
350
351
  ops.push({ t: "update", ns: "admissionGrants", id: opts.admissionGrant.id, attrs: {
351
352
  consumedAt: opts.now,
352
- consumedApplicationId: id4
353
+ consumedApplicationId: id5
353
354
  } });
354
355
  }
355
356
  const { duplicate } = await db.transact(
356
357
  ops,
357
358
  opts.submissionId ? { mutationId: `join:${opts.submissionId}` } : void 0
358
359
  );
359
- return { ok: true, id: id4, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
360
+ return { ok: true, id: id5, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
360
361
  }
361
362
  function joinConfig(group, paymentsReady2, tiers2 = [], conditions = {}) {
362
363
  return {
@@ -670,6 +671,88 @@ var stripeEventReceiptsEntity = {
670
671
  }
671
672
  };
672
673
 
674
+ // src/schema-network-person.ts
675
+ var string = (flags = {}) => ({
676
+ type: "string",
677
+ unique: flags.unique ?? false,
678
+ indexed: flags.indexed ?? false,
679
+ optional: flags.optional ?? false
680
+ });
681
+ var number = (flags = {}) => ({
682
+ type: "number",
683
+ unique: false,
684
+ indexed: flags.indexed ?? false,
685
+ optional: flags.optional ?? false
686
+ });
687
+ var json = (flags = {}) => ({
688
+ type: "json",
689
+ unique: false,
690
+ indexed: false,
691
+ optional: flags.optional ?? false
692
+ });
693
+ var id3 = () => string({ unique: true, indexed: true });
694
+ var networkPersonsEntity = { attrs: {
695
+ id: id3(),
696
+ displayName: string({ indexed: true }),
697
+ state: string({ indexed: true }),
698
+ mergedIntoId: string({ indexed: true, optional: true }),
699
+ contactPolicy: json(),
700
+ revision: number({ indexed: true }),
701
+ createdAt: number({ indexed: true }),
702
+ updatedAt: number({ indexed: true })
703
+ } };
704
+ var networkIdentityClaimsEntity = { attrs: {
705
+ id: id3(),
706
+ personId: string({ indexed: true }),
707
+ kind: string({ indexed: true }),
708
+ value: string(),
709
+ hint: string({ optional: true }),
710
+ verifiedAt: number({ indexed: true }),
711
+ createdAt: number({ indexed: true })
712
+ } };
713
+ var networkPersonAliasesEntity = { attrs: {
714
+ id: id3(),
715
+ sourceSystem: string({ indexed: true }),
716
+ sourceRecordId: string({ indexed: true }),
717
+ sourceKey: string({ unique: true, indexed: true }),
718
+ displayName: string({ indexed: true }),
719
+ provenance: string(),
720
+ consent: json(),
721
+ status: string({ indexed: true }),
722
+ personId: string({ indexed: true, optional: true }),
723
+ candidatePersonIds: json({ optional: true }),
724
+ identityClaims: json(),
725
+ chapterLinks: json(),
726
+ revision: number({ indexed: true }),
727
+ createdAt: number({ indexed: true }),
728
+ updatedAt: number({ indexed: true })
729
+ } };
730
+ var networkChapterPersonLinksEntity = { attrs: {
731
+ id: id3(),
732
+ personId: string({ indexed: true }),
733
+ chapterId: string({ indexed: true }),
734
+ chapterPersonId: string({ indexed: true }),
735
+ state: string({ indexed: true }),
736
+ createdAt: number({ indexed: true })
737
+ } };
738
+ var networkPersonEventsEntity = { attrs: {
739
+ id: id3(),
740
+ kind: string({ indexed: true }),
741
+ personId: string({ indexed: true, optional: true }),
742
+ aliasId: string({ indexed: true, optional: true }),
743
+ actorId: string({ indexed: true }),
744
+ mutationId: string({ unique: true, indexed: true }),
745
+ detail: json(),
746
+ createdAt: number({ indexed: true })
747
+ } };
748
+ var networkPersonEntities = {
749
+ networkPersons: networkPersonsEntity,
750
+ networkIdentityClaims: networkIdentityClaimsEntity,
751
+ networkPersonAliases: networkPersonAliasesEntity,
752
+ networkChapterPersonLinks: networkChapterPersonLinksEntity,
753
+ networkPersonEvents: networkPersonEventsEntity
754
+ };
755
+
673
756
  // src/schema.ts
674
757
  function attr5(type, flags = {}) {
675
758
  return {
@@ -679,10 +762,10 @@ function attr5(type, flags = {}) {
679
762
  optional: flags.optional ?? false
680
763
  };
681
764
  }
682
- var id3 = () => attr5("string", { unique: true, indexed: true });
765
+ var id4 = () => attr5("string", { unique: true, indexed: true });
683
766
  var admins = {
684
767
  attrs: {
685
- id: id3(),
768
+ id: id4(),
686
769
  email: attr5("string", { unique: true, indexed: true }),
687
770
  name: attr5("string", { optional: true }),
688
771
  note: attr5("string", { optional: true })
@@ -690,7 +773,7 @@ var admins = {
690
773
  };
691
774
  var superAdmins = {
692
775
  attrs: {
693
- id: id3(),
776
+ id: id4(),
694
777
  email: attr5("string", { unique: true, indexed: true }),
695
778
  note: attr5("string", { optional: true }),
696
779
  createdAt: attr5("number", { indexed: true })
@@ -698,7 +781,7 @@ var superAdmins = {
698
781
  };
699
782
  var networkNotes = {
700
783
  attrs: {
701
- id: id3(),
784
+ id: id4(),
702
785
  recordKey: attr5("string", { indexed: true }),
703
786
  targetId: attr5("string", { indexed: true }),
704
787
  recordType: attr5("string", { indexed: true }),
@@ -711,7 +794,7 @@ var networkNotes = {
711
794
  };
712
795
  var signupControls = {
713
796
  attrs: {
714
- id: id3(),
797
+ id: id4(),
715
798
  sourceId: attr5("string", { indexed: true }),
716
799
  targetId: attr5("string", { indexed: true }),
717
800
  environment: attr5("string", { indexed: true }),
@@ -724,7 +807,7 @@ var signupControls = {
724
807
  };
725
808
  var memberContentControls = {
726
809
  attrs: {
727
- id: id3(),
810
+ id: id4(),
728
811
  sourceId: attr5("string", { indexed: true }),
729
812
  targetId: attr5("string", { indexed: true }),
730
813
  environment: attr5("string", { indexed: true }),
@@ -737,7 +820,7 @@ var memberContentControls = {
737
820
  };
738
821
  var memberContentLocal = {
739
822
  attrs: {
740
- id: id3(),
823
+ id: id4(),
741
824
  chapterId: attr5("string", { indexed: true }),
742
825
  environment: attr5("string", { indexed: true }),
743
826
  revision: attr5("number", { indexed: true }),
@@ -748,7 +831,7 @@ var memberContentLocal = {
748
831
  };
749
832
  var builderLibraryControls = {
750
833
  attrs: {
751
- id: id3(),
834
+ id: id4(),
752
835
  sourceId: attr5("string", { indexed: true }),
753
836
  targetId: attr5("string", { indexed: true }),
754
837
  environment: attr5("string", { indexed: true }),
@@ -761,7 +844,7 @@ var builderLibraryControls = {
761
844
  };
762
845
  var builderLibraryLocal = {
763
846
  attrs: {
764
- id: id3(),
847
+ id: id4(),
765
848
  appId: attr5("string", { indexed: true }),
766
849
  environment: attr5("string", { indexed: true }),
767
850
  revision: attr5("number", { indexed: true }),
@@ -772,7 +855,7 @@ var builderLibraryLocal = {
772
855
  };
773
856
  var groups = {
774
857
  attrs: {
775
- id: id3(),
858
+ id: id4(),
776
859
  name: attr5("string"),
777
860
  standardPriceCents: attr5("number"),
778
861
  foundingDiscountCents: attr5("number"),
@@ -795,7 +878,7 @@ var groups = {
795
878
  };
796
879
  var tiers = {
797
880
  attrs: {
798
- id: id3(),
881
+ id: id4(),
799
882
  groupId: attr5("string", { indexed: true }),
800
883
  name: attr5("string"),
801
884
  priceCents: attr5("number"),
@@ -808,7 +891,7 @@ var tiers = {
808
891
  };
809
892
  var meetings = {
810
893
  attrs: {
811
- id: id3(),
894
+ id: id4(),
812
895
  applicationId: attr5("string", { indexed: true }),
813
896
  groupId: attr5("string", { indexed: true }),
814
897
  startAt: attr5("number", { indexed: true }),
@@ -827,7 +910,7 @@ var meetings = {
827
910
  };
828
911
  var emailLog = {
829
912
  attrs: {
830
- id: id3(),
913
+ id: id4(),
831
914
  groupId: attr5("string", { indexed: true }),
832
915
  applicationId: attr5("string", { indexed: true, optional: true }),
833
916
  to: attr5("string", { indexed: true }),
@@ -842,7 +925,7 @@ var emailLog = {
842
925
  sentAt: attr5("number", { indexed: true })
843
926
  }
844
927
  };
845
- function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false) {
928
+ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false, includeNetworkPersonRegistry = false) {
846
929
  const entities = {};
847
930
  if (mode === "chapter") {
848
931
  entities.applications = applicationsFor(application);
@@ -876,6 +959,9 @@ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolv
876
959
  entities.leaderAdmissionDeliveries = leaderAdmissionDeliveriesEntity;
877
960
  entities.leaderAdmissionMutations = leaderAdmissionMutationsEntity;
878
961
  }
962
+ if (includeNetworkPersonRegistry) {
963
+ Object.assign(entities, networkPersonEntities);
964
+ }
879
965
  const schema = { entities, links: {} };
880
966
  const rules = {};
881
967
  for (const ns of Object.keys(entities)) {
@@ -1527,9 +1613,9 @@ var APPLICATION_LABELS = {
1527
1613
  phone: "Phone",
1528
1614
  state: "State"
1529
1615
  };
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;
1616
+ function humanize(id5) {
1617
+ const text4 = id5.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1618
+ return text4 ? `${text4[0].toUpperCase()}${text4.slice(1)}` : id5;
1533
1619
  }
1534
1620
  function applicationDefs(chapter) {
1535
1621
  try {
@@ -1538,23 +1624,23 @@ function applicationDefs(chapter) {
1538
1624
  return {};
1539
1625
  }
1540
1626
  }
1541
- function applicationType(id4, def) {
1627
+ function applicationType(id5, def) {
1542
1628
  if (def && ["string", "email", "url", "enum"].includes(def.type)) return def.type;
1543
- return id4 === "email" ? "email" : id4 === "linkedin" ? "url" : "string";
1629
+ return id5 === "email" ? "email" : id5 === "linkedin" ? "url" : "string";
1544
1630
  }
1545
1631
  function applicationFlowFields(chapter, application = chapter.application) {
1546
1632
  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);
1633
+ return [...application.required, ...application.optional].map((id5, order) => {
1634
+ const def = defs[id5];
1635
+ const conditions = application.conditions[id5];
1636
+ const type = applicationType(id5, def);
1551
1637
  return {
1552
- id: id4,
1553
- label: def?.label ?? APPLICATION_LABELS[id4] ?? humanize(id4),
1638
+ id: id5,
1639
+ label: def?.label ?? APPLICATION_LABELS[id5] ?? humanize(id5),
1554
1640
  type,
1555
- required: application.required.includes(id4),
1641
+ required: application.required.includes(id5),
1556
1642
  order,
1557
- maxLength: application.maxLen[id4] ?? application.defaultMaxLen,
1643
+ maxLength: application.maxLen[id5] ?? application.defaultMaxLen,
1558
1644
  ...type === "enum" && def?.options ? { options: [...def.options] } : {},
1559
1645
  ...conditions ? { conditions: { ...conditions } } : {},
1560
1646
  ...type === "email" ? { emailIdentity: "literal" } : {}
@@ -1564,17 +1650,17 @@ function applicationFlowFields(chapter, application = chapter.application) {
1564
1650
  function resolveFormationFlowFields(crm, formation) {
1565
1651
  if (!formation.enabled) return [];
1566
1652
  const def = crm.type(formation.type);
1567
- return [...formation.required, ...formation.optional].map((id4, order) => {
1568
- const field = def.fields[id4];
1653
+ return [...formation.required, ...formation.optional].map((id5, order) => {
1654
+ const field = def.fields[id5];
1569
1655
  return {
1570
- id: id4,
1571
- label: field.label ?? humanize(id4),
1656
+ id: id5,
1657
+ label: field.label ?? humanize(id5),
1572
1658
  type: field.type,
1573
- required: formation.required.includes(id4),
1659
+ required: formation.required.includes(id5),
1574
1660
  order,
1575
- maxLength: formation.maxLen[id4] ?? formation.defaultMaxLen,
1661
+ maxLength: formation.maxLen[id5] ?? formation.defaultMaxLen,
1576
1662
  ...field.options ? { options: [...field.options] } : {},
1577
- ...id4 === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
1663
+ ...id5 === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
1578
1664
  };
1579
1665
  });
1580
1666
  }
@@ -1868,12 +1954,30 @@ function resolveNetwork(config, crm) {
1868
1954
  return { targets, readers };
1869
1955
  }
1870
1956
 
1957
+ // src/config-network-person.ts
1958
+ var SLUG2 = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
1959
+ function resolveNetworkPersonRegistry(mode, input, targetIds) {
1960
+ if (!input) return null;
1961
+ if (mode !== "hub") throw new Error("defineChapter.networkPersonRegistry: only hub mode may own network Person authority");
1962
+ if (input.mode !== "shadow") throw new Error('defineChapter.networkPersonRegistry.mode: must be "shadow"');
1963
+ const chapterIds = input.chapterIds ? [...input.chapterIds] : [...targetIds];
1964
+ if (chapterIds.length === 0) throw new Error("defineChapter.networkPersonRegistry.chapterIds: at least one chapter is required");
1965
+ if (new Set(chapterIds).size !== chapterIds.length || chapterIds.some((id5) => !SLUG2.test(id5))) {
1966
+ throw new Error("defineChapter.networkPersonRegistry.chapterIds: expected distinct lowercase slugs");
1967
+ }
1968
+ const unknown = chapterIds.filter((id5) => !targetIds.includes(id5));
1969
+ if (unknown.length) {
1970
+ throw new Error(`defineChapter.networkPersonRegistry.chapterIds: ${unknown.map((id5) => `"${id5}"`).join(", ")} are not configured network targets`);
1971
+ }
1972
+ return { mode: "shadow", chapterIds };
1973
+ }
1974
+
1871
1975
  // src/config-signup-control.ts
1872
- var SLUG2 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1976
+ var SLUG3 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1873
1977
  var SECRET_NAME = /^[a-z][a-z0-9_]{1,127}$/;
1874
1978
  function resolveSignupControl(input) {
1875
1979
  if (!input) return null;
1876
- if (!SLUG2.test(input.sourceId)) {
1980
+ if (!SLUG3.test(input.sourceId)) {
1877
1981
  throw new Error("defineChapter.signupControl.sourceId: must be a lowercase app slug");
1878
1982
  }
1879
1983
  if (input.stripeMode !== "test" && input.stripeMode !== "live") {
@@ -1887,12 +1991,12 @@ function resolveSignupControl(input) {
1887
1991
  }
1888
1992
 
1889
1993
  // src/config-member-content.ts
1890
- var SLUG3 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1994
+ var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1891
1995
  var SECRET_NAME2 = /^[a-z][a-z0-9_]{1,127}$/;
1892
1996
  function resolveMemberContent(input) {
1893
1997
  if (!input) return null;
1894
1998
  const parent = input.parent;
1895
- if (parent && !SLUG3.test(parent.sourceId)) {
1999
+ if (parent && !SLUG4.test(parent.sourceId)) {
1896
2000
  throw new Error("defineChapter.memberContent.parent.sourceId: must be a lowercase app slug");
1897
2001
  }
1898
2002
  const secretName = parent?.secretName?.trim() || "member_content_control_secret";
@@ -1907,7 +2011,7 @@ function resolveMemberContent(input) {
1907
2011
  }
1908
2012
 
1909
2013
  // src/config-builder-library.ts
1910
- var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
2014
+ var SLUG5 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1911
2015
  function resolveBuilderLibrary(mode, input, network = { targets: [], readers: [] }) {
1912
2016
  if (!input) return null;
1913
2017
  if (input.sources !== void 0 && !Array.isArray(input.sources)) {
@@ -1915,7 +2019,7 @@ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: []
1915
2019
  }
1916
2020
  const seen = /* @__PURE__ */ new Set();
1917
2021
  const sources = (input.sources ?? []).map((source, index) => {
1918
- if (!source || typeof source !== "object" || !SLUG4.test(source.sourceId)) {
2022
+ if (!source || typeof source !== "object" || !SLUG5.test(source.sourceId)) {
1919
2023
  throw new Error(`defineChapter.builderLibrary.sources[${index}].sourceId: must be a lowercase app slug`);
1920
2024
  }
1921
2025
  if (source.sourceId === "built-not-found" && mode === "hub") {
@@ -1946,15 +2050,15 @@ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: []
1946
2050
  }
1947
2051
 
1948
2052
  // src/config.ts
1949
- var SLUG5 = /^[a-z0-9][a-z0-9-]{1,62}$/;
2053
+ var SLUG6 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1950
2054
  function isResolvedCrm(x) {
1951
2055
  return !!x && typeof x === "object" && "prepare" in x && typeof x.prepare === "function";
1952
2056
  }
1953
2057
  function defineChapter(config) {
1954
2058
  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)}`);
2059
+ const { id: id5, name } = config;
2060
+ if (typeof id5 !== "string" || !SLUG6.test(id5)) {
2061
+ throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id5)}`);
1958
2062
  }
1959
2063
  if (typeof name !== "string" || name.trim() === "") throw new Error("defineChapter.name: a non-empty name is required");
1960
2064
  const mode = config.mode ?? "chapter";
@@ -1977,6 +2081,11 @@ function defineChapter(config) {
1977
2081
  const brand = { ...config.brand, wordmark: config.brand?.wordmark ?? name };
1978
2082
  const copy = resolveChapterCopy(config.copy);
1979
2083
  const network = resolveNetwork(config, crm);
2084
+ const networkPersonRegistry = resolveNetworkPersonRegistry(
2085
+ mode,
2086
+ config.networkPersonRegistry,
2087
+ network.targets.map((target) => target.id)
2088
+ );
1980
2089
  const signupControl = resolveSignupControl(config.signupControl);
1981
2090
  const memberContent = resolveMemberContent(config.memberContent);
1982
2091
  const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary, network);
@@ -1995,7 +2104,8 @@ function defineChapter(config) {
1995
2104
  Boolean(signupControl),
1996
2105
  Boolean(memberContent),
1997
2106
  Boolean(builderLibrary),
1998
- network.targets.some((target) => target.admissionGrants)
2107
+ network.targets.some((target) => target.admissionGrants),
2108
+ Boolean(networkPersonRegistry)
1999
2109
  );
2000
2110
  const services = config.services ?? ["db", "calendar", "o11y"];
2001
2111
  const account = config.account ?? "none";
@@ -2045,7 +2155,7 @@ function defineChapter(config) {
2045
2155
  };
2046
2156
  const chapter = {
2047
2157
  config,
2048
- id: id4,
2158
+ id: id5,
2049
2159
  name,
2050
2160
  mode,
2051
2161
  brand,
@@ -2054,6 +2164,7 @@ function defineChapter(config) {
2054
2164
  signupControl,
2055
2165
  memberContent,
2056
2166
  builderLibrary,
2167
+ networkPersonRegistry,
2057
2168
  formation,
2058
2169
  crm,
2059
2170
  auth,
@@ -2365,7 +2476,8 @@ function createChapterIntegration(chapter, options = {}) {
2365
2476
  { path: "/api/builder-library-control", expectedStatus: 401 },
2366
2477
  { path: "/api/admin/builder-library", expectedStatus: 401 },
2367
2478
  { path: "/api/admin/builder-library/manifest", expectedStatus: 401 }
2368
- ] : []
2479
+ ] : [],
2480
+ ...chapter.networkPersonRegistry ? [{ path: "/api/admin/network-person-registry", expectedStatus: 401 }] : []
2369
2481
  ],
2370
2482
  runbooks
2371
2483
  };
@@ -2373,7 +2485,7 @@ function createChapterIntegration(chapter, options = {}) {
2373
2485
 
2374
2486
  // src/member-content-validation-helpers.ts
2375
2487
  var ID = /^[a-z][a-z0-9-]{0,79}$/;
2376
- var SLUG6 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2488
+ var SLUG7 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2377
2489
  var ROOT_KEYS = /* @__PURE__ */ new Set(["schemaVersion", "revision", "generatedAt", "source", "target", "pages", "upsellOverrides"]);
2378
2490
  var SOURCE_KEYS = /* @__PURE__ */ new Set(["type", "appId"]);
2379
2491
  var TARGET_KEYS = /* @__PURE__ */ new Set(["chapterId", "environment"]);
@@ -2479,14 +2591,14 @@ function validateMemberContentDocument(input, options = {}) {
2479
2591
  continue;
2480
2592
  }
2481
2593
  unknownKeys(raw, PAGE_KEYS, path, errors);
2482
- const id4 = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2594
+ const id5 = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2483
2595
  const slug = textValue(raw.slug, `${path}.slug`, errors, 120, true) ?? "";
2484
2596
  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}"`);
2597
+ if (id5 && !ID.test(id5)) errors.push(`${path}.id must be a lowercase kebab-case id`);
2598
+ if (slug && !SLUG7.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
2599
+ if (ids.has(id5) || knownParentIds.has(id5)) errors.push(`${path}.id duplicates page "${id5}"`);
2488
2600
  if (slugs.has(slug) || (options.parentPages ?? []).some((page) => page.slug === slug)) errors.push(`${path}.slug duplicates "${slug}"`);
2489
- ids.add(id4);
2601
+ ids.add(id5);
2490
2602
  slugs.add(slug);
2491
2603
  const navRaw = record(raw.navigation);
2492
2604
  let navigation = { section: "General", order: index };
@@ -2542,7 +2654,7 @@ function validateMemberContentDocument(input, options = {}) {
2542
2654
  else if (owner === "chapter") upsell = { owner, ...offer ? { offer } : {} };
2543
2655
  }
2544
2656
  }
2545
- pages.push({ id: id4, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2657
+ pages.push({ id: id5, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2546
2658
  }
2547
2659
  const allIds = /* @__PURE__ */ new Set([...knownParentIds, ...ids]);
2548
2660
  for (const [index, page] of pages.entries()) {
@@ -2748,8 +2860,8 @@ function resourceValue(value, path, errors) {
2748
2860
  return null;
2749
2861
  }
2750
2862
  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`);
2863
+ const id5 = text(raw.id, `${path}.id`, errors, 80) ?? "";
2864
+ if (id5 && !ID2.test(id5)) errors.push(`${path}.id must be lowercase kebab-case`);
2753
2865
  const kind = text(raw.kind, `${path}.kind`, errors) ?? "";
2754
2866
  if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
2755
2867
  const title = text(raw.title, `${path}.title`, errors, 240) ?? "";
@@ -2806,7 +2918,7 @@ function resourceValue(value, path, errors) {
2806
2918
  updatedAt: integer(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
2807
2919
  ...promotedFrom ? { promotedFrom } : {}
2808
2920
  };
2809
- return { id: id4, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
2921
+ return { id: id5, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
2810
2922
  }
2811
2923
  function validateBuilderLibraryDocument(value, options = {}) {
2812
2924
  const errors = [];
@@ -2837,7 +2949,7 @@ function validateBuilderLibraryDocument(value, options = {}) {
2837
2949
  const inputs = Array.isArray(raw.resources) ? raw.resources : [];
2838
2950
  if (inputs.length > MAX_RESOURCES) errors.push(`document.resources may contain at most ${MAX_RESOURCES} items`);
2839
2951
  const resources = inputs.map((item, index) => resourceValue(item, `document.resources[${index}]`, errors)).filter(Boolean);
2840
- const ids = resources.map(({ id: id4 }) => id4);
2952
+ const ids = resources.map(({ id: id5 }) => id5);
2841
2953
  if (new Set(ids).size !== ids.length) errors.push("document.resources ids must be unique");
2842
2954
  for (const resource of resources) {
2843
2955
  if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
@@ -2992,9 +3104,9 @@ async function sendTemplated(deps, input) {
2992
3104
  error = e instanceof Error ? e.message : String(e);
2993
3105
  }
2994
3106
  }
2995
- const id4 = deps.newId();
3107
+ const id5 = deps.newId();
2996
3108
  const row = {
2997
- id: id4,
3109
+ id: id5,
2998
3110
  groupId: input.group.id,
2999
3111
  to: decision.to,
3000
3112
  template: input.template,
@@ -3008,7 +3120,7 @@ async function sendTemplated(deps, input) {
3008
3120
  ...messageId ? { messageId } : {},
3009
3121
  ...error ? { error } : {}
3010
3122
  };
3011
- await deps.db.transact([{ t: "update", ns: "emailLog", id: id4, attrs: row }], error ? void 0 : { mutationId: `email:${input.dedupeKey}` });
3123
+ await deps.db.transact([{ t: "update", ns: "emailLog", id: id5, attrs: row }], error ? void 0 : { mutationId: `email:${input.dedupeKey}` });
3012
3124
  return error ? { sent: false, reason: error } : { sent: true };
3013
3125
  }
3014
3126
  function emailGroupFrom(row) {
@@ -3196,14 +3308,14 @@ function validateAdmissionGrantDocument(input) {
3196
3308
  const seenEmails = /* @__PURE__ */ new Set();
3197
3309
  for (const [index, raw] of rawGrants.slice(0, MAX_GRANTS).entries()) {
3198
3310
  const grant = raw && typeof raw === "object" ? raw : {};
3199
- const id4 = typeof grant.id === "string" ? grant.id : "";
3311
+ const id5 = typeof grant.id === "string" ? grant.id : "";
3200
3312
  const email = typeof grant.email === "string" ? grant.email.trim() : "";
3201
3313
  const normalized = normalizeAdmissionEmail(email);
3202
3314
  const decision = grant.decision;
3203
3315
  const note = typeof grant.note === "string" ? grant.note.trim() : void 0;
3204
3316
  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}"`);
3317
+ if (!ID3.test(id5)) errors.push(`document.grants[${index}].id must be an identifier`);
3318
+ if (seenIds.has(id5)) errors.push(`document.grants repeats id "${id5}"`);
3207
3319
  if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index}].email must be valid`);
3208
3320
  if (seenEmails.has(normalized)) errors.push(`document.grants repeats email "${normalized}"`);
3209
3321
  if (decision !== "waive_interview" && decision !== "require_interview") {
@@ -3213,10 +3325,10 @@ function validateAdmissionGrantDocument(input) {
3213
3325
  if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= 0)) {
3214
3326
  errors.push(`document.grants[${index}].expiresAt must be epoch milliseconds`);
3215
3327
  }
3216
- seenIds.add(id4);
3328
+ seenIds.add(id5);
3217
3329
  seenEmails.add(normalized);
3218
3330
  grants.push({
3219
- id: id4,
3331
+ id: id5,
3220
3332
  email,
3221
3333
  decision,
3222
3334
  ...note ? { note } : {},
@@ -3287,8 +3399,8 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3287
3399
  const incomingIds = new Set(document.grants.map((grant) => `admission:parent:${document.source.appId}:${grant.id}`));
3288
3400
  const ops = [];
3289
3401
  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: {
3402
+ const id5 = String(row.id ?? "");
3403
+ if (id5 && !incomingIds.has(id5)) ops.push({ t: "update", ns: "admissionGrants", id: id5, attrs: {
3292
3404
  active: false,
3293
3405
  revokedAt: receivedAt,
3294
3406
  revokedBy: document.source.actor,
@@ -3297,10 +3409,10 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3297
3409
  } });
3298
3410
  }
3299
3411
  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,
3412
+ const id5 = `admission:parent:${document.source.appId}:${grant.id}`;
3413
+ const existing = current.find((row) => row.id === id5);
3414
+ ops.push({ t: "update", ns: "admissionGrants", id: id5, attrs: {
3415
+ id: id5,
3304
3416
  sourceGrantId: grant.id,
3305
3417
  sourceScope: "parent",
3306
3418
  sourceId: document.source.appId,
@@ -3321,7 +3433,7 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3321
3433
  ...!grant.note && existing?.note !== void 0 ? ["note"] : [],
3322
3434
  ...grant.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
3323
3435
  ];
3324
- if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id4, attrs: retract });
3436
+ if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id5, attrs: retract });
3325
3437
  }
3326
3438
  if (previous?.active) ops.push({ t: "update", ns: "admissionGrantRevisions", id: previous.id, attrs: { active: false } });
3327
3439
  const revisionId = `admission-revision:${document.source.appId}:${document.target.environment}:${document.revision}`;
@@ -3391,10 +3503,10 @@ function validateGiftOfferCatalog(input, tierIds) {
3391
3503
  }
3392
3504
  const allowed2 = /* @__PURE__ */ new Set(["id", "target", "billingMode", "stripePriceId", "priceCents", "currency", "redemptionLifetimeHours", "activeFrom", "activeUntil", "title", "description", "ctaLabel"]);
3393
3505
  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);
3506
+ const id5 = text2(item.id, `${path}.id`, errors, 80);
3507
+ if (id5 && !ID4.test(id5)) errors.push(`${path}.id must be a lowercase kebab-case id`);
3508
+ if (ids.has(id5)) errors.push(`${path}.id duplicates "${id5}"`);
3509
+ ids.add(id5);
3398
3510
  const targetRaw = record3(item.target);
3399
3511
  let target = { type: "entitlement", entitlementKey: "invalid" };
3400
3512
  if (targetRaw?.type === "tier") {
@@ -3420,7 +3532,7 @@ function validateGiftOfferCatalog(input, tierIds) {
3420
3532
  if (activeUntil !== void 0 && (!Number.isSafeInteger(activeUntil) || activeUntil < 1)) errors.push(`${path}.activeUntil must be epoch milliseconds`);
3421
3533
  if (activeFrom !== void 0 && activeUntil !== void 0 && activeUntil <= activeFrom) errors.push(`${path}.activeUntil must follow activeFrom`);
3422
3534
  offers.push({
3423
- id: id4,
3535
+ id: id5,
3424
3536
  target,
3425
3537
  billingMode: item.billingMode,
3426
3538
  stripePriceId,
@@ -3833,9 +3945,9 @@ async function updateClerkUserMetadataByEmail(secretKey, email, input, fetchImpl
3833
3945
  const found = await fetchImpl(`https://api.clerk.com/v1/users?email_address=${encodeURIComponent(email)}&limit=1`, { headers: auth });
3834
3946
  if (!found.ok) return false;
3835
3947
  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);
3948
+ const id5 = Array.isArray(users) && typeof users[0]?.id === "string" ? users[0].id : void 0;
3949
+ if (!id5) return false;
3950
+ return updateClerkUserMetadata(secretKey, id5, input, fetchImpl);
3839
3951
  }
3840
3952
  async function createClerkUser(secretKey, input, fetchImpl = fetch) {
3841
3953
  const { path, body } = clerkUserRequest(input);
@@ -3898,8 +4010,8 @@ async function clerkGetUserByEmail(secretKey, email, fetchImpl = fetch) {
3898
4010
  const user = Array.isArray(data) ? data[0] : void 0;
3899
4011
  return user ? toRecord(user) : null;
3900
4012
  }
3901
- async function clerkGetUser(secretKey, id4, fetchImpl = fetch) {
3902
- const data = await clerkGet(`/v1/users/${encodeURIComponent(id4)}`, secretKey, fetchImpl).catch(() => null);
4013
+ async function clerkGetUser(secretKey, id5, fetchImpl = fetch) {
4014
+ const data = await clerkGet(`/v1/users/${encodeURIComponent(id5)}`, secretKey, fetchImpl).catch(() => null);
3903
4015
  return data ? toRecord(data) : null;
3904
4016
  }
3905
4017
  async function clerkListUsers(secretKey, fetchImpl = fetch) {
@@ -3915,8 +4027,8 @@ async function clerkListUsers(secretKey, fetchImpl = fetch) {
3915
4027
  }
3916
4028
  return out;
3917
4029
  }
3918
- async function clerkSetRole(secretKey, id4, role, fetchImpl = fetch) {
3919
- const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id4)}/metadata`, {
4030
+ async function clerkSetRole(secretKey, id5, role, fetchImpl = fetch) {
4031
+ const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id5)}/metadata`, {
3920
4032
  method: "PATCH",
3921
4033
  headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
3922
4034
  body: JSON.stringify({ public_metadata: { role } })
@@ -4482,7 +4594,7 @@ function membershipPageDocument(input, options = {}) {
4482
4594
  }
4483
4595
 
4484
4596
  // src/signup-control-validate.ts
4485
- var SLUG7 = /^[a-z0-9][a-z0-9-]{1,62}$/;
4597
+ var SLUG8 = /^[a-z0-9][a-z0-9-]{1,62}$/;
4486
4598
  var ENVIRONMENT = /^[a-z][a-z0-9_-]{0,62}$/;
4487
4599
  var TIER = /^[a-z][a-z0-9-]{0,79}$/;
4488
4600
  var PRICE2 = /^price_[A-Za-z0-9]{12,}$/;
@@ -4528,10 +4640,10 @@ function tiersOf(value, errors) {
4528
4640
  return [];
4529
4641
  }
4530
4642
  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);
4643
+ const id5 = text3(item.id, `${path}.id`, errors, 80);
4644
+ if (id5 && !TIER.test(id5)) errors.push(`${path}.id must be a lowercase kebab-case id`);
4645
+ if (seen.has(id5)) errors.push(`${path}.id duplicates "${id5}"`);
4646
+ seen.add(id5);
4535
4647
  const name = text3(item.name, `${path}.name`, errors, 160);
4536
4648
  if (typeof item.available !== "boolean") errors.push(`${path}.available must be boolean`);
4537
4649
  if (!Number.isInteger(item.sortOrder) || Number(item.sortOrder) < 0) errors.push(`${path}.sortOrder must be a non-negative integer`);
@@ -4546,7 +4658,7 @@ function tiersOf(value, errors) {
4546
4658
  }
4547
4659
  const blurb = item.blurb === void 0 ? void 0 : text3(item.blurb, `${path}.blurb`, errors, 4e3);
4548
4660
  return [{
4549
- id: id4,
4661
+ id: id5,
4550
4662
  name,
4551
4663
  available: item.available === true,
4552
4664
  sortOrder: Number(item.sortOrder) || 0,
@@ -4641,8 +4753,8 @@ function validateSignupControlDocument(input) {
4641
4753
  const appId = text3(source?.appId, "document.source.appId", errors, 63);
4642
4754
  const chapterId = text3(target?.chapterId, "document.target.chapterId", errors, 63);
4643
4755
  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");
4756
+ if (appId && !SLUG8.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
4757
+ if (chapterId && !SLUG8.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
4646
4758
  if (environment && !ENVIRONMENT.test(environment)) errors.push("document.target.environment is invalid");
4647
4759
  if (target?.stripeMode !== "test" && target?.stripeMode !== "live") errors.push("document.target.stripeMode must be test or live");
4648
4760
  const tiers2 = tiersOf(root.tiers, errors);
@@ -4919,7 +5031,7 @@ var exclusions = (chapterId, paidTierIds) => [
4919
5031
  id: chapterId,
4920
5032
  reason: "provider_identity",
4921
5033
  paths: [
4922
- ...paidTierIds.map((id4) => `tiers[${id4}].stripePriceId`),
5034
+ ...paidTierIds.map((id5) => `tiers[${id5}].stripePriceId`),
4923
5035
  "groups.stripePublishableKey",
4924
5036
  "applications.stripeCustomerId",
4925
5037
  "applications.stripeSubscriptionId",
@@ -4961,9 +5073,9 @@ async function exportChapterPromotion(input) {
4961
5073
  sourceIds: [`signup-control:${document.revision}`, document.source.appId, document.target.chapterId].sort(),
4962
5074
  receiptDigests: [input.digest]
4963
5075
  };
4964
- const artifact = (kind, id4, content) => ({
5076
+ const artifact = (kind, id5, content) => ({
4965
5077
  kind,
4966
- id: id4,
5078
+ id: id5,
4967
5079
  sourceRevision: revision,
4968
5080
  provenance: promotionProvenance,
4969
5081
  content: (0, import_promotion.canonicalPromotionValue)(content)
@@ -5002,7 +5114,7 @@ async function exportChapterPromotion(input) {
5002
5114
  }
5003
5115
 
5004
5116
  // src/member-system-authority.ts
5005
- var rows2 = (group) => group.fields.map((id4) => ({ ...group, id: id4 }));
5117
+ var rows2 = (group) => group.fields.map((id5) => ({ ...group, id: id5 }));
5006
5118
  var MEMBER_SYSTEM_AUTHORITY_MATRIX = [
5007
5119
  ...rows2({
5008
5120
  scope: "network",