@odla-ai/chapter 0.43.2 → 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.js CHANGED
@@ -115,8 +115,8 @@ async function submitApplication(db, chapter, fields, opts) {
115
115
  if (app.requireDisclaimerAck && !acked) {
116
116
  return { ok: false, error: "disclaimerAck is required" };
117
117
  }
118
- const id4 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
119
- const row = { id: id4, status: chapter.pipeline.initial, createdAt: opts.now };
118
+ const id5 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
119
+ const row = { id: id5, status: chapter.pipeline.initial, createdAt: opts.now };
120
120
  for (const f of [...app.required, ...app.optional]) {
121
121
  if (fieldStates[f]?.visible === false) continue;
122
122
  if (typeof fields[f] === "string") row[f] = fields[f].trim();
@@ -140,19 +140,19 @@ async function submitApplication(db, chapter, fields, opts) {
140
140
  }
141
141
  if (acked) row.disclaimerAckAt = opts.now;
142
142
  const ops = [
143
- { t: "update", ns: "applications", id: id4, attrs: row }
143
+ { t: "update", ns: "applications", id: id5, attrs: row }
144
144
  ];
145
145
  if (opts.admissionGrant?.decision === "waive_interview") {
146
146
  ops.push({ t: "update", ns: "admissionGrants", id: opts.admissionGrant.id, attrs: {
147
147
  consumedAt: opts.now,
148
- consumedApplicationId: id4
148
+ consumedApplicationId: id5
149
149
  } });
150
150
  }
151
151
  const { duplicate } = await db.transact(
152
152
  ops,
153
153
  opts.submissionId ? { mutationId: `join:${opts.submissionId}` } : void 0
154
154
  );
155
- return { ok: true, id: id4, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
155
+ return { ok: true, id: id5, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
156
156
  }
157
157
  function joinConfig(group, paymentsReady2, tiers2 = [], conditions = {}) {
158
158
  return {
@@ -466,6 +466,88 @@ var stripeEventReceiptsEntity = {
466
466
  }
467
467
  };
468
468
 
469
+ // src/schema-network-person.ts
470
+ var string = (flags = {}) => ({
471
+ type: "string",
472
+ unique: flags.unique ?? false,
473
+ indexed: flags.indexed ?? false,
474
+ optional: flags.optional ?? false
475
+ });
476
+ var number = (flags = {}) => ({
477
+ type: "number",
478
+ unique: false,
479
+ indexed: flags.indexed ?? false,
480
+ optional: flags.optional ?? false
481
+ });
482
+ var json = (flags = {}) => ({
483
+ type: "json",
484
+ unique: false,
485
+ indexed: false,
486
+ optional: flags.optional ?? false
487
+ });
488
+ var id3 = () => string({ unique: true, indexed: true });
489
+ var networkPersonsEntity = { attrs: {
490
+ id: id3(),
491
+ displayName: string({ indexed: true }),
492
+ state: string({ indexed: true }),
493
+ mergedIntoId: string({ indexed: true, optional: true }),
494
+ contactPolicy: json(),
495
+ revision: number({ indexed: true }),
496
+ createdAt: number({ indexed: true }),
497
+ updatedAt: number({ indexed: true })
498
+ } };
499
+ var networkIdentityClaimsEntity = { attrs: {
500
+ id: id3(),
501
+ personId: string({ indexed: true }),
502
+ kind: string({ indexed: true }),
503
+ value: string(),
504
+ hint: string({ optional: true }),
505
+ verifiedAt: number({ indexed: true }),
506
+ createdAt: number({ indexed: true })
507
+ } };
508
+ var networkPersonAliasesEntity = { attrs: {
509
+ id: id3(),
510
+ sourceSystem: string({ indexed: true }),
511
+ sourceRecordId: string({ indexed: true }),
512
+ sourceKey: string({ unique: true, indexed: true }),
513
+ displayName: string({ indexed: true }),
514
+ provenance: string(),
515
+ consent: json(),
516
+ status: string({ indexed: true }),
517
+ personId: string({ indexed: true, optional: true }),
518
+ candidatePersonIds: json({ optional: true }),
519
+ identityClaims: json(),
520
+ chapterLinks: json(),
521
+ revision: number({ indexed: true }),
522
+ createdAt: number({ indexed: true }),
523
+ updatedAt: number({ indexed: true })
524
+ } };
525
+ var networkChapterPersonLinksEntity = { attrs: {
526
+ id: id3(),
527
+ personId: string({ indexed: true }),
528
+ chapterId: string({ indexed: true }),
529
+ chapterPersonId: string({ indexed: true }),
530
+ state: string({ indexed: true }),
531
+ createdAt: number({ indexed: true })
532
+ } };
533
+ var networkPersonEventsEntity = { attrs: {
534
+ id: id3(),
535
+ kind: string({ indexed: true }),
536
+ personId: string({ indexed: true, optional: true }),
537
+ aliasId: string({ indexed: true, optional: true }),
538
+ actorId: string({ indexed: true }),
539
+ mutationId: string({ unique: true, indexed: true }),
540
+ detail: json(),
541
+ createdAt: number({ indexed: true })
542
+ } };
543
+ var networkPersonEntities = {
544
+ networkPersons: networkPersonsEntity,
545
+ networkIdentityClaims: networkIdentityClaimsEntity,
546
+ networkPersonAliases: networkPersonAliasesEntity,
547
+ networkChapterPersonLinks: networkChapterPersonLinksEntity,
548
+ networkPersonEvents: networkPersonEventsEntity
549
+ };
550
+
469
551
  // src/schema.ts
470
552
  function attr5(type, flags = {}) {
471
553
  return {
@@ -475,10 +557,10 @@ function attr5(type, flags = {}) {
475
557
  optional: flags.optional ?? false
476
558
  };
477
559
  }
478
- var id3 = () => attr5("string", { unique: true, indexed: true });
560
+ var id4 = () => attr5("string", { unique: true, indexed: true });
479
561
  var admins = {
480
562
  attrs: {
481
- id: id3(),
563
+ id: id4(),
482
564
  email: attr5("string", { unique: true, indexed: true }),
483
565
  name: attr5("string", { optional: true }),
484
566
  note: attr5("string", { optional: true })
@@ -486,7 +568,7 @@ var admins = {
486
568
  };
487
569
  var superAdmins = {
488
570
  attrs: {
489
- id: id3(),
571
+ id: id4(),
490
572
  email: attr5("string", { unique: true, indexed: true }),
491
573
  note: attr5("string", { optional: true }),
492
574
  createdAt: attr5("number", { indexed: true })
@@ -494,7 +576,7 @@ var superAdmins = {
494
576
  };
495
577
  var networkNotes = {
496
578
  attrs: {
497
- id: id3(),
579
+ id: id4(),
498
580
  recordKey: attr5("string", { indexed: true }),
499
581
  targetId: attr5("string", { indexed: true }),
500
582
  recordType: attr5("string", { indexed: true }),
@@ -507,7 +589,7 @@ var networkNotes = {
507
589
  };
508
590
  var signupControls = {
509
591
  attrs: {
510
- id: id3(),
592
+ id: id4(),
511
593
  sourceId: attr5("string", { indexed: true }),
512
594
  targetId: attr5("string", { indexed: true }),
513
595
  environment: attr5("string", { indexed: true }),
@@ -520,7 +602,7 @@ var signupControls = {
520
602
  };
521
603
  var memberContentControls = {
522
604
  attrs: {
523
- id: id3(),
605
+ id: id4(),
524
606
  sourceId: attr5("string", { indexed: true }),
525
607
  targetId: attr5("string", { indexed: true }),
526
608
  environment: attr5("string", { indexed: true }),
@@ -533,7 +615,7 @@ var memberContentControls = {
533
615
  };
534
616
  var memberContentLocal = {
535
617
  attrs: {
536
- id: id3(),
618
+ id: id4(),
537
619
  chapterId: attr5("string", { indexed: true }),
538
620
  environment: attr5("string", { indexed: true }),
539
621
  revision: attr5("number", { indexed: true }),
@@ -544,7 +626,7 @@ var memberContentLocal = {
544
626
  };
545
627
  var builderLibraryControls = {
546
628
  attrs: {
547
- id: id3(),
629
+ id: id4(),
548
630
  sourceId: attr5("string", { indexed: true }),
549
631
  targetId: attr5("string", { indexed: true }),
550
632
  environment: attr5("string", { indexed: true }),
@@ -557,7 +639,7 @@ var builderLibraryControls = {
557
639
  };
558
640
  var builderLibraryLocal = {
559
641
  attrs: {
560
- id: id3(),
642
+ id: id4(),
561
643
  appId: attr5("string", { indexed: true }),
562
644
  environment: attr5("string", { indexed: true }),
563
645
  revision: attr5("number", { indexed: true }),
@@ -568,7 +650,7 @@ var builderLibraryLocal = {
568
650
  };
569
651
  var groups = {
570
652
  attrs: {
571
- id: id3(),
653
+ id: id4(),
572
654
  name: attr5("string"),
573
655
  standardPriceCents: attr5("number"),
574
656
  foundingDiscountCents: attr5("number"),
@@ -591,7 +673,7 @@ var groups = {
591
673
  };
592
674
  var tiers = {
593
675
  attrs: {
594
- id: id3(),
676
+ id: id4(),
595
677
  groupId: attr5("string", { indexed: true }),
596
678
  name: attr5("string"),
597
679
  priceCents: attr5("number"),
@@ -604,7 +686,7 @@ var tiers = {
604
686
  };
605
687
  var meetings = {
606
688
  attrs: {
607
- id: id3(),
689
+ id: id4(),
608
690
  applicationId: attr5("string", { indexed: true }),
609
691
  groupId: attr5("string", { indexed: true }),
610
692
  startAt: attr5("number", { indexed: true }),
@@ -623,7 +705,7 @@ var meetings = {
623
705
  };
624
706
  var emailLog = {
625
707
  attrs: {
626
- id: id3(),
708
+ id: id4(),
627
709
  groupId: attr5("string", { indexed: true }),
628
710
  applicationId: attr5("string", { indexed: true, optional: true }),
629
711
  to: attr5("string", { indexed: true }),
@@ -638,7 +720,7 @@ var emailLog = {
638
720
  sentAt: attr5("number", { indexed: true })
639
721
  }
640
722
  };
641
- function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false) {
723
+ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false, includeNetworkPersonRegistry = false) {
642
724
  const entities = {};
643
725
  if (mode === "chapter") {
644
726
  entities.applications = applicationsFor(application);
@@ -672,6 +754,9 @@ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolv
672
754
  entities.leaderAdmissionDeliveries = leaderAdmissionDeliveriesEntity;
673
755
  entities.leaderAdmissionMutations = leaderAdmissionMutationsEntity;
674
756
  }
757
+ if (includeNetworkPersonRegistry) {
758
+ Object.assign(entities, networkPersonEntities);
759
+ }
675
760
  const schema = { entities, links: {} };
676
761
  const rules = {};
677
762
  for (const ns of Object.keys(entities)) {
@@ -1323,9 +1408,9 @@ var APPLICATION_LABELS = {
1323
1408
  phone: "Phone",
1324
1409
  state: "State"
1325
1410
  };
1326
- function humanize(id4) {
1327
- const text4 = id4.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1328
- return text4 ? `${text4[0].toUpperCase()}${text4.slice(1)}` : id4;
1411
+ function humanize(id5) {
1412
+ const text4 = id5.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
1413
+ return text4 ? `${text4[0].toUpperCase()}${text4.slice(1)}` : id5;
1329
1414
  }
1330
1415
  function applicationDefs(chapter) {
1331
1416
  try {
@@ -1334,23 +1419,23 @@ function applicationDefs(chapter) {
1334
1419
  return {};
1335
1420
  }
1336
1421
  }
1337
- function applicationType(id4, def) {
1422
+ function applicationType(id5, def) {
1338
1423
  if (def && ["string", "email", "url", "enum"].includes(def.type)) return def.type;
1339
- return id4 === "email" ? "email" : id4 === "linkedin" ? "url" : "string";
1424
+ return id5 === "email" ? "email" : id5 === "linkedin" ? "url" : "string";
1340
1425
  }
1341
1426
  function applicationFlowFields(chapter, application = chapter.application) {
1342
1427
  const defs = applicationDefs(chapter);
1343
- return [...application.required, ...application.optional].map((id4, order) => {
1344
- const def = defs[id4];
1345
- const conditions = application.conditions[id4];
1346
- const type = applicationType(id4, def);
1428
+ return [...application.required, ...application.optional].map((id5, order) => {
1429
+ const def = defs[id5];
1430
+ const conditions = application.conditions[id5];
1431
+ const type = applicationType(id5, def);
1347
1432
  return {
1348
- id: id4,
1349
- label: def?.label ?? APPLICATION_LABELS[id4] ?? humanize(id4),
1433
+ id: id5,
1434
+ label: def?.label ?? APPLICATION_LABELS[id5] ?? humanize(id5),
1350
1435
  type,
1351
- required: application.required.includes(id4),
1436
+ required: application.required.includes(id5),
1352
1437
  order,
1353
- maxLength: application.maxLen[id4] ?? application.defaultMaxLen,
1438
+ maxLength: application.maxLen[id5] ?? application.defaultMaxLen,
1354
1439
  ...type === "enum" && def?.options ? { options: [...def.options] } : {},
1355
1440
  ...conditions ? { conditions: { ...conditions } } : {},
1356
1441
  ...type === "email" ? { emailIdentity: "literal" } : {}
@@ -1360,17 +1445,17 @@ function applicationFlowFields(chapter, application = chapter.application) {
1360
1445
  function resolveFormationFlowFields(crm, formation) {
1361
1446
  if (!formation.enabled) return [];
1362
1447
  const def = crm.type(formation.type);
1363
- return [...formation.required, ...formation.optional].map((id4, order) => {
1364
- const field = def.fields[id4];
1448
+ return [...formation.required, ...formation.optional].map((id5, order) => {
1449
+ const field = def.fields[id5];
1365
1450
  return {
1366
- id: id4,
1367
- label: field.label ?? humanize(id4),
1451
+ id: id5,
1452
+ label: field.label ?? humanize(id5),
1368
1453
  type: field.type,
1369
- required: formation.required.includes(id4),
1454
+ required: formation.required.includes(id5),
1370
1455
  order,
1371
- maxLength: formation.maxLen[id4] ?? formation.defaultMaxLen,
1456
+ maxLength: formation.maxLen[id5] ?? formation.defaultMaxLen,
1372
1457
  ...field.options ? { options: [...field.options] } : {},
1373
- ...id4 === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
1458
+ ...id5 === def.emailField || field.type === "email" ? { emailIdentity: "literal" } : {}
1374
1459
  };
1375
1460
  });
1376
1461
  }
@@ -1664,12 +1749,30 @@ function resolveNetwork(config, crm) {
1664
1749
  return { targets, readers };
1665
1750
  }
1666
1751
 
1752
+ // src/config-network-person.ts
1753
+ var SLUG2 = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
1754
+ function resolveNetworkPersonRegistry(mode, input, targetIds) {
1755
+ if (!input) return null;
1756
+ if (mode !== "hub") throw new Error("defineChapter.networkPersonRegistry: only hub mode may own network Person authority");
1757
+ if (input.mode !== "shadow") throw new Error('defineChapter.networkPersonRegistry.mode: must be "shadow"');
1758
+ const chapterIds = input.chapterIds ? [...input.chapterIds] : [...targetIds];
1759
+ if (chapterIds.length === 0) throw new Error("defineChapter.networkPersonRegistry.chapterIds: at least one chapter is required");
1760
+ if (new Set(chapterIds).size !== chapterIds.length || chapterIds.some((id5) => !SLUG2.test(id5))) {
1761
+ throw new Error("defineChapter.networkPersonRegistry.chapterIds: expected distinct lowercase slugs");
1762
+ }
1763
+ const unknown = chapterIds.filter((id5) => !targetIds.includes(id5));
1764
+ if (unknown.length) {
1765
+ throw new Error(`defineChapter.networkPersonRegistry.chapterIds: ${unknown.map((id5) => `"${id5}"`).join(", ")} are not configured network targets`);
1766
+ }
1767
+ return { mode: "shadow", chapterIds };
1768
+ }
1769
+
1667
1770
  // src/config-signup-control.ts
1668
- var SLUG2 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1771
+ var SLUG3 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1669
1772
  var SECRET_NAME = /^[a-z][a-z0-9_]{1,127}$/;
1670
1773
  function resolveSignupControl(input) {
1671
1774
  if (!input) return null;
1672
- if (!SLUG2.test(input.sourceId)) {
1775
+ if (!SLUG3.test(input.sourceId)) {
1673
1776
  throw new Error("defineChapter.signupControl.sourceId: must be a lowercase app slug");
1674
1777
  }
1675
1778
  if (input.stripeMode !== "test" && input.stripeMode !== "live") {
@@ -1683,12 +1786,12 @@ function resolveSignupControl(input) {
1683
1786
  }
1684
1787
 
1685
1788
  // src/config-member-content.ts
1686
- var SLUG3 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1789
+ var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1687
1790
  var SECRET_NAME2 = /^[a-z][a-z0-9_]{1,127}$/;
1688
1791
  function resolveMemberContent(input) {
1689
1792
  if (!input) return null;
1690
1793
  const parent = input.parent;
1691
- if (parent && !SLUG3.test(parent.sourceId)) {
1794
+ if (parent && !SLUG4.test(parent.sourceId)) {
1692
1795
  throw new Error("defineChapter.memberContent.parent.sourceId: must be a lowercase app slug");
1693
1796
  }
1694
1797
  const secretName = parent?.secretName?.trim() || "member_content_control_secret";
@@ -1703,7 +1806,7 @@ function resolveMemberContent(input) {
1703
1806
  }
1704
1807
 
1705
1808
  // src/config-builder-library.ts
1706
- var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1809
+ var SLUG5 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1707
1810
  function resolveBuilderLibrary(mode, input, network = { targets: [], readers: [] }) {
1708
1811
  if (!input) return null;
1709
1812
  if (input.sources !== void 0 && !Array.isArray(input.sources)) {
@@ -1711,7 +1814,7 @@ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: []
1711
1814
  }
1712
1815
  const seen = /* @__PURE__ */ new Set();
1713
1816
  const sources = (input.sources ?? []).map((source, index) => {
1714
- if (!source || typeof source !== "object" || !SLUG4.test(source.sourceId)) {
1817
+ if (!source || typeof source !== "object" || !SLUG5.test(source.sourceId)) {
1715
1818
  throw new Error(`defineChapter.builderLibrary.sources[${index}].sourceId: must be a lowercase app slug`);
1716
1819
  }
1717
1820
  if (source.sourceId === "built-not-found" && mode === "hub") {
@@ -1742,15 +1845,15 @@ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: []
1742
1845
  }
1743
1846
 
1744
1847
  // src/config.ts
1745
- var SLUG5 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1848
+ var SLUG6 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1746
1849
  function isResolvedCrm(x) {
1747
1850
  return !!x && typeof x === "object" && "prepare" in x && typeof x.prepare === "function";
1748
1851
  }
1749
1852
  function defineChapter(config) {
1750
1853
  if (!config || typeof config !== "object") throw new Error("defineChapter: a config object is required");
1751
- const { id: id4, name } = config;
1752
- if (typeof id4 !== "string" || !SLUG5.test(id4)) {
1753
- throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id4)}`);
1854
+ const { id: id5, name } = config;
1855
+ if (typeof id5 !== "string" || !SLUG6.test(id5)) {
1856
+ throw new Error(`defineChapter.id: must be a lowercase slug [a-z0-9-] (2-63 chars) \u2014 got ${JSON.stringify(id5)}`);
1754
1857
  }
1755
1858
  if (typeof name !== "string" || name.trim() === "") throw new Error("defineChapter.name: a non-empty name is required");
1756
1859
  const mode = config.mode ?? "chapter";
@@ -1773,6 +1876,11 @@ function defineChapter(config) {
1773
1876
  const brand = { ...config.brand, wordmark: config.brand?.wordmark ?? name };
1774
1877
  const copy = resolveChapterCopy(config.copy);
1775
1878
  const network = resolveNetwork(config, crm);
1879
+ const networkPersonRegistry = resolveNetworkPersonRegistry(
1880
+ mode,
1881
+ config.networkPersonRegistry,
1882
+ network.targets.map((target) => target.id)
1883
+ );
1776
1884
  const signupControl = resolveSignupControl(config.signupControl);
1777
1885
  const memberContent = resolveMemberContent(config.memberContent);
1778
1886
  const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary, network);
@@ -1791,7 +1899,8 @@ function defineChapter(config) {
1791
1899
  Boolean(signupControl),
1792
1900
  Boolean(memberContent),
1793
1901
  Boolean(builderLibrary),
1794
- network.targets.some((target) => target.admissionGrants)
1902
+ network.targets.some((target) => target.admissionGrants),
1903
+ Boolean(networkPersonRegistry)
1795
1904
  );
1796
1905
  const services = config.services ?? ["db", "calendar", "o11y"];
1797
1906
  const account = config.account ?? "none";
@@ -1841,7 +1950,7 @@ function defineChapter(config) {
1841
1950
  };
1842
1951
  const chapter = {
1843
1952
  config,
1844
- id: id4,
1953
+ id: id5,
1845
1954
  name,
1846
1955
  mode,
1847
1956
  brand,
@@ -1850,6 +1959,7 @@ function defineChapter(config) {
1850
1959
  signupControl,
1851
1960
  memberContent,
1852
1961
  builderLibrary,
1962
+ networkPersonRegistry,
1853
1963
  formation,
1854
1964
  crm,
1855
1965
  auth,
@@ -2161,7 +2271,8 @@ function createChapterIntegration(chapter, options = {}) {
2161
2271
  { path: "/api/builder-library-control", expectedStatus: 401 },
2162
2272
  { path: "/api/admin/builder-library", expectedStatus: 401 },
2163
2273
  { path: "/api/admin/builder-library/manifest", expectedStatus: 401 }
2164
- ] : []
2274
+ ] : [],
2275
+ ...chapter.networkPersonRegistry ? [{ path: "/api/admin/network-person-registry", expectedStatus: 401 }] : []
2165
2276
  ],
2166
2277
  runbooks
2167
2278
  };
@@ -2169,7 +2280,7 @@ function createChapterIntegration(chapter, options = {}) {
2169
2280
 
2170
2281
  // src/member-content-validation-helpers.ts
2171
2282
  var ID = /^[a-z][a-z0-9-]{0,79}$/;
2172
- var SLUG6 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2283
+ var SLUG7 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2173
2284
  var ROOT_KEYS = /* @__PURE__ */ new Set(["schemaVersion", "revision", "generatedAt", "source", "target", "pages", "upsellOverrides"]);
2174
2285
  var SOURCE_KEYS = /* @__PURE__ */ new Set(["type", "appId"]);
2175
2286
  var TARGET_KEYS = /* @__PURE__ */ new Set(["chapterId", "environment"]);
@@ -2275,14 +2386,14 @@ function validateMemberContentDocument(input, options = {}) {
2275
2386
  continue;
2276
2387
  }
2277
2388
  unknownKeys(raw, PAGE_KEYS, path, errors);
2278
- const id4 = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2389
+ const id5 = textValue(raw.id, `${path}.id`, errors, 80, true) ?? "";
2279
2390
  const slug = textValue(raw.slug, `${path}.slug`, errors, 120, true) ?? "";
2280
2391
  const title = textValue(raw.title, `${path}.title`, errors, MAX_TEXT, true) ?? "";
2281
- if (id4 && !ID.test(id4)) errors.push(`${path}.id must be a lowercase kebab-case id`);
2282
- if (slug && !SLUG6.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
2283
- if (ids.has(id4) || knownParentIds.has(id4)) errors.push(`${path}.id duplicates page "${id4}"`);
2392
+ if (id5 && !ID.test(id5)) errors.push(`${path}.id must be a lowercase kebab-case id`);
2393
+ if (slug && !SLUG7.test(slug)) errors.push(`${path}.slug must be a lowercase URL slug`);
2394
+ if (ids.has(id5) || knownParentIds.has(id5)) errors.push(`${path}.id duplicates page "${id5}"`);
2284
2395
  if (slugs.has(slug) || (options.parentPages ?? []).some((page) => page.slug === slug)) errors.push(`${path}.slug duplicates "${slug}"`);
2285
- ids.add(id4);
2396
+ ids.add(id5);
2286
2397
  slugs.add(slug);
2287
2398
  const navRaw = record(raw.navigation);
2288
2399
  let navigation = { section: "General", order: index };
@@ -2338,7 +2449,7 @@ function validateMemberContentDocument(input, options = {}) {
2338
2449
  else if (owner === "chapter") upsell = { owner, ...offer ? { offer } : {} };
2339
2450
  }
2340
2451
  }
2341
- pages.push({ id: id4, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2452
+ pages.push({ id: id5, slug, title, ...summary ? { summary } : {}, navigation, audience, ...teaserMarkdown ? { teaserMarkdown } : {}, bodyMarkdown, ...upsell ? { upsell } : {} });
2342
2453
  }
2343
2454
  const allIds = /* @__PURE__ */ new Set([...knownParentIds, ...ids]);
2344
2455
  for (const [index, page] of pages.entries()) {
@@ -2544,8 +2655,8 @@ function resourceValue(value, path, errors) {
2544
2655
  return null;
2545
2656
  }
2546
2657
  unknownKeys2(raw, RESOURCE_KEYS, path, errors);
2547
- const id4 = text(raw.id, `${path}.id`, errors, 80) ?? "";
2548
- if (id4 && !ID2.test(id4)) errors.push(`${path}.id must be lowercase kebab-case`);
2658
+ const id5 = text(raw.id, `${path}.id`, errors, 80) ?? "";
2659
+ if (id5 && !ID2.test(id5)) errors.push(`${path}.id must be lowercase kebab-case`);
2549
2660
  const kind = text(raw.kind, `${path}.kind`, errors) ?? "";
2550
2661
  if (!KINDS.has(kind)) errors.push(`${path}.kind is unsupported`);
2551
2662
  const title = text(raw.title, `${path}.title`, errors, 240) ?? "";
@@ -2602,7 +2713,7 @@ function resourceValue(value, path, errors) {
2602
2713
  updatedAt: integer(provenanceRaw?.updatedAt, `${path}.provenance.updatedAt`, errors, 0),
2603
2714
  ...promotedFrom ? { promotedFrom } : {}
2604
2715
  };
2605
- return { id: id4, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
2716
+ return { id: id5, kind, title, ...summary ? { summary } : {}, tags, scope, status, revision, ...bodyMarkdown ? { bodyMarkdown } : {}, ...url ? { url } : {}, ...brandAsset ? { brandAsset } : {}, provenance };
2606
2717
  }
2607
2718
  function validateBuilderLibraryDocument(value, options = {}) {
2608
2719
  const errors = [];
@@ -2633,7 +2744,7 @@ function validateBuilderLibraryDocument(value, options = {}) {
2633
2744
  const inputs = Array.isArray(raw.resources) ? raw.resources : [];
2634
2745
  if (inputs.length > MAX_RESOURCES) errors.push(`document.resources may contain at most ${MAX_RESOURCES} items`);
2635
2746
  const resources = inputs.map((item, index) => resourceValue(item, `document.resources[${index}]`, errors)).filter(Boolean);
2636
- const ids = resources.map(({ id: id4 }) => id4);
2747
+ const ids = resources.map(({ id: id5 }) => id5);
2637
2748
  if (new Set(ids).size !== ids.length) errors.push("document.resources ids must be unique");
2638
2749
  for (const resource of resources) {
2639
2750
  if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
@@ -2788,9 +2899,9 @@ async function sendTemplated(deps, input) {
2788
2899
  error = e instanceof Error ? e.message : String(e);
2789
2900
  }
2790
2901
  }
2791
- const id4 = deps.newId();
2902
+ const id5 = deps.newId();
2792
2903
  const row = {
2793
- id: id4,
2904
+ id: id5,
2794
2905
  groupId: input.group.id,
2795
2906
  to: decision.to,
2796
2907
  template: input.template,
@@ -2804,7 +2915,7 @@ async function sendTemplated(deps, input) {
2804
2915
  ...messageId ? { messageId } : {},
2805
2916
  ...error ? { error } : {}
2806
2917
  };
2807
- await deps.db.transact([{ t: "update", ns: "emailLog", id: id4, attrs: row }], error ? void 0 : { mutationId: `email:${input.dedupeKey}` });
2918
+ await deps.db.transact([{ t: "update", ns: "emailLog", id: id5, attrs: row }], error ? void 0 : { mutationId: `email:${input.dedupeKey}` });
2808
2919
  return error ? { sent: false, reason: error } : { sent: true };
2809
2920
  }
2810
2921
  function emailGroupFrom(row) {
@@ -2992,14 +3103,14 @@ function validateAdmissionGrantDocument(input) {
2992
3103
  const seenEmails = /* @__PURE__ */ new Set();
2993
3104
  for (const [index, raw] of rawGrants.slice(0, MAX_GRANTS).entries()) {
2994
3105
  const grant = raw && typeof raw === "object" ? raw : {};
2995
- const id4 = typeof grant.id === "string" ? grant.id : "";
3106
+ const id5 = typeof grant.id === "string" ? grant.id : "";
2996
3107
  const email = typeof grant.email === "string" ? grant.email.trim() : "";
2997
3108
  const normalized = normalizeAdmissionEmail(email);
2998
3109
  const decision = grant.decision;
2999
3110
  const note = typeof grant.note === "string" ? grant.note.trim() : void 0;
3000
3111
  const expiresAt = grant.expiresAt === void 0 ? void 0 : Number(grant.expiresAt);
3001
- if (!ID3.test(id4)) errors.push(`document.grants[${index}].id must be an identifier`);
3002
- if (seenIds.has(id4)) errors.push(`document.grants repeats id "${id4}"`);
3112
+ if (!ID3.test(id5)) errors.push(`document.grants[${index}].id must be an identifier`);
3113
+ if (seenIds.has(id5)) errors.push(`document.grants repeats id "${id5}"`);
3003
3114
  if (!isAdmissionEmail(normalized)) errors.push(`document.grants[${index}].email must be valid`);
3004
3115
  if (seenEmails.has(normalized)) errors.push(`document.grants repeats email "${normalized}"`);
3005
3116
  if (decision !== "waive_interview" && decision !== "require_interview") {
@@ -3009,10 +3120,10 @@ function validateAdmissionGrantDocument(input) {
3009
3120
  if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= 0)) {
3010
3121
  errors.push(`document.grants[${index}].expiresAt must be epoch milliseconds`);
3011
3122
  }
3012
- seenIds.add(id4);
3123
+ seenIds.add(id5);
3013
3124
  seenEmails.add(normalized);
3014
3125
  grants.push({
3015
- id: id4,
3126
+ id: id5,
3016
3127
  email,
3017
3128
  decision,
3018
3129
  ...note ? { note } : {},
@@ -3083,8 +3194,8 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3083
3194
  const incomingIds = new Set(document.grants.map((grant) => `admission:parent:${document.source.appId}:${grant.id}`));
3084
3195
  const ops = [];
3085
3196
  for (const row of current) {
3086
- const id4 = String(row.id ?? "");
3087
- if (id4 && !incomingIds.has(id4)) ops.push({ t: "update", ns: "admissionGrants", id: id4, attrs: {
3197
+ const id5 = String(row.id ?? "");
3198
+ if (id5 && !incomingIds.has(id5)) ops.push({ t: "update", ns: "admissionGrants", id: id5, attrs: {
3088
3199
  active: false,
3089
3200
  revokedAt: receivedAt,
3090
3201
  revokedBy: document.source.actor,
@@ -3093,10 +3204,10 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3093
3204
  } });
3094
3205
  }
3095
3206
  for (const grant of document.grants) {
3096
- const id4 = `admission:parent:${document.source.appId}:${grant.id}`;
3097
- const existing = current.find((row) => row.id === id4);
3098
- ops.push({ t: "update", ns: "admissionGrants", id: id4, attrs: {
3099
- id: id4,
3207
+ const id5 = `admission:parent:${document.source.appId}:${grant.id}`;
3208
+ const existing = current.find((row) => row.id === id5);
3209
+ ops.push({ t: "update", ns: "admissionGrants", id: id5, attrs: {
3210
+ id: id5,
3100
3211
  sourceGrantId: grant.id,
3101
3212
  sourceScope: "parent",
3102
3213
  sourceId: document.source.appId,
@@ -3117,7 +3228,7 @@ async function materializeAdmissionGrants(db, envelope, previous, receivedAt) {
3117
3228
  ...!grant.note && existing?.note !== void 0 ? ["note"] : [],
3118
3229
  ...grant.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
3119
3230
  ];
3120
- if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id4, attrs: retract });
3231
+ if (retract.length) ops.push({ t: "retract", ns: "admissionGrants", id: id5, attrs: retract });
3121
3232
  }
3122
3233
  if (previous?.active) ops.push({ t: "update", ns: "admissionGrantRevisions", id: previous.id, attrs: { active: false } });
3123
3234
  const revisionId = `admission-revision:${document.source.appId}:${document.target.environment}:${document.revision}`;
@@ -3187,10 +3298,10 @@ function validateGiftOfferCatalog(input, tierIds) {
3187
3298
  }
3188
3299
  const allowed2 = /* @__PURE__ */ new Set(["id", "target", "billingMode", "stripePriceId", "priceCents", "currency", "redemptionLifetimeHours", "activeFrom", "activeUntil", "title", "description", "ctaLabel"]);
3189
3300
  for (const key of Object.keys(item)) if (!allowed2.has(key)) errors.push(`${path}.${key} is not supported`);
3190
- const id4 = text2(item.id, `${path}.id`, errors, 80);
3191
- if (id4 && !ID4.test(id4)) errors.push(`${path}.id must be a lowercase kebab-case id`);
3192
- if (ids.has(id4)) errors.push(`${path}.id duplicates "${id4}"`);
3193
- ids.add(id4);
3301
+ const id5 = text2(item.id, `${path}.id`, errors, 80);
3302
+ if (id5 && !ID4.test(id5)) errors.push(`${path}.id must be a lowercase kebab-case id`);
3303
+ if (ids.has(id5)) errors.push(`${path}.id duplicates "${id5}"`);
3304
+ ids.add(id5);
3194
3305
  const targetRaw = record3(item.target);
3195
3306
  let target = { type: "entitlement", entitlementKey: "invalid" };
3196
3307
  if (targetRaw?.type === "tier") {
@@ -3216,7 +3327,7 @@ function validateGiftOfferCatalog(input, tierIds) {
3216
3327
  if (activeUntil !== void 0 && (!Number.isSafeInteger(activeUntil) || activeUntil < 1)) errors.push(`${path}.activeUntil must be epoch milliseconds`);
3217
3328
  if (activeFrom !== void 0 && activeUntil !== void 0 && activeUntil <= activeFrom) errors.push(`${path}.activeUntil must follow activeFrom`);
3218
3329
  offers.push({
3219
- id: id4,
3330
+ id: id5,
3220
3331
  target,
3221
3332
  billingMode: item.billingMode,
3222
3333
  stripePriceId,
@@ -3634,9 +3745,9 @@ async function updateClerkUserMetadataByEmail(secretKey, email, input, fetchImpl
3634
3745
  const found = await fetchImpl(`https://api.clerk.com/v1/users?email_address=${encodeURIComponent(email)}&limit=1`, { headers: auth });
3635
3746
  if (!found.ok) return false;
3636
3747
  const users = await found.json().catch(() => null);
3637
- const id4 = Array.isArray(users) && typeof users[0]?.id === "string" ? users[0].id : void 0;
3638
- if (!id4) return false;
3639
- return updateClerkUserMetadata(secretKey, id4, input, fetchImpl);
3748
+ const id5 = Array.isArray(users) && typeof users[0]?.id === "string" ? users[0].id : void 0;
3749
+ if (!id5) return false;
3750
+ return updateClerkUserMetadata(secretKey, id5, input, fetchImpl);
3640
3751
  }
3641
3752
  async function createClerkUser(secretKey, input, fetchImpl = fetch) {
3642
3753
  const { path, body } = clerkUserRequest(input);
@@ -3699,8 +3810,8 @@ async function clerkGetUserByEmail(secretKey, email, fetchImpl = fetch) {
3699
3810
  const user = Array.isArray(data) ? data[0] : void 0;
3700
3811
  return user ? toRecord(user) : null;
3701
3812
  }
3702
- async function clerkGetUser(secretKey, id4, fetchImpl = fetch) {
3703
- const data = await clerkGet(`/v1/users/${encodeURIComponent(id4)}`, secretKey, fetchImpl).catch(() => null);
3813
+ async function clerkGetUser(secretKey, id5, fetchImpl = fetch) {
3814
+ const data = await clerkGet(`/v1/users/${encodeURIComponent(id5)}`, secretKey, fetchImpl).catch(() => null);
3704
3815
  return data ? toRecord(data) : null;
3705
3816
  }
3706
3817
  async function clerkListUsers(secretKey, fetchImpl = fetch) {
@@ -3716,8 +3827,8 @@ async function clerkListUsers(secretKey, fetchImpl = fetch) {
3716
3827
  }
3717
3828
  return out;
3718
3829
  }
3719
- async function clerkSetRole(secretKey, id4, role, fetchImpl = fetch) {
3720
- const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id4)}/metadata`, {
3830
+ async function clerkSetRole(secretKey, id5, role, fetchImpl = fetch) {
3831
+ const res = await fetchImpl(`${CLERK_API}/v1/users/${encodeURIComponent(id5)}/metadata`, {
3721
3832
  method: "PATCH",
3722
3833
  headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
3723
3834
  body: JSON.stringify({ public_metadata: { role } })
@@ -4283,7 +4394,7 @@ function membershipPageDocument(input, options = {}) {
4283
4394
  }
4284
4395
 
4285
4396
  // src/signup-control-validate.ts
4286
- var SLUG7 = /^[a-z0-9][a-z0-9-]{1,62}$/;
4397
+ var SLUG8 = /^[a-z0-9][a-z0-9-]{1,62}$/;
4287
4398
  var ENVIRONMENT = /^[a-z][a-z0-9_-]{0,62}$/;
4288
4399
  var TIER = /^[a-z][a-z0-9-]{0,79}$/;
4289
4400
  var PRICE2 = /^price_[A-Za-z0-9]{12,}$/;
@@ -4329,10 +4440,10 @@ function tiersOf(value, errors) {
4329
4440
  return [];
4330
4441
  }
4331
4442
  unknownKeys3(item, TIER_KEYS2, path, errors);
4332
- const id4 = text3(item.id, `${path}.id`, errors, 80);
4333
- if (id4 && !TIER.test(id4)) errors.push(`${path}.id must be a lowercase kebab-case id`);
4334
- if (seen.has(id4)) errors.push(`${path}.id duplicates "${id4}"`);
4335
- seen.add(id4);
4443
+ const id5 = text3(item.id, `${path}.id`, errors, 80);
4444
+ if (id5 && !TIER.test(id5)) errors.push(`${path}.id must be a lowercase kebab-case id`);
4445
+ if (seen.has(id5)) errors.push(`${path}.id duplicates "${id5}"`);
4446
+ seen.add(id5);
4336
4447
  const name = text3(item.name, `${path}.name`, errors, 160);
4337
4448
  if (typeof item.available !== "boolean") errors.push(`${path}.available must be boolean`);
4338
4449
  if (!Number.isInteger(item.sortOrder) || Number(item.sortOrder) < 0) errors.push(`${path}.sortOrder must be a non-negative integer`);
@@ -4347,7 +4458,7 @@ function tiersOf(value, errors) {
4347
4458
  }
4348
4459
  const blurb = item.blurb === void 0 ? void 0 : text3(item.blurb, `${path}.blurb`, errors, 4e3);
4349
4460
  return [{
4350
- id: id4,
4461
+ id: id5,
4351
4462
  name,
4352
4463
  available: item.available === true,
4353
4464
  sortOrder: Number(item.sortOrder) || 0,
@@ -4442,8 +4553,8 @@ function validateSignupControlDocument(input) {
4442
4553
  const appId = text3(source?.appId, "document.source.appId", errors, 63);
4443
4554
  const chapterId = text3(target?.chapterId, "document.target.chapterId", errors, 63);
4444
4555
  const environment = text3(target?.environment, "document.target.environment", errors, 63);
4445
- if (appId && !SLUG7.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
4446
- if (chapterId && !SLUG7.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
4556
+ if (appId && !SLUG8.test(appId)) errors.push("document.source.appId must be a lowercase app slug");
4557
+ if (chapterId && !SLUG8.test(chapterId)) errors.push("document.target.chapterId must be a lowercase app slug");
4447
4558
  if (environment && !ENVIRONMENT.test(environment)) errors.push("document.target.environment is invalid");
4448
4559
  if (target?.stripeMode !== "test" && target?.stripeMode !== "live") errors.push("document.target.stripeMode must be test or live");
4449
4560
  const tiers2 = tiersOf(root.tiers, errors);
@@ -4722,7 +4833,7 @@ var exclusions = (chapterId, paidTierIds) => [
4722
4833
  id: chapterId,
4723
4834
  reason: "provider_identity",
4724
4835
  paths: [
4725
- ...paidTierIds.map((id4) => `tiers[${id4}].stripePriceId`),
4836
+ ...paidTierIds.map((id5) => `tiers[${id5}].stripePriceId`),
4726
4837
  "groups.stripePublishableKey",
4727
4838
  "applications.stripeCustomerId",
4728
4839
  "applications.stripeSubscriptionId",
@@ -4764,9 +4875,9 @@ async function exportChapterPromotion(input) {
4764
4875
  sourceIds: [`signup-control:${document.revision}`, document.source.appId, document.target.chapterId].sort(),
4765
4876
  receiptDigests: [input.digest]
4766
4877
  };
4767
- const artifact = (kind, id4, content) => ({
4878
+ const artifact = (kind, id5, content) => ({
4768
4879
  kind,
4769
- id: id4,
4880
+ id: id5,
4770
4881
  sourceRevision: revision,
4771
4882
  provenance: promotionProvenance,
4772
4883
  content: canonicalPromotionValue(content)
@@ -4805,7 +4916,7 @@ async function exportChapterPromotion(input) {
4805
4916
  }
4806
4917
 
4807
4918
  // src/member-system-authority.ts
4808
- var rows2 = (group) => group.fields.map((id4) => ({ ...group, id: id4 }));
4919
+ var rows2 = (group) => group.fields.map((id5) => ({ ...group, id: id5 }));
4809
4920
  var MEMBER_SYSTEM_AUTHORITY_MATRIX = [
4810
4921
  ...rows2({
4811
4922
  scope: "network",
@@ -5394,6 +5505,7 @@ export {
5394
5505
  resolveJoinContinuation,
5395
5506
  resolveLeaderFormation,
5396
5507
  resolveMemberContent,
5508
+ resolveNetworkPersonRegistry,
5397
5509
  resolvePipeline,
5398
5510
  resolveScheduling,
5399
5511
  resolveSignupControl,