@odla-ai/chapter 0.31.4 → 0.32.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.d.cts CHANGED
@@ -1852,4 +1852,73 @@ declare function loadTiers(db: ChapterDb, group: TierGroupRow): Promise<ChapterT
1852
1852
  /** The tiers a join page may show: active, and joinable as configured. */
1853
1853
  declare function loadOfferableTiers(db: ChapterDb, group: TierGroupRow, hasSecretKey: boolean): Promise<ChapterTier[]>;
1854
1854
 
1855
- export { type AccountModel, type AdminNotificationTrigger, type Applicant, type ApplicationBookingPatch, type ApplicationRecord, type ApplicationSummary, type Attr, type AttrType, type Chapter, type ChapterApplication, type ChapterAuth, type ChapterBrand, type ChapterConfig, type ChapterCopy, type ChapterCopyInput, type ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkReader, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterRunbookHints, type ChapterScheduling, type ChapterSends, type ChapterTier, type ChapterTierConfig, type ClerkInviteInput, type ClerkMetadataInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, type CompiledChapterBrandTokens, DEFAULT_CHAPTER_COPY, DEFAULT_SHARE_FIELDS, type DashboardMetricData, type DashboardMetricSeries, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type JoinConfigTier, type LeaderCrmOptions, type LeaderFormationConfig, type LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NetworkRecord, type NetworkRecordNote, type NetworkRecordPage, type NetworkRollup, type NetworkRollupTarget, type NetworkSharedNote, type NetworkSnapshot, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedLeaderFormation, type ResolvedNetwork, type ResolvedNetworkReader, type ResolvedNetworkTarget, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedBusiness, type SharedPerson, type SharedRecord, type SharedRecordV1, type SharedRecordV2, type StripeEvent, type StripeResult, type SubmitResult, type TierGroup, type TierGroupRow, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, buildTierSeeds, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterBrandFromTokens, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, dashboardMetricData, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, findTier, firstPaymentPatch, formatChapterCopy, formationFields, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, joinConfigTiers, leaderCrmConfig, loadOfferableTiers, loadTiers, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, offerableTiers, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolveChapterCopy, resolveLeaderFormation, resolvePipeline, resolveScheduling, resolveTiers, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, tierIsFree, tierPayable, updateClerkUserMetadata, updateClerkUserMetadataByEmail, validateScheduling, webhookMutationId };
1855
+ /** The first stable, shared Chapter membership-page renderer contract. */
1856
+ declare const MEMBERSHIP_PAGE_RENDERER: "membership-cards-v1";
1857
+ /** Fixed visual emphasis choices supported by the versioned card renderer. */
1858
+ type MembershipTierVariant = "standard" | "featured" | "supporting";
1859
+ /** A bounded, token-rendered aside within one membership card. */
1860
+ interface MembershipTierCallout {
1861
+ label?: string;
1862
+ bodyMarkdown: string;
1863
+ }
1864
+ /** Scarcity copy displayed without changing tier availability or checkout. */
1865
+ interface MembershipTierCapacity {
1866
+ total: number;
1867
+ label?: string;
1868
+ }
1869
+ /**
1870
+ * Presentation attached to one server-authoritative membership tier.
1871
+ *
1872
+ * Price, free/paid state, identity, and application targeting deliberately do
1873
+ * not live here. They are resolved from the public Chapter join config so
1874
+ * authored marketing copy can never change what checkout charges.
1875
+ */
1876
+ interface MembershipTierPresentation {
1877
+ tierId: string;
1878
+ variant?: MembershipTierVariant;
1879
+ badge?: string;
1880
+ summaryMarkdown?: string;
1881
+ priceEyebrow?: string;
1882
+ compareAtPriceCents?: number;
1883
+ billingLabel?: string;
1884
+ priceNoteMarkdown?: string;
1885
+ capacity?: MembershipTierCapacity;
1886
+ benefits?: readonly string[];
1887
+ callout?: MembershipTierCallout;
1888
+ ctaLabel?: string;
1889
+ }
1890
+ /** A bounded document that can be reviewed once and rendered by any Chapter host. */
1891
+ interface MembershipPageDocument {
1892
+ schemaVersion: 1;
1893
+ renderer: typeof MEMBERSHIP_PAGE_RENDERER;
1894
+ tiers: readonly MembershipTierPresentation[];
1895
+ footnoteMarkdown?: string;
1896
+ }
1897
+ /** Minimal server-authoritative tier identity and charged amount used during validation. */
1898
+ interface MembershipTierAuthority {
1899
+ id: string;
1900
+ priceCents: number;
1901
+ }
1902
+ /** Live tier authority used to reject missing, unknown, or misleading presentation. */
1903
+ interface MembershipPageValidationOptions {
1904
+ /** When supplied, every live tier must occur exactly once in the document. */
1905
+ tiers?: readonly MembershipTierAuthority[];
1906
+ }
1907
+ /** A normalized document or the complete actionable list of validation failures. */
1908
+ type MembershipPageValidationResult = {
1909
+ ok: true;
1910
+ value: MembershipPageDocument;
1911
+ } | {
1912
+ ok: false;
1913
+ errors: readonly string[];
1914
+ };
1915
+ /**
1916
+ * Validate and normalize a membership-page document without evaluating code,
1917
+ * accepting HTML, or allowing authored links/styles. Markdown slots are later
1918
+ * rendered by @odla-ai/ui's bounded safe Markdown component.
1919
+ */
1920
+ declare function validateMembershipPageDocument(input: unknown, options?: MembershipPageValidationOptions): MembershipPageValidationResult;
1921
+ /** Validate a document or fail closed with all actionable validation errors. */
1922
+ declare function membershipPageDocument(input: unknown, options?: MembershipPageValidationOptions): MembershipPageDocument;
1923
+
1924
+ export { type AccountModel, type AdminNotificationTrigger, type Applicant, type ApplicationBookingPatch, type ApplicationRecord, type ApplicationSummary, type Attr, type AttrType, type Chapter, type ChapterApplication, type ChapterAuth, type ChapterBrand, type ChapterConfig, type ChapterCopy, type ChapterCopyInput, type ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkReader, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterRunbookHints, type ChapterScheduling, type ChapterSends, type ChapterTier, type ChapterTierConfig, type ClerkInviteInput, type ClerkMetadataInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, type CompiledChapterBrandTokens, DEFAULT_CHAPTER_COPY, DEFAULT_SHARE_FIELDS, type DashboardMetricData, type DashboardMetricSeries, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type JoinConfigTier, type LeaderCrmOptions, type LeaderFormationConfig, type LiveEvent, MEMBERSHIP_PAGE_RENDERER, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type MembershipPageDocument, type MembershipPageValidationOptions, type MembershipPageValidationResult, type MembershipTierAuthority, type MembershipTierCallout, type MembershipTierCapacity, type MembershipTierPresentation, type MembershipTierVariant, type NetworkRecord, type NetworkRecordNote, type NetworkRecordPage, type NetworkRollup, type NetworkRollupTarget, type NetworkSharedNote, type NetworkSnapshot, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedLeaderFormation, type ResolvedNetwork, type ResolvedNetworkReader, type ResolvedNetworkTarget, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedBusiness, type SharedPerson, type SharedRecord, type SharedRecordV1, type SharedRecordV2, type StripeEvent, type StripeResult, type SubmitResult, type TierGroup, type TierGroupRow, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, buildTierSeeds, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterBrandFromTokens, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, dashboardMetricData, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, findTier, firstPaymentPatch, formatChapterCopy, formationFields, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, joinConfigTiers, leaderCrmConfig, loadOfferableTiers, loadTiers, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, membershipPageDocument, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, offerableTiers, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolveChapterCopy, resolveLeaderFormation, resolvePipeline, resolveScheduling, resolveTiers, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, tierIsFree, tierPayable, updateClerkUserMetadata, updateClerkUserMetadataByEmail, validateMembershipPageDocument, validateScheduling, webhookMutationId };
package/dist/index.d.ts CHANGED
@@ -1852,4 +1852,73 @@ declare function loadTiers(db: ChapterDb, group: TierGroupRow): Promise<ChapterT
1852
1852
  /** The tiers a join page may show: active, and joinable as configured. */
