@odla-ai/chapter 0.42.1 → 0.42.3

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  formatChapterCopy,
3
3
  useChapterCopy
4
- } from "./chunk-JXFYOUDL.js";
4
+ } from "./chunk-4HS2JPE2.js";
5
5
  import {
6
6
  collectFormFields
7
7
  } from "./chunk-IVXVECKR.js";
@@ -1524,4 +1524,4 @@ export {
1524
1524
  membershipApplicationHref,
1525
1525
  MembershipCards
1526
1526
  };
1527
- //# sourceMappingURL=chunk-S7CND3R5.js.map
1527
+ //# sourceMappingURL=chunk-VWDEHUNI.js.map
@@ -24,7 +24,7 @@ type TextFields$1<Key extends string> = {
24
24
  [Field in Key]: string;
25
25
  };
26
26
  /** User-visible language for sharing, delivery state, and leader rollups. */
27
- type AdminNetworkCopy = TextFields$1<"title" | "allowlistDescription" | "sharing" | "shareWith" | "shared" | "shareFailed" | "deliveryFailed" | "loadingRollup" | "loadRollupFailed" | "overview" | "configuredFollowers" | "reachableFollowers" | "recordsAcrossFollowers" | "noFollowers" | "followers" | "available" | "unavailable" | "recordTotals" | "noRecordTotals">;
27
+ type AdminNetworkCopy = TextFields$1<"title" | "allowlistDescription" | "sharing" | "shareWith" | "shared" | "shareFailed" | "deliveryFailed" | "loadingRollup" | "loadRollupFailed" | "overview" | "configuredFollowers" | "reachableFollowers" | "recordsAcrossFollowers" | "noFollowers" | "followers" | "available" | "unavailable" | "transportReachable" | "transportUnavailable" | "transportNotChecked" | "bindingTransport" | "urlTransport" | "activeConfiguration" | "noActiveConfiguration" | "publicSiteAvailable" | "publicSiteUnavailable" | "publicSiteNotChecked" | "recordTotals" | "noRecordTotals">;
28
28
 
