@odla-ai/chapter 0.20.2 → 0.22.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
@@ -53,8 +53,50 @@ interface Rule {
53
53
  }
54
54
  /** Namespace → rule set. */
55
55
  type DbRules = Record<string, Rule>;
56
- /** Brand tokens that theme the site: palette, fonts, wordmark, nav, logos. */
56
+ /** Typed semantic roles shared by the public site and application surfaces. */
57
+ interface ChapterBrandTokens {
58
+ background?: string;
59
+ surface?: string;
60
+ surface2?: string;
61
+ text?: string;
62
+ textMuted?: string;
63
+ textFaint?: string;
64
+ border?: string;
65
+ borderStrong?: string;
66
+ accent?: string;
67
+ accentStrong?: string;
68
+ accentSoft?: string;
69
+ onAccent?: string;
70
+ good?: string;
71
+ warn?: string;
72
+ danger?: string;
73
+ chart1?: string;
74
+ chart2?: string;
75
+ chart3?: string;
76
+ chart4?: string;
77
+ chart5?: string;
78
+ chart6?: string;
79
+ chartPositive?: string;
80
+ chartNegative?: string;
81
+ /** Admin/application content width, e.g. `"960px"`. */
82
+ contentWidth?: string;
83
+ panelRadius?: string;
84
+ panelShadow?: string;
85
+ masterDetailColumns?: string;
86
+ masterDetailMinHeight?: string;
87
+ /** Chapter-admin page padding shorthand. */
88
+ pagePadding?: string;
89
+ /** Chapter-admin workspace-tab padding shorthand. */
90
+ workspacePadding?: string;
91
+ }
92
+ /** Brand tokens that theme the site: theme, semantic roles, fonts, and logos. */
57
93
  interface ChapterBrand {
94
+ /** Preloaded @odla-ai/ui theme name used by ThemeScope. */
95
+ theme?: string;
96
+ /** Admin/application color mode. Explicit light is the safe default. */
97
+ colorScheme?: "light" | "dark" | "system";
98
+ tokens?: ChapterBrandTokens;
99
+ tokensDark?: ChapterBrandTokens;
58
100
  /** Palette overrides written into styles.css :root (e.g. `{ moss: "#2F3E34" }`
59
101
  * or `--ui-*` token names). Applies in BOTH light and dark unless overridden
60
102
  * by {@link paletteDark}. */
@@ -88,7 +130,7 @@ interface ChapterNetworkTarget {
88
130
  id: string;
89
131
  /** Human-facing site name. Defaults to `id`. */
90
132
  name?: string;
91
- /** Absolute follower origin, e.g. `https://silver.example.com`. */
133
+ /** Absolute follower origin, e.g. `https://chapter.example.com`. */
92
134
  url: string;
93
135
  /** Leader-vault key holding this follower's share secret. Defaults to
94
136
  * `network_share_<id-with-underscores>`. */
@@ -159,10 +201,10 @@ interface ChapterScheduling {
159
201
  }
160
202
  /** How a signed-in user's role is determined.
161
203
  *
162
- * - `"claim"` reads a role ladder from a JWT claim (Silver & Salt's model:
204
+ * - `"claim"` reads a role ladder from a JWT claim (the member-site model:
163
205
  * `provisional → member → admin`). Admin is the last (highest) rung; the
164
206
  * read-only `superAdmins` tier sits above it.
165
- * - `"table"` gates on the deny-all `admins` allowlist (Built Not Found's model):
207
+ * - `"table"` gates on the deny-all `admins` allowlist (the hub model):
166
208
  * a binary "email is an admin or isn't", set only in odla Studio.
167
209
  *
168
210
  * Defaults: `"claim"` in `chapter` mode, `"table"` in `hub` mode. */
@@ -189,7 +231,7 @@ interface ResolvedAuth {
189
231
  }
190
232
  /** The application status pipeline. Which statuses exist, and the subsets a site
191
233
  * allows a call to be booked from / an application approved from. Defaults to
192
- * Silver & Salt's pipeline. Status never moves backwards (package-enforced). */
234
+ * the reference pipeline. Status never moves backwards (package-enforced). */
193
235
  interface ChapterPipeline {
194
236
  stages?: readonly string[];
195
237
  bookableFrom?: readonly string[];
@@ -206,8 +248,8 @@ interface ResolvedPipeline {
206
248
  }
207
249
  /** The application (join form) validation surface — which string fields are
208
250
  * required vs accepted, their max lengths, and the request body cap. Drives
209
- * submit validation + the CRM slot projection; defaults to Silver & Salt's form.
210
- * The `applications` schema attrs stay fixed (byte-equal to S&S); this is
251
+ * submit validation + the CRM slot projection; defaults to the reference form.
252
+ * The `applications` schema attrs stay fixed; this is
211
253
  * validation config, not schema generation. */
212
254
  interface ChapterApplication {
213
255
  required?: readonly string[];
@@ -279,7 +321,7 @@ interface ChapterConfig {
279
321
  /** `notificationEmail` required in `chapter` mode. */
280
322
  emails?: ChapterEmails;
281
323
  scheduling?: ChapterScheduling;
282
- /** Application status pipeline (stages + bookable/approvable subsets). Defaults to S&S's. */
324
+ /** Application status pipeline (stages + bookable/approvable subsets). */
283
325
  pipeline?: ChapterPipeline;
284
326
  /** Join-form validation (required/optional fields, max lengths, body cap). */
285
327
  application?: ChapterApplication;
@@ -439,7 +481,7 @@ declare function createChapterIntegration(chapter: Chapter, options?: ChapterInt
439
481
  * `chapter` mode only. The auth tables follow {@link ResolvedAuth}: `source:
440
482
  * "table"` adds the `admins` allowlist (hub/BNF); `superAdmins` adds the
441
483
  * read-only super-admin tier (default on for the `"claim"` ladder). A `"claim"`
442
- * chapter therefore emits exactly Silver & Salt's namespace set — `applications`,
484
+ * chapter therefore emits the reference namespace set — `applications`,
443
485
  * `groups`, `meetings`, `emailLog`, `superAdmins` — with no `admins` table. */
444
486
  declare function chapterDb(mode: ChapterMode, auth: ResolvedAuth): {
445
487
  schema: DbSchema;
@@ -457,8 +499,8 @@ declare function buildGroupSeed(config: ChapterConfig): Record<string, unknown>;
457
499
  /**
458
500
  * Apply defaults + validate the auth config into a {@link ResolvedAuth}. Defaults
459
501
  * by mode: `chapter` → the `provisional/member/admin` claim ladder with the
460
- * `superAdmins` tier (Silver & Salt); `hub` → the `admins` allowlist table, no
461
- * super tier (Built Not Found). Throws at import on a bad policy.
502
+ * `superAdmins` tier; `hub` → the `admins` allowlist table with no super tier.
503
+ * Throws at import on a bad policy.
462
504
  */
463
505
  declare function resolveAuth(mode: ChapterMode, auth: ChapterAuth | undefined): ResolvedAuth;
464
506
  /** The role from a verified JWT payload, per the resolved policy. An unknown or
@@ -633,8 +675,8 @@ declare function sendTemplated(deps: NotifyDeps, input: NotifyInput): Promise<No
633
675
  declare function emailGroupFrom(row: Record<string, unknown>): EmailGroup;
634
676
 
635
677
  /**
636
- * Apply defaults + validate the pipeline config. With no config, the full Silver
637
- * & Salt pipeline. With `stages` given but the subsets omitted, the subsets
678
+ * Apply defaults + validate the pipeline config. With no config, the complete
679
+ * reference pipeline. With `stages` given but the subsets omitted, the subsets
638
680
  * default to empty (a site opts in to bookable/approvable states explicitly).
639
681
  * Throws at import on a bad pipeline (empty/duplicate stages, an initial or a
640
682
  * subset entry not on the ladder).
@@ -678,8 +720,7 @@ declare function paymentsReady(group: PaymentsGroup, hasSecretKey: boolean): boo
678
720
  * of nested objects into bracket syntax (`metadata[applicationId]=...`). */
679
721
  declare function stripeForm(params: Record<string, unknown>): string;
680
722
  /** The Stripe idempotency key for creating an application's subscription — one
681
- * per application, so a client retry can't orphan a second subscription (S&S
682
- * lacked this; the package enforces it). */
723
+ * per application, so a client retry can't orphan a second subscription. */
683
724
  declare function subscriptionIdempotencyKey(applicationId: string): string;
684
725
  /** The db mutationId for a webhook-driven write — exactly-once per Stripe event,
685
726
  * so replays are deduped at the db layer. */
@@ -941,6 +982,22 @@ declare function bucketSeries(points: Array<{
941
982
  weekStart: number;
942
983
  value: number;
943
984
  }>;
985
+ /** Labels and current/previous values for one dashboard comparison window. */
986
+ interface DashboardMetricSeries {
987
+ labels: string[];
988
+ current: number[];
989
+ previous: number[];
990
+ }
991
+ /** Week-over-week and month-over-month series consumed by MetricWidget. */
992
+ interface DashboardMetricData {
993
+ wow: DashboardMetricSeries;
994
+ mom: DashboardMetricSeries;
995
+ }
996
+ /** MetricWidget-ready daily and weekly comparison windows. */
997
+ declare function dashboardMetricData(points: Array<{
998
+ t: number;
999
+ v: number;
1000
+ }>, now: number): DashboardMetricData;
944
1001
  /** Annualized cents for a Stripe subscription: sum each item's
945
1002
  * `unit_amount * quantity`, ×12 for monthly intervals. A yearly interval is
946
1003
  * taken as-is. Returns 0 for a shape with no priced items. */
@@ -1183,7 +1240,9 @@ declare function memberSession(user: SessionUser, opts: {
1183
1240
  * theme. These are brand OVERRIDES on top of a base theme — they do not replace
1184
1241
  * the theme layer the components need (see {@link brandTokens} usage in the docs).
1185
1242
  */
1186
- declare function brandTokens(brand: ChapterBrand | undefined): string;
1243
+ declare function brandTokens(brand: ChapterBrand | undefined, options?: {
1244
+ selector?: string;
1245
+ }): string;
1187
1246
 
1188
1247
  /** A `meetings` row, as far as reconciliation cares. */
1189
1248
  interface MeetingForReconcile {
@@ -1235,13 +1294,13 @@ interface ResolvedScheduling {
1235
1294
  windowDays: number;
1236
1295
  summaryTemplate: string;
1237
1296
  }
1238
- /** The S&S-proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1297
+ /** Proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1239
1298
  * a 14-day window. The summary is generic (a chapter's group seed supplies a
1240
1299
  * name-branded one). */
1241
1300
  declare const SCHEDULING_DEFAULTS: ResolvedScheduling;
1242
1301
  /**
1243
1302
  * Resolve a group's scheduling config against the defaults, validating every
1244
- * bound the way S&S does at config-write time (throws on a bad config, so a
1303
+ * bound at config-write time (throws on a bad config, so a
1245
1304
  * misconfiguration surfaces immediately rather than yielding empty slots).
1246
1305
  * `windowDays` is capped at 62 because Google FreeBusy is.
1247
1306
  */
@@ -1345,4 +1404,4 @@ type ApplicationBookingPatch = {
1345
1404
  * already there (never backward). */
1346
1405
  declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
1347
1406
 
1348
- 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 ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, DEFAULT_SHARE_FIELDS, 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 LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedNetwork, 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 StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
1407
+ 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 ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, 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 LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedNetwork, 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 StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, dashboardMetricData, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
package/dist/index.d.ts CHANGED
@@ -53,8 +53,50 @@ interface Rule {
53
53
  }
54
54
  /** Namespace → rule set. */
55
55
  type DbRules = Record<string, Rule>;
56
- /** Brand tokens that theme the site: palette, fonts, wordmark, nav, logos. */
56
+ /** Typed semantic roles shared by the public site and application surfaces. */
57
+ interface ChapterBrandTokens {
58
+ background?: string;
59
+ surface?: string;
60
+ surface2?: string;
61
+ text?: string;
62
+ textMuted?: string;
63
+ textFaint?: string;
64
+ border?: string;
65
+ borderStrong?: string;
66
+ accent?: string;
67
+ accentStrong?: string;
68
+ accentSoft?: string;
69
+ onAccent?: string;
70
+ good?: string;
71
+ warn?: string;
72
+ danger?: string;
73
+ chart1?: string;
74
+ chart2?: string;
75
+ chart3?: string;
76
+ chart4?: string;
77
+ chart5?: string;
78
+ chart6?: string;
79
+ chartPositive?: string;
80
+ chartNegative?: string;
81
+ /** Admin/application content width, e.g. `"960px"`. */
82
+ contentWidth?: string;
83
+ panelRadius?: string;
84
+ panelShadow?: string;
85
+ masterDetailColumns?: string;
86
+ masterDetailMinHeight?: string;
87
+ /** Chapter-admin page padding shorthand. */
88
+ pagePadding?: string;
89
+ /** Chapter-admin workspace-tab padding shorthand. */
90
+ workspacePadding?: string;
91
+ }
92
+ /** Brand tokens that theme the site: theme, semantic roles, fonts, and logos. */
57
93
  interface ChapterBrand {
94
+ /** Preloaded @odla-ai/ui theme name used by ThemeScope. */
95
+ theme?: string;
96
+ /** Admin/application color mode. Explicit light is the safe default. */
97
+ colorScheme?: "light" | "dark" | "system";
98
+ tokens?: ChapterBrandTokens;
99
+ tokensDark?: ChapterBrandTokens;
58
100
  /** Palette overrides written into styles.css :root (e.g. `{ moss: "#2F3E34" }`
59
101
  * or `--ui-*` token names). Applies in BOTH light and dark unless overridden
60
102
  * by {@link paletteDark}. */
@@ -88,7 +130,7 @@ interface ChapterNetworkTarget {
88
130
  id: string;
89
131
  /** Human-facing site name. Defaults to `id`. */
90
132
  name?: string;
91
- /** Absolute follower origin, e.g. `https://silver.example.com`. */
133
+ /** Absolute follower origin, e.g. `https://chapter.example.com`. */
92
134
  url: string;
93
135
  /** Leader-vault key holding this follower's share secret. Defaults to
94
136
  * `network_share_<id-with-underscores>`. */
@@ -159,10 +201,10 @@ interface ChapterScheduling {
159
201
  }
160
202
  /** How a signed-in user's role is determined.
161
203
  *
162
- * - `"claim"` reads a role ladder from a JWT claim (Silver & Salt's model:
204
+ * - `"claim"` reads a role ladder from a JWT claim (the member-site model:
163
205
  * `provisional → member → admin`). Admin is the last (highest) rung; the
164
206
  * read-only `superAdmins` tier sits above it.
165
- * - `"table"` gates on the deny-all `admins` allowlist (Built Not Found's model):
207
+ * - `"table"` gates on the deny-all `admins` allowlist (the hub model):
166
208
  * a binary "email is an admin or isn't", set only in odla Studio.
167
209
  *
168
210
  * Defaults: `"claim"` in `chapter` mode, `"table"` in `hub` mode. */
@@ -189,7 +231,7 @@ interface ResolvedAuth {
189
231
  }
190
232
  /** The application status pipeline. Which statuses exist, and the subsets a site
191
233
  * allows a call to be booked from / an application approved from. Defaults to
192
- * Silver & Salt's pipeline. Status never moves backwards (package-enforced). */
234
+ * the reference pipeline. Status never moves backwards (package-enforced). */
193
235
  interface ChapterPipeline {
194
236
  stages?: readonly string[];
195
237
  bookableFrom?: readonly string[];
@@ -206,8 +248,8 @@ interface ResolvedPipeline {
206
248
  }
207
249
  /** The application (join form) validation surface — which string fields are
208
250
  * required vs accepted, their max lengths, and the request body cap. Drives
209
- * submit validation + the CRM slot projection; defaults to Silver & Salt's form.
210
- * The `applications` schema attrs stay fixed (byte-equal to S&S); this is
251
+ * submit validation + the CRM slot projection; defaults to the reference form.
252
+ * The `applications` schema attrs stay fixed; this is
211
253
  * validation config, not schema generation. */
212
254
  interface ChapterApplication {
213
255
  required?: readonly string[];
@@ -279,7 +321,7 @@ interface ChapterConfig {
279
321
  /** `notificationEmail` required in `chapter` mode. */
280
322
  emails?: ChapterEmails;
281
323
  scheduling?: ChapterScheduling;
282
- /** Application status pipeline (stages + bookable/approvable subsets). Defaults to S&S's. */
324
+ /** Application status pipeline (stages + bookable/approvable subsets). */
283
325
  pipeline?: ChapterPipeline;
284
326
  /** Join-form validation (required/optional fields, max lengths, body cap). */
285
327
  application?: ChapterApplication;
@@ -439,7 +481,7 @@ declare function createChapterIntegration(chapter: Chapter, options?: ChapterInt
439
481
  * `chapter` mode only. The auth tables follow {@link ResolvedAuth}: `source:
440
482
  * "table"` adds the `admins` allowlist (hub/BNF); `superAdmins` adds the
441
483
  * read-only super-admin tier (default on for the `"claim"` ladder). A `"claim"`
442
- * chapter therefore emits exactly Silver & Salt's namespace set — `applications`,
484
+ * chapter therefore emits the reference namespace set — `applications`,
443
485
  * `groups`, `meetings`, `emailLog`, `superAdmins` — with no `admins` table. */
444
486
  declare function chapterDb(mode: ChapterMode, auth: ResolvedAuth): {
445
487
  schema: DbSchema;
@@ -457,8 +499,8 @@ declare function buildGroupSeed(config: ChapterConfig): Record<string, unknown>;
457
499
  /**
458
500
  * Apply defaults + validate the auth config into a {@link ResolvedAuth}. Defaults
459
501
  * by mode: `chapter` → the `provisional/member/admin` claim ladder with the
460
- * `superAdmins` tier (Silver & Salt); `hub` → the `admins` allowlist table, no
461
- * super tier (Built Not Found). Throws at import on a bad policy.
502
+ * `superAdmins` tier; `hub` → the `admins` allowlist table with no super tier.
503
+ * Throws at import on a bad policy.
462
504
  */
463
505
  declare function resolveAuth(mode: ChapterMode, auth: ChapterAuth | undefined): ResolvedAuth;
464
506
  /** The role from a verified JWT payload, per the resolved policy. An unknown or
@@ -633,8 +675,8 @@ declare function sendTemplated(deps: NotifyDeps, input: NotifyInput): Promise<No
633
675
  declare function emailGroupFrom(row: Record<string, unknown>): EmailGroup;
634
676
 
635
677
  /**
636
- * Apply defaults + validate the pipeline config. With no config, the full Silver
637
- * & Salt pipeline. With `stages` given but the subsets omitted, the subsets
678
+ * Apply defaults + validate the pipeline config. With no config, the complete
679
+ * reference pipeline. With `stages` given but the subsets omitted, the subsets
638
680
  * default to empty (a site opts in to bookable/approvable states explicitly).
639
681
  * Throws at import on a bad pipeline (empty/duplicate stages, an initial or a
640
682
  * subset entry not on the ladder).
@@ -678,8 +720,7 @@ declare function paymentsReady(group: PaymentsGroup, hasSecretKey: boolean): boo
678
720
  * of nested objects into bracket syntax (`metadata[applicationId]=...`). */
679
721
  declare function stripeForm(params: Record<string, unknown>): string;
680
722
  /** The Stripe idempotency key for creating an application's subscription — one
681
- * per application, so a client retry can't orphan a second subscription (S&S
682
- * lacked this; the package enforces it). */
723
+ * per application, so a client retry can't orphan a second subscription. */
683
724
  declare function subscriptionIdempotencyKey(applicationId: string): string;
684
725
  /** The db mutationId for a webhook-driven write — exactly-once per Stripe event,
685
726
  * so replays are deduped at the db layer. */
@@ -941,6 +982,22 @@ declare function bucketSeries(points: Array<{
941
982
  weekStart: number;
942
983
  value: number;
943
984
  }>;
985
+ /** Labels and current/previous values for one dashboard comparison window. */
986
+ interface DashboardMetricSeries {
987
+ labels: string[];
988
+ current: number[];
989
+ previous: number[];
990
+ }
991
+ /** Week-over-week and month-over-month series consumed by MetricWidget. */
992
+ interface DashboardMetricData {
993
+ wow: DashboardMetricSeries;
994
+ mom: DashboardMetricSeries;
995
+ }
996
+ /** MetricWidget-ready daily and weekly comparison windows. */
997
+ declare function dashboardMetricData(points: Array<{
998
+ t: number;
999
+ v: number;
1000
+ }>, now: number): DashboardMetricData;
944
1001
  /** Annualized cents for a Stripe subscription: sum each item's
945
1002
  * `unit_amount * quantity`, ×12 for monthly intervals. A yearly interval is
946
1003
  * taken as-is. Returns 0 for a shape with no priced items. */
@@ -1183,7 +1240,9 @@ declare function memberSession(user: SessionUser, opts: {
1183
1240
  * theme. These are brand OVERRIDES on top of a base theme — they do not replace
1184
1241
  * the theme layer the components need (see {@link brandTokens} usage in the docs).
1185
1242
  */
1186
- declare function brandTokens(brand: ChapterBrand | undefined): string;
1243
+ declare function brandTokens(brand: ChapterBrand | undefined, options?: {
1244
+ selector?: string;
1245
+ }): string;
1187
1246
 
1188
1247
  /** A `meetings` row, as far as reconciliation cares. */
1189
1248
  interface MeetingForReconcile {
@@ -1235,13 +1294,13 @@ interface ResolvedScheduling {
1235
1294
  windowDays: number;
1236
1295
  summaryTemplate: string;
1237
1296
  }
1238
- /** The S&S-proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1297
+ /** Proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1239
1298
  * a 14-day window. The summary is generic (a chapter's group seed supplies a
1240
1299
  * name-branded one). */
1241
1300
  declare const SCHEDULING_DEFAULTS: ResolvedScheduling;
1242
1301
  /**
1243
1302
  * Resolve a group's scheduling config against the defaults, validating every
1244
- * bound the way S&S does at config-write time (throws on a bad config, so a
1303
+ * bound at config-write time (throws on a bad config, so a
1245
1304
  * misconfiguration surfaces immediately rather than yielding empty slots).
1246
1305
  * `windowDays` is capped at 62 because Google FreeBusy is.
1247
1306
  */
@@ -1345,4 +1404,4 @@ type ApplicationBookingPatch = {
1345
1404
  * already there (never backward). */
1346
1405
  declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
1347
1406
 
1348
- 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 ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, DEFAULT_SHARE_FIELDS, 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 LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedNetwork, 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 StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
1407
+ 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 ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, 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 LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedNetwork, 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 StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, dashboardMetricData, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
package/dist/index.js CHANGED
@@ -1111,6 +1111,31 @@ function bucketSeries(points, now, weeks = 12) {
1111
1111
  }
1112
1112
  return buckets;
1113
1113
  }
1114
+ function periodSeries(points, now, count, periodMs) {
1115
+ const start = now - count * periodMs;
1116
+ const previousStart = start - count * periodMs;
1117
+ const current = Array.from({ length: count }, () => 0);
1118
+ const previous = Array.from({ length: count }, () => 0);
1119
+ for (const point of points) {
1120
+ if (!Number.isFinite(point.t) || !Number.isFinite(point.v)) continue;
1121
+ if (point.t >= start && point.t <= now) {
1122
+ const index = Math.min(count - 1, Math.floor((point.t - start) / periodMs));
1123
+ current[index] = (current[index] ?? 0) + point.v;
1124
+ } else if (point.t >= previousStart && point.t < start) {
1125
+ const index = Math.min(count - 1, Math.floor((point.t - previousStart) / periodMs));
1126
+ previous[index] = (previous[index] ?? 0) + point.v;
1127
+ }
1128
+ }
1129
+ const labels = current.map((_, index) => new Date(start + index * periodMs).toISOString().slice(5, 10));
1130
+ return { labels, current, previous };
1131
+ }
1132
+ function dashboardMetricData(points, now) {
1133
+ const DAY = 864e5;
1134
+ return {
1135
+ wow: periodSeries(points, now, 7, DAY),
1136
+ mom: periodSeries(points, now, 4, 7 * DAY)
1137
+ };
1138
+ }
1114
1139
  function subAnnualCents(sub) {
1115
1140
  const items = sub.items?.data ?? [];
1116
1141
  let cents = 0;
@@ -1367,15 +1392,55 @@ function paletteDecls(palette) {
1367
1392
  }
1368
1393
  return decls;
1369
1394
  }
1370
- function brandTokens(brand) {
1395
+ var TOKEN_VARS = {
1396
+ background: "--ui-bg",
1397
+ surface: "--ui-surface",
1398
+ surface2: "--ui-surface-2",
1399
+ text: "--ui-text",
1400
+ textMuted: "--ui-text-muted",
1401
+ textFaint: "--ui-text-faint",
1402
+ border: "--ui-border",
1403
+ borderStrong: "--ui-border-strong",
1404
+ accent: "--ui-accent",
1405
+ accentStrong: "--ui-accent-strong",
1406
+ accentSoft: "--ui-accent-soft",
1407
+ onAccent: "--ui-on-accent",
1408
+ good: "--ui-good",
1409
+ warn: "--ui-warn",
1410
+ danger: "--ui-danger",
1411
+ chart1: "--ui-chart-1",
1412
+ chart2: "--ui-chart-2",
1413
+ chart3: "--ui-chart-3",
1414
+ chart4: "--ui-chart-4",
1415
+ chart5: "--ui-chart-5",
1416
+ chart6: "--ui-chart-6",
1417
+ chartPositive: "--ui-chart-pos",
1418
+ chartNegative: "--ui-chart-neg",
1419
+ contentWidth: "--ui-content-width",
1420
+ panelRadius: "--ui-radius-lg",
1421
+ panelShadow: "--ui-shadow",
1422
+ masterDetailColumns: "--ui-master-detail-columns",
1423
+ masterDetailMinHeight: "--ui-master-detail-min-height",
1424
+ pagePadding: "--chapter-admin-page-padding",
1425
+ workspacePadding: "--chapter-admin-workspace-padding"
1426
+ };
1427
+ function semanticDecls(tokens) {
1428
+ return Object.entries(tokens ?? {}).flatMap(
1429
+ ([key, value]) => typeof value === "string" && value.trim() ? [`${TOKEN_VARS[key]}: ${cleanValue(value)};`] : []
1430
+ );
1431
+ }
1432
+ function brandTokens(brand, options = {}) {
1371
1433
  if (!brand) return "";
1372
- const light = paletteDecls(brand.palette);
1434
+ const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];
1373
1435
  const fonts = brand.fonts;
1374
1436
  if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);
1375
1437
  if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);
1376
1438
  if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);
1377
- const dark = paletteDecls(brand.paletteDark);
1378
- let css = light.length ? `:root {
1439
+ const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];
1440
+ const selector = options.selector ?? ":root";
1441
+ const darkSelector = selector === ":root" ? ':root[data-theme="dark"]' : `${selector}[data-theme="dark"]`;
1442
+ const systemSelector = selector === ":root" ? ':root:not([data-theme="light"])' : `${selector}:not([data-theme="light"])`;
1443
+ let css = light.length ? `${selector} {
1379
1444
  ${light.join("\n ")}
1380
1445
  }
1381
1446
  ` : "";
@@ -1383,9 +1448,9 @@ function brandTokens(brand) {
1383
1448
  const block = `{
1384
1449
  ${dark.join("\n ")}
1385
1450
  }`;
1386
- css += `:root[data-theme="dark"] ${block}
1451
+ css += `${darkSelector} ${block}
1387
1452
  @media (prefers-color-scheme: dark) {
1388
- :root:not([data-theme="light"]) ${block}
1453
+ ${systemSelector} ${block}
1389
1454
  }
1390
1455
  `;
1391
1456
  }
@@ -1558,6 +1623,7 @@ export {
1558
1623
  createChapterIntegration,
1559
1624
  createClerkInvitation,
1560
1625
  createClerkUser,
1626
+ dashboardMetricData,
1561
1627
  defaultCrm,
1562
1628
  defineChapter,
1563
1629
  emailGroupFrom,