1853
1853
  declare function loadOfferableTiers(db: ChapterDb, group: TierGroupRow, hasSecretKey: boolean): Promise<ChapterTier[]>;
1854
1854
 
1855
- export { type AccountModel, type AdminNotificationTrigger, type Applicant, type ApplicationBookingPatch, type ApplicationRecord, type ApplicationSummary, type Attr, type AttrType, type Chapter, type ChapterApplication, type ChapterAuth, type ChapterBrand, type ChapterConfig, type ChapterCopy, type ChapterCopyInput, type ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkReader, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterRunbookHints, type ChapterScheduling, type ChapterSends, type ChapterTier, type ChapterTierConfig, type ClerkInviteInput, type ClerkMetadataInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, type CompiledChapterBrandTokens, DEFAULT_CHAPTER_COPY, DEFAULT_SHARE_FIELDS, type DashboardMetricData, type DashboardMetricSeries, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type JoinConfigTier, type LeaderCrmOptions, type LeaderFormationConfig, type LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NetworkRecord, type NetworkRecordNote, type NetworkRecordPage, type NetworkRollup, type NetworkRollupTarget, type NetworkSharedNote, type NetworkSnapshot, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedLeaderFormation, type ResolvedNetwork, type ResolvedNetworkReader, type ResolvedNetworkTarget, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedBusiness, type SharedPerson, type SharedRecord, type SharedRecordV1, type SharedRecordV2, type StripeEvent, type StripeResult, type SubmitResult, type TierGroup, type TierGroupRow, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, buildTierSeeds, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterBrandFromTokens, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, dashboardMetricData, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, findTier, firstPaymentPatch, formatChapterCopy, formationFields, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, joinConfigTiers, leaderCrmConfig, loadOfferableTiers, loadTiers, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, offerableTiers, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolveChapterCopy, resolveLeaderFormation, resolvePipeline, resolveScheduling, resolveTiers, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, tierIsFree, tierPayable, updateClerkUserMetadata, updateClerkUserMetadataByEmail, validateScheduling, webhookMutationId };
1855
+ /** The first stable, shared Chapter membership-page renderer contract. */
1856
+ declare const MEMBERSHIP_PAGE_RENDERER: "membership-cards-v1";
1857
+ /** Fixed visual emphasis choices supported by the versioned card renderer. */
1858
+ type MembershipTierVariant = "standard" | "featured" | "supporting";
1859
+ /** A bounded, token-rendered aside within one membership card. */
1860
+ interface MembershipTierCallout {
1861
+ label?: string;
1862
+ bodyMarkdown: string;
1863
+ }
1864
+ /** Scarcity copy displayed without changing tier availability or checkout. */
1865
+ interface MembershipTierCapacity {
1866
+ total: number;
1867
+ label?: string;
1868
+ }
1869
+ /**
1870
+ * Presentation attached to one server-authoritative membership tier.
1871
+ *
1872
+ * Price, free/paid state, identity, and application targeting deliberately do
1873
+ * not live here. They are resolved from the public Chapter join config so
1874
+ * authored marketing copy can never change what checkout charges.
1875
+ */
1876
+ interface MembershipTierPresentation {
1877
+ tierId: string;
1878
+ variant?: MembershipTierVariant;
1879
+ badge?: string;
1880
+ summaryMarkdown?: string;
1881
+ priceEyebrow?: string;
1882
+ compareAtPriceCents?: number;
1883
+ billingLabel?: string;
1884
+ priceNoteMarkdown?: string;
1885
+ capacity?: MembershipTierCapacity;
1886
+ benefits?: readonly string[];
1887
+ callout?: MembershipTierCallout;
1888
+ ctaLabel?: string;
1889
+ }
1890
+ /** A bounded document that can be reviewed once and rendered by any Chapter host. */
1891
+ interface MembershipPageDocument {
1892
+ schemaVersion: 1;
1893
+ renderer: typeof MEMBERSHIP_PAGE_RENDERER;
1894
+ tiers: readonly MembershipTierPresentation[];
1895
+ footnoteMarkdown?: string;
1896
+ }
1897
+ /** Minimal server-authoritative tier identity and charged amount used during validation. */
1898
+ interface MembershipTierAuthority {
1899
+ id: string;
1900
+ priceCents: number;
1901
+ }
1902
+ /** Live tier authority used to reject missing, unknown, or misleading presentation. */
1903
+ interface MembershipPageValidationOptions {
1904
+ /** When supplied, every live tier must occur exactly once in the document. */
1905
+ tiers?: readonly MembershipTierAuthority[];
1906
+ }
1907
+ /** A normalized document or the complete actionable list of validation failures. */
1908
+ type MembershipPageValidationResult = {
1909
+ ok: true;
1910
+ value: MembershipPageDocument;
1911
+ } | {
1912
+ ok: false;
1913
+ errors: readonly string[];
1914
+ };
1915
+ /**
1916
+ * Validate and normalize a membership-page document without evaluating code,
1917
+ * accepting HTML, or allowing authored links/styles. Markdown slots are later
1918
+ * rendered by @odla-ai/ui's bounded safe Markdown component.
1919
+ */
1920
+ declare function validateMembershipPageDocument(input: unknown, options?: MembershipPageValidationOptions): MembershipPageValidationResult;
1921
+ /** Validate a document or fail closed with all actionable validation errors. */
1922
+ declare function membershipPageDocument(input: unknown, options?: MembershipPageValidationOptions): MembershipPageDocument;
1923
+
1924
+ export { type AccountModel, type AdminNotificationTrigger, type Applicant, type ApplicationBookingPatch, type ApplicationRecord, type ApplicationSummary, type Attr, type AttrType, type Chapter, type ChapterApplication, type ChapterAuth, type ChapterBrand, type ChapterConfig, type ChapterCopy, type ChapterCopyInput, type ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkReader, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterRunbookHints, type ChapterScheduling, type ChapterSends, type ChapterTier, type ChapterTierConfig, type ClerkInviteInput, type ClerkMetadataInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, type CompiledChapterBrandTokens, DEFAULT_CHAPTER_COPY, DEFAULT_SHARE_FIELDS, type DashboardMetricData, type DashboardMetricSeries, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type JoinConfigTier, type LeaderCrmOptions, type LeaderFormationConfig, type LiveEvent, MEMBERSHIP_PAGE_RENDERER, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type MembershipPageDocument, type MembershipPageValidationOptions, type MembershipPageValidationResult, type MembershipTierAuthority, type MembershipTierCallout, type MembershipTierCapacity, type MembershipTierPresentation, type MembershipTierVariant, type NetworkRecord, type NetworkRecordNote, type NetworkRecordPage, type NetworkRollup, type NetworkRollupTarget, type NetworkSharedNote, type NetworkSnapshot, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedLeaderFormation, type ResolvedNetwork, type ResolvedNetworkReader, type ResolvedNetworkTarget, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedBusiness, type SharedPerson, type SharedRecord, type SharedRecordV1, type SharedRecordV2, type StripeEvent, type StripeResult, type SubmitResult, type TierGroup, type TierGroupRow, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, buildTierSeeds, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterBrandFromTokens, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, dashboardMetricData, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, findTier, firstPaymentPatch, formatChapterCopy, formationFields, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, joinConfigTiers, leaderCrmConfig, loadOfferableTiers, loadTiers, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, membershipPageDocument, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, offerableTiers, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolveChapterCopy, resolveLeaderFormation, resolvePipeline, resolveScheduling, resolveTiers, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, tierIsFree, tierPayable, updateClerkUserMetadata, updateClerkUserMetadataByEmail, validateMembershipPageDocument, validateScheduling, webhookMutationId };
package/dist/index.js CHANGED
@@ -2527,9 +2527,178 @@ async function loadTiers(db, group) {
2527
2527
  async function loadOfferableTiers(db, group, hasSecretKey) {
2528
2528
  return offerableTiers(await loadTiers(db, group), group, hasSecretKey);
2529
2529
  }
2530
+
2531
+ // src/membership-page.ts
2532
+ var MEMBERSHIP_PAGE_RENDERER = "membership-cards-v1";
2533
+ var TIER_ID = /^[a-z][a-z0-9-]{0,79}$/;
2534
+ var VARIANTS = /* @__PURE__ */ new Set(["standard", "featured", "supporting"]);
2535
+ var ROOT_KEYS = /* @__PURE__ */ new Set(["schemaVersion", "renderer", "tiers", "footnoteMarkdown"]);
2536
+ var TIER_KEYS = /* @__PURE__ */ new Set([
2537
+ "tierId",
2538
+ "variant",
2539
+ "badge",
2540
+ "summaryMarkdown",
2541
+ "priceEyebrow",
2542
+ "compareAtPriceCents",
2543
+ "billingLabel",
2544
+ "priceNoteMarkdown",
2545
+ "capacity",
2546
+ "benefits",
2547
+ "callout",
2548
+ "ctaLabel"
2549
+ ]);
2550
+ var CALLOUT_KEYS = /* @__PURE__ */ new Set(["label", "bodyMarkdown"]);
2551
+ var CAPACITY_KEYS = /* @__PURE__ */ new Set(["total", "label"]);
2552
+ var MAX_TIERS = 20;
2553
+ var MAX_BENEFITS = 24;
2554
+ var MAX_MARKDOWN = 65536;
2555
+ var MAX_LABEL = 160;
2556
+ var objectRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
2557
+ function rejectUnknownKeys(value, allowed, path, errors) {
2558
+ for (const key of Object.keys(value)) {
2559
+ if (!allowed.has(key)) errors.push(`${path}.${key} is not supported`);
2560
+ }
2561
+ }
2562
+ function boundedString(value, path, errors, max, required = false) {
2563
+ if (value === void 0) {
2564
+ if (required) errors.push(`${path} is required`);
2565
+ return void 0;
2566
+ }
2567
+ if (typeof value !== "string") {
2568
+ errors.push(`${path} must be a string`);
2569
+ return void 0;
2570
+ }
2571
+ const text = value.trim();
2572
+ if (required && !text) errors.push(`${path} is required`);
2573
+ if (text.length > max) errors.push(`${path} must be at most ${max.toLocaleString("en-US")} characters`);
2574
+ return text;
2575
+ }
2576
+ function validateMembershipPageDocument(input, options = {}) {
2577
+ const errors = [];
2578
+ const root = objectRecord(input);
2579
+ if (!root) return { ok: false, errors: ["document must be an object"] };
2580
+ rejectUnknownKeys(root, ROOT_KEYS, "document", errors);
2581
+ if (root.schemaVersion !== 1) errors.push("document.schemaVersion must be 1");
2582
+ if (root.renderer !== MEMBERSHIP_PAGE_RENDERER) {
2583
+ errors.push(`document.renderer must be "${MEMBERSHIP_PAGE_RENDERER}"`);
2584
+ }
2585
+ if (!Array.isArray(root.tiers)) errors.push("document.tiers must be an array");
2586
+ const rawTiers = Array.isArray(root.tiers) ? root.tiers : [];
2587
+ if (Array.isArray(root.tiers) && rawTiers.length === 0) errors.push("document.tiers must include at least one tier");
2588
+ if (rawTiers.length > MAX_TIERS) errors.push(`document.tiers supports at most ${MAX_TIERS} entries`);
2589
+ const authority = new Map((options.tiers ?? []).map((tier) => [tier.id, tier]));
2590
+ const seen = /* @__PURE__ */ new Set();
2591
+ const tiers2 = [];
2592
+ for (const [index, raw] of rawTiers.slice(0, MAX_TIERS).entries()) {
2593
+ const path = `document.tiers[${index}]`;
2594
+ const item = objectRecord(raw);
2595
+ if (!item) {
2596
+ errors.push(`${path} must be an object`);
2597
+ continue;
2598
+ }
2599
+ rejectUnknownKeys(item, TIER_KEYS, path, errors);
2600
+ const tierId = boundedString(item.tierId, `${path}.tierId`, errors, 80, true) ?? "";
2601
+ if (tierId && !TIER_ID.test(tierId)) errors.push(`${path}.tierId must be a lowercase kebab-case id`);
2602
+ if (seen.has(tierId)) errors.push(`${path}.tierId duplicates "${tierId}"`);
2603
+ seen.add(tierId);
2604
+ if (authority.size && !authority.has(tierId)) errors.push(`${path}.tierId "${tierId}" is not an offered tier`);
2605
+ const variant = item.variant;
2606
+ if (variant !== void 0 && (typeof variant !== "string" || !VARIANTS.has(variant))) {
2607
+ errors.push(`${path}.variant must be standard, featured, or supporting`);
2608
+ }
2609
+ const compareAt = item.compareAtPriceCents;
2610
+ if (compareAt !== void 0 && (!Number.isInteger(compareAt) || Number(compareAt) < 0 || Number(compareAt) > 1e8)) {
2611
+ errors.push(`${path}.compareAtPriceCents must be an integer from 0 to 100,000,000`);
2612
+ }
2613
+ const actual = authority.get(tierId)?.priceCents;
2614
+ if (Number.isInteger(compareAt) && actual !== void 0 && Number(compareAt) <= actual) {
2615
+ errors.push(`${path}.compareAtPriceCents must be greater than the authoritative tier price`);
2616
+ }
2617
+ let benefits;
2618
+ if (item.benefits !== void 0) {
2619
+ if (!Array.isArray(item.benefits)) errors.push(`${path}.benefits must be an array`);
2620
+ else {
2621
+ if (item.benefits.length > MAX_BENEFITS) errors.push(`${path}.benefits supports at most ${MAX_BENEFITS} entries`);
2622
+ benefits = item.benefits.slice(0, MAX_BENEFITS).flatMap((benefit, benefitIndex) => {
2623
+ const text = boundedString(benefit, `${path}.benefits[${benefitIndex}]`, errors, 2e3, true);
2624
+ return text ? [text] : [];
2625
+ });
2626
+ }
2627
+ }
2628
+ let callout;
2629
+ if (item.callout !== void 0) {
2630
+ const rawCallout = objectRecord(item.callout);
2631
+ if (!rawCallout) errors.push(`${path}.callout must be an object`);
2632
+ else {
2633
+ rejectUnknownKeys(rawCallout, CALLOUT_KEYS, `${path}.callout`, errors);
2634
+ const bodyMarkdown = boundedString(rawCallout.bodyMarkdown, `${path}.callout.bodyMarkdown`, errors, MAX_MARKDOWN, true);
2635
+ const label = boundedString(rawCallout.label, `${path}.callout.label`, errors, MAX_LABEL);
2636
+ if (bodyMarkdown) callout = { ...label ? { label } : {}, bodyMarkdown };
2637
+ }
2638
+ }
2639
+ let capacity;
2640
+ if (item.capacity !== void 0) {
2641
+ const rawCapacity = objectRecord(item.capacity);
2642
+ if (!rawCapacity) errors.push(`${path}.capacity must be an object`);
2643
+ else {
2644
+ rejectUnknownKeys(rawCapacity, CAPACITY_KEYS, `${path}.capacity`, errors);
2645
+ const total = rawCapacity.total;
2646
+ if (!Number.isInteger(total) || Number(total) < 1 || Number(total) > 1e6) {
2647
+ errors.push(`${path}.capacity.total must be an integer from 1 to 1,000,000`);
2648
+ }
2649
+ const label = boundedString(rawCapacity.label, `${path}.capacity.label`, errors, MAX_LABEL);
2650
+ if (Number.isInteger(total) && Number(total) >= 1 && Number(total) <= 1e6) {
2651
+ capacity = { total: Number(total), ...label ? { label } : {} };
2652
+ }
2653
+ }
2654
+ }
2655
+ const badge = boundedString(item.badge, `${path}.badge`, errors, MAX_LABEL);
2656
+ const summaryMarkdown = boundedString(item.summaryMarkdown, `${path}.summaryMarkdown`, errors, MAX_MARKDOWN);
2657
+ const priceEyebrow = boundedString(item.priceEyebrow, `${path}.priceEyebrow`, errors, MAX_LABEL);
2658
+ const billingLabel = boundedString(item.billingLabel, `${path}.billingLabel`, errors, MAX_LABEL);
2659
+ const priceNoteMarkdown = boundedString(item.priceNoteMarkdown, `${path}.priceNoteMarkdown`, errors, MAX_MARKDOWN);
2660
+ const ctaLabel = boundedString(item.ctaLabel, `${path}.ctaLabel`, errors, MAX_LABEL);
2661
+ tiers2.push({
2662
+ tierId,
2663
+ ...typeof variant === "string" && VARIANTS.has(variant) ? { variant } : {},
2664
+ ...badge ? { badge } : {},
2665
+ ...summaryMarkdown ? { summaryMarkdown } : {},
2666
+ ...priceEyebrow ? { priceEyebrow } : {},
2667
+ ...Number.isInteger(compareAt) ? { compareAtPriceCents: Number(compareAt) } : {},
2668
+ ...billingLabel ? { billingLabel } : {},
2669
+ ...priceNoteMarkdown ? { priceNoteMarkdown } : {},
2670
+ ...capacity ? { capacity } : {},
2671
+ ...benefits ? { benefits } : {},
2672
+ ...callout ? { callout } : {},
2673
+ ...ctaLabel ? { ctaLabel } : {}
2674
+ });
2675
+ }
2676
+ if (authority.size) {
2677
+ for (const tierId of authority.keys()) {
2678
+ if (!seen.has(tierId)) errors.push(`document.tiers is missing offered tier "${tierId}"`);
2679
+ }
2680
+ }
2681
+ const footnoteMarkdown = boundedString(root.footnoteMarkdown, "document.footnoteMarkdown", errors, MAX_MARKDOWN);
2682
+ if (errors.length) return { ok: false, errors };
2683
+ return {
2684
+ ok: true,
2685
+ value: {
2686
+ schemaVersion: 1,
2687
+ renderer: MEMBERSHIP_PAGE_RENDERER,
2688
+ tiers: tiers2,
2689
+ ...footnoteMarkdown ? { footnoteMarkdown } : {}
2690
+ }
2691
+ };
2692
+ }
2693
+ function membershipPageDocument(input, options = {}) {
2694
+ const result = validateMembershipPageDocument(input, options);
2695
+ if (!result.ok) throw new TypeError(`Invalid membership page document: ${result.errors.join("; ")}`);
2696
+ return result.value;
2697
+ }
2530
2698
  export {
2531
2699
  DEFAULT_CHAPTER_COPY,
2532
2700
  DEFAULT_SHARE_FIELDS,
2701
+ MEMBERSHIP_PAGE_RENDERER,
2533
2702
  SCHEDULING_DEFAULTS,
2534
2703
  applicantProfile,
2535
2704
  applicationBookingUpdate,
@@ -2585,6 +2754,7 @@ export {
2585
2754
  meetingRescheduleUpdate,
2586
2755
  memberApplication,
2587
2756
  memberSession,
2757
+ membershipPageDocument,
2588
2758
  networkSourceTag,
2589
2759
  normalizeSharedRecord,
2590
2760
  normalizeWebhookEvent,
@@ -2623,6 +2793,7 @@ export {
2623
2793
  tierPayable,
2624
2794
  updateClerkUserMetadata,
2625
2795
  updateClerkUserMetadataByEmail,
2796
+ validateMembershipPageDocument,
2626
2797
  validateScheduling,
2627
2798
  verifyStripeSignature,
2628
2799
  webhookMutationId