29
29
  type TextFields<Key extends string> = {
30
30
  [Field in Key]: string;
@@ -193,6 +193,9 @@ interface ChapterNetworkTarget {
193
193
  /** Record types on which this leader permits remote lifecycle transitions.
194
194
  * The follower must independently opt in and remains pipeline authority. */
195
195
  stageTransitions?: readonly string[];
196
+ /** Allow package-owned desired admission decisions for existing leader CRM
197
+ * people to be delivered to this target. Omitted means default deny. */
198
+ admissionGrants?: boolean;
196
199
  }
197
200
  /** One signed peer allowed to browse a privacy-bounded projection of this
198
201
  * site's CRM. The follower owns this allowlist; a leader target declaration
@@ -202,7 +205,7 @@ interface ChapterNetworkReader {
202
205
  id: string;
203
206
  /** Per-record-type field allowlist. Omitted types and fields never leave
204
207
  * this site. Record id, name, stage, and timestamps are bounded metadata. */
205
- fields: Record<string, readonly string[]>;
208
+ fields?: Record<string, readonly string[]>;
206
209
  /** Record types on which this reader may append an explicitly shared note.
207
210
  * Omitted by default: browse permission never silently grants write access.
208
211
  * Shared notes land in the follower CRM activity feed. */
@@ -214,6 +217,9 @@ interface ChapterNetworkReader {
214
217
  /** Local CRM record types on which this reader may request a lifecycle
215
218
  * transition. Chapter still enforces the local pipeline and gates. */
216
219
  stageTransitions?: readonly string[];
220
+ /** Accept signed desired admission decisions from this reader. Independent
221
+ * of record browsing; an admission-only reader may omit `fields`. */
222
+ admissionGrants?: boolean;
217
223
  }
218
224
  /** Leader/follower network configuration. A follower needs no code config to
219
225
  * receive pushed records. It must explicitly declare `readers` before any peer
@@ -258,6 +264,7 @@ interface ResolvedNetworkTarget {
258
264
  sharedNotes: readonly string[];
259
265
  editableFields: Record<string, readonly string[]>;
260
266
  stageTransitions: readonly string[];
267
+ admissionGrants?: boolean;
261
268
  }
262
269
  /** A validated follower-owned record projection for one signed reader. */
263
270
  interface ResolvedNetworkReader {
@@ -266,6 +273,7 @@ interface ResolvedNetworkReader {
266
273
  sharedNotes: readonly string[];
267
274
  editableFields: Record<string, readonly string[]>;
268
275
  stageTransitions: readonly string[];
276
+ admissionGrants?: boolean;
269
277
  }
270
278
  /** Fully-resolved leader network configuration. */
271
279
  interface ResolvedNetwork {
package/dist/index.cjs CHANGED
@@ -486,6 +486,50 @@ var admissionGrantRevisionsEntity = {
486
486
  receivedAt: attr2("number", { indexed: true })
487
487
  }
488
488
  };
489
+ var leaderAdmissionAssignmentsEntity = {
490
+ attrs: {
491
+ id: id(),
492
+ targetId: attr2("string", { indexed: true }),
493
+ personId: attr2("string", { indexed: true }),
494
+ grantId: attr2("string", { indexed: true }),
495
+ decision: attr2("string", { indexed: true }),
496
+ note: attr2("string", { optional: true }),
497
+ expiresAt: attr2("number", { optional: true, indexed: true }),
498
+ active: attr2("boolean", { indexed: true }),
499
+ lastMutationId: attr2("string", { indexed: true }),
500
+ lastRequestDigest: attr2("string"),
501
+ createdAt: attr2("number", { indexed: true }),
502
+ createdBy: attr2("string", { indexed: true }),
503
+ updatedAt: attr2("number", { indexed: true }),
504
+ updatedBy: attr2("string", { indexed: true })
505
+ }
506
+ };
507
+ var leaderAdmissionDeliveriesEntity = {
508
+ attrs: {
509
+ id: id(),
510
+ targetId: attr2("string", { indexed: true }),
511
+ environment: attr2("string", { indexed: true }),
512
+ revision: attr2("number", { indexed: true }),
513
+ digest: attr2("string", { indexed: true }),
514
+ envelope: attr2("json"),
515
+ status: attr2("string", { indexed: true }),
516
+ attempts: attr2("number", { indexed: true }),
517
+ lastAttemptAt: attr2("number", { optional: true, indexed: true }),
518
+ lastError: attr2("string", { optional: true }),
519
+ acknowledgedAt: attr2("number", { optional: true, indexed: true }),
520
+ updatedAt: attr2("number", { indexed: true })
521
+ }
522
+ };
523
+ var leaderAdmissionMutationsEntity = {
524
+ attrs: {
525
+ id: id(),
526
+ targetId: attr2("string", { indexed: true }),
527
+ personId: attr2("string", { indexed: true }),
528
+ requestDigest: attr2("string", { indexed: true }),
529
+ revision: attr2("number", { indexed: true }),
530
+ createdAt: attr2("number", { indexed: true })
531
+ }
532
+ };
489
533
 
490
534
  // src/schema-gifts.ts
491
535
  var attr3 = (type, flags = {}) => ({
@@ -757,7 +801,7 @@ var emailLog = {
757
801
  sentAt: attr4("number", { indexed: true })
758
802
  }
759
803
  };
760
- function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false) {
804
+ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolveApplication(void 0), includeSignupControl = false, includeMemberContent = false, includeBuilderLibrary = false, includeLeaderAdmissions = false) {
761
805
  const entities = {};
762
806
  if (mode === "chapter") {
763
807
  entities.applications = applicationsFor(application);
@@ -785,6 +829,11 @@ function chapterDb(mode, auth, includeNetworkNotes = false, application = resolv
785
829
  entities.builderLibraryControls = builderLibraryControls;
786
830
  entities.builderLibraryLocal = builderLibraryLocal;
787
831
  }
832
+ if (includeLeaderAdmissions) {
833
+ entities.leaderAdmissionAssignments = leaderAdmissionAssignmentsEntity;
834
+ entities.leaderAdmissionDeliveries = leaderAdmissionDeliveriesEntity;
835
+ entities.leaderAdmissionMutations = leaderAdmissionMutationsEntity;
836
+ }
788
837
  const schema = { entities, links: {} };
789
838
  const rules = {};
790
839
  for (const ns of Object.keys(entities)) {
@@ -1214,6 +1263,16 @@ var DEFAULT_ADMIN_COPY = {
1214
1263
  followers: "Followers",
1215
1264
  available: "available",
1216
1265
  unavailable: "unavailable",
1266
+ transportReachable: "transport reachable",
1267
+ transportUnavailable: "transport unavailable",
1268
+ transportNotChecked: "transport not checked",
1269
+ bindingTransport: "service binding {binding}",
1270
+ urlTransport: "public URL transport",
1271
+ activeConfiguration: "active configuration r{revision}",
1272
+ noActiveConfiguration: "no active managed configuration",
1273
+ publicSiteAvailable: "public site reachable",
1274
+ publicSiteUnavailable: "public site unavailable",
1275
+ publicSiteNotChecked: "public site not probed",
1217
1276
  recordTotals: "Portfolio records",
1218
1277
  noRecordTotals: "No aggregate records are available yet."
1219
1278
  },
@@ -1591,6 +1650,13 @@ function validateStageTransitions(opts) {
1591
1650
  throw new Error(`${opts.path}: must contain unique, readable CRM types with pipelines`);
1592
1651
  }
1593
1652
  }
1653
+ function validateAdmissionCapability(path, enabled, crm) {
1654
+ if (!enabled) return;
1655
+ const person = crm.config.types.person;
1656
+ if (!person?.emailField || person.fields[person.emailField]?.type !== "email") {
1657
+ throw new Error(`${path}: requires a CRM person type with an emailField`);
1658
+ }
1659
+ }
1594
1660
  function resolveNetwork(config, crm) {
1595
1661
  const seen = /* @__PURE__ */ new Set();
1596
1662
  const targets = [];
@@ -1664,6 +1730,11 @@ function resolveNetwork(config, crm) {
1664
1730
  crm,
1665
1731
  readable: readableFields
1666
1732
  });
1733
+ validateAdmissionCapability(
1734
+ `defineChapter.network.targets.${target.id}.admissionGrants`,
1735
+ target.admissionGrants,
1736
+ crm
1737
+ );
1667
1738
  targets.push({
1668
1739
  id: target.id,
1669
1740
  name: target.name?.trim() || target.id,
@@ -1673,7 +1744,8 @@ function resolveNetwork(config, crm) {
1673
1744
  ...fields ? { fields } : {},
1674
1745
  sharedNotes,
1675
1746
  editableFields,
1676
- stageTransitions
1747
+ stageTransitions,
1748
+ ...target.admissionGrants === true ? { admissionGrants: true } : {}
1677
1749
  });
1678
1750
  }
1679
1751
  const readerIds = /* @__PURE__ */ new Set();
@@ -1686,9 +1758,12 @@ function resolveNetwork(config, crm) {
1686
1758
  throw new Error(`defineChapter.network.readers: duplicate reader "${reader.id}"`);
1687
1759
  }
1688
1760
  readerIds.add(reader.id);
1689
- const entries = Object.entries(reader.fields ?? {});
1690
- if (entries.length === 0) {
1691
- throw new Error(`defineChapter.network.readers.${reader.id}.fields: at least one CRM type is required`);
1761
+ const readerFields = reader.fields ?? {};
1762
+ const entries = Object.entries(readerFields);
1763
+ if (entries.length === 0 && !reader.admissionGrants) {
1764
+ throw new Error(
1765
+ `defineChapter.network.readers.${reader.id}.fields: at least one CRM type is required unless this is an admission-only reader`
1766
+ );
1692
1767
  }
1693
1768
  for (const [type, names] of entries) {
1694
1769
  if (!FIELD.test(type) || names.length === 0 || names.some((name) => !FIELD.test(name))) {
@@ -1718,23 +1793,35 @@ function resolveNetwork(config, crm) {
1718
1793
  path: `defineChapter.network.readers.${reader.id}.sharedNotes`,
1719
1794
  values: sharedNotes,
1720
1795
  crm,
1721
- fields: reader.fields
1796
+ fields: readerFields
1722
1797
  });
1723
1798
  const editableFields = reader.editableFields ?? {};
1724
1799
  validateEditableFields({
1725
1800
  path: `defineChapter.network.readers.${reader.id}.editableFields`,
1726
1801
  values: editableFields,
1727
1802
  crm,
1728
- readable: reader.fields
1803
+ readable: readerFields
1729
1804
  });
1730
1805
  const stageTransitions = reader.stageTransitions ?? [];
1731
1806
  validateStageTransitions({
1732
1807
  path: `defineChapter.network.readers.${reader.id}.stageTransitions`,
1733
1808
  values: stageTransitions,
1734
1809
  crm,
1735
- readable: reader.fields
1810
+ readable: readerFields
1811
+ });
1812
+ validateAdmissionCapability(
1813
+ `defineChapter.network.readers.${reader.id}.admissionGrants`,
1814
+ reader.admissionGrants,
1815
+ crm
1816
+ );
1817
+ readers.push({
1818
+ id: reader.id,
1819
+ fields: readerFields,
1820
+ sharedNotes,
1821
+ editableFields,
1822
+ stageTransitions,
1823
+ ...reader.admissionGrants === true ? { admissionGrants: true } : {}
1736
1824
  });
1737
- readers.push({ id: reader.id, fields: reader.fields, sharedNotes, editableFields, stageTransitions });
1738
1825
  }
1739
1826
  return { targets, readers };
1740
1827
  }
@@ -1865,7 +1952,8 @@ function defineChapter(config) {
1865
1952
  application,
1866
1953
  Boolean(signupControl),
1867
1954
  Boolean(memberContent),
1868
- Boolean(builderLibrary)
1955
+ Boolean(builderLibrary),
1956
+ network.targets.some((target) => target.admissionGrants)
1869
1957
  );
1870
1958
  const services = config.services ?? ["db", "calendar", "o11y"];
1871
1959
  const account = config.account ?? "none";