@odla-ai/chapter 0.20.2 → 0.21.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,36 @@ 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
+ chartPositive?: string;
76
+ chartNegative?: string;
77
+ }
78
+ /** Brand tokens that theme the site: theme, semantic roles, fonts, and logos. */
57
79
  interface ChapterBrand {
80
+ /** Preloaded @odla-ai/ui theme name used by ThemeScope. */
81
+ theme?: string;
82
+ /** Admin/application color mode. Explicit light is the safe default. */
83
+ colorScheme?: "light" | "dark" | "system";
84
+ tokens?: ChapterBrandTokens;
85
+ tokensDark?: ChapterBrandTokens;
58
86
  /** Palette overrides written into styles.css :root (e.g. `{ moss: "#2F3E34" }`
59
87
  * or `--ui-*` token names). Applies in BOTH light and dark unless overridden
60
88
  * by {@link paletteDark}. */
@@ -88,7 +116,7 @@ interface ChapterNetworkTarget {
88
116
  id: string;
89
117
  /** Human-facing site name. Defaults to `id`. */
90
118
  name?: string;
91
- /** Absolute follower origin, e.g. `https://silver.example.com`. */
119
+ /** Absolute follower origin, e.g. `https://chapter.example.com`. */
92
120
  url: string;
93
121
  /** Leader-vault key holding this follower's share secret. Defaults to
94
122
  * `network_share_<id-with-underscores>`. */
@@ -159,10 +187,10 @@ interface ChapterScheduling {
159
187
  }
160
188
  /** How a signed-in user's role is determined.
161
189
  *
162
- * - `"claim"` reads a role ladder from a JWT claim (Silver & Salt's model:
190
+ * - `"claim"` reads a role ladder from a JWT claim (the member-site model:
163
191
  * `provisional → member → admin`). Admin is the last (highest) rung; the
164
192
  * read-only `superAdmins` tier sits above it.
165
- * - `"table"` gates on the deny-all `admins` allowlist (Built Not Found's model):
193
+ * - `"table"` gates on the deny-all `admins` allowlist (the hub model):
166
194
  * a binary "email is an admin or isn't", set only in odla Studio.
167
195
  *
168
196
  * Defaults: `"claim"` in `chapter` mode, `"table"` in `hub` mode. */
@@ -189,7 +217,7 @@ interface ResolvedAuth {
189
217
  }
190
218
  /** The application status pipeline. Which statuses exist, and the subsets a site
191
219
  * allows a call to be booked from / an application approved from. Defaults to
192
- * Silver & Salt's pipeline. Status never moves backwards (package-enforced). */
220
+ * the reference pipeline. Status never moves backwards (package-enforced). */
193
221
  interface ChapterPipeline {
194
222
  stages?: readonly string[];
195
223
  bookableFrom?: readonly string[];
@@ -206,8 +234,8 @@ interface ResolvedPipeline {
206
234
  }
207
235
  /** The application (join form) validation surface — which string fields are
208
236
  * 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
237
+ * submit validation + the CRM slot projection; defaults to the reference form.
238
+ * The `applications` schema attrs stay fixed; this is
211
239
  * validation config, not schema generation. */
212
240
  interface ChapterApplication {
213
241
  required?: readonly string[];
@@ -279,7 +307,7 @@ interface ChapterConfig {
279
307
  /** `notificationEmail` required in `chapter` mode. */
280
308
  emails?: ChapterEmails;
281
309
  scheduling?: ChapterScheduling;
282
- /** Application status pipeline (stages + bookable/approvable subsets). Defaults to S&S's. */
310
+ /** Application status pipeline (stages + bookable/approvable subsets). */
283
311
  pipeline?: ChapterPipeline;
284
312
  /** Join-form validation (required/optional fields, max lengths, body cap). */
285
313
  application?: ChapterApplication;
@@ -439,7 +467,7 @@ declare function createChapterIntegration(chapter: Chapter, options?: ChapterInt
439
467
  * `chapter` mode only. The auth tables follow {@link ResolvedAuth}: `source:
440
468
  * "table"` adds the `admins` allowlist (hub/BNF); `superAdmins` adds the
441
469
  * read-only super-admin tier (default on for the `"claim"` ladder). A `"claim"`
442
- * chapter therefore emits exactly Silver & Salt's namespace set — `applications`,
470
+ * chapter therefore emits the reference namespace set — `applications`,
443
471
  * `groups`, `meetings`, `emailLog`, `superAdmins` — with no `admins` table. */
444
472
  declare function chapterDb(mode: ChapterMode, auth: ResolvedAuth): {
445
473
  schema: DbSchema;
@@ -457,8 +485,8 @@ declare function buildGroupSeed(config: ChapterConfig): Record<string, unknown>;
457
485
  /**
458
486
  * Apply defaults + validate the auth config into a {@link ResolvedAuth}. Defaults
459
487
  * 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.
488
+ * `superAdmins` tier; `hub` → the `admins` allowlist table with no super tier.
489
+ * Throws at import on a bad policy.
462
490
  */
463
491
  declare function resolveAuth(mode: ChapterMode, auth: ChapterAuth | undefined): ResolvedAuth;
464
492
  /** The role from a verified JWT payload, per the resolved policy. An unknown or
@@ -633,8 +661,8 @@ declare function sendTemplated(deps: NotifyDeps, input: NotifyInput): Promise<No
633
661
  declare function emailGroupFrom(row: Record<string, unknown>): EmailGroup;
634
662
 
635
663
  /**
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
664
+ * Apply defaults + validate the pipeline config. With no config, the complete
665
+ * reference pipeline. With `stages` given but the subsets omitted, the subsets
638
666
  * default to empty (a site opts in to bookable/approvable states explicitly).
639
667
  * Throws at import on a bad pipeline (empty/duplicate stages, an initial or a
640
668
  * subset entry not on the ladder).
@@ -678,8 +706,7 @@ declare function paymentsReady(group: PaymentsGroup, hasSecretKey: boolean): boo
678
706
  * of nested objects into bracket syntax (`metadata[applicationId]=...`). */
679
707
  declare function stripeForm(params: Record<string, unknown>): string;
680
708
  /** 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). */
709
+ * per application, so a client retry can't orphan a second subscription. */
683
710
  declare function subscriptionIdempotencyKey(applicationId: string): string;
684
711
  /** The db mutationId for a webhook-driven write — exactly-once per Stripe event,
685
712
  * so replays are deduped at the db layer. */
@@ -941,6 +968,22 @@ declare function bucketSeries(points: Array<{
941
968
  weekStart: number;
942
969
  value: number;
943
970
  }>;
971
+ /** Labels and current/previous values for one dashboard comparison window. */
972
+ interface DashboardMetricSeries {
973
+ labels: string[];
974
+ current: number[];
975
+ previous: number[];
976
+ }
977
+ /** Week-over-week and month-over-month series consumed by MetricWidget. */
978
+ interface DashboardMetricData {
979
+ wow: DashboardMetricSeries;
980
+ mom: DashboardMetricSeries;
981
+ }
982
+ /** MetricWidget-ready daily and weekly comparison windows. */
983
+ declare function dashboardMetricData(points: Array<{
984
+ t: number;
985
+ v: number;
986
+ }>, now: number): DashboardMetricData;
944
987
  /** Annualized cents for a Stripe subscription: sum each item's
945
988
  * `unit_amount * quantity`, ×12 for monthly intervals. A yearly interval is
946
989
  * taken as-is. Returns 0 for a shape with no priced items. */
@@ -1183,7 +1226,9 @@ declare function memberSession(user: SessionUser, opts: {
1183
1226
  * theme. These are brand OVERRIDES on top of a base theme — they do not replace
1184
1227
  * the theme layer the components need (see {@link brandTokens} usage in the docs).
1185
1228
  */
1186
- declare function brandTokens(brand: ChapterBrand | undefined): string;
1229
+ declare function brandTokens(brand: ChapterBrand | undefined, options?: {
1230
+ selector?: string;
1231
+ }): string;
1187
1232
 
1188
1233
  /** A `meetings` row, as far as reconciliation cares. */
1189
1234
  interface MeetingForReconcile {
@@ -1235,13 +1280,13 @@ interface ResolvedScheduling {
1235
1280
  windowDays: number;
1236
1281
  summaryTemplate: string;
1237
1282
  }
1238
- /** The S&S-proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1283
+ /** Proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1239
1284
  * a 14-day window. The summary is generic (a chapter's group seed supplies a
1240
1285
  * name-branded one). */
1241
1286
  declare const SCHEDULING_DEFAULTS: ResolvedScheduling;
1242
1287
  /**
1243
1288
  * 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
1289
+ * bound at config-write time (throws on a bad config, so a
1245
1290
  * misconfiguration surfaces immediately rather than yielding empty slots).
1246
1291
  * `windowDays` is capped at 62 because Google FreeBusy is.
1247
1292
  */
@@ -1345,4 +1390,4 @@ type ApplicationBookingPatch = {
1345
1390
  * already there (never backward). */
1346
1391
  declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
1347
1392
 
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 };
1393
+ 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,36 @@ 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
+ chartPositive?: string;
76
+ chartNegative?: string;
77
+ }
78
+ /** Brand tokens that theme the site: theme, semantic roles, fonts, and logos. */
57
79
  interface ChapterBrand {
80
+ /** Preloaded @odla-ai/ui theme name used by ThemeScope. */
81
+ theme?: string;
82
+ /** Admin/application color mode. Explicit light is the safe default. */
83
+ colorScheme?: "light" | "dark" | "system";
84
+ tokens?: ChapterBrandTokens;
85
+ tokensDark?: ChapterBrandTokens;
58
86
  /** Palette overrides written into styles.css :root (e.g. `{ moss: "#2F3E34" }`
59
87
  * or `--ui-*` token names). Applies in BOTH light and dark unless overridden
60
88
  * by {@link paletteDark}. */
@@ -88,7 +116,7 @@ interface ChapterNetworkTarget {
88
116
  id: string;
89
117
  /** Human-facing site name. Defaults to `id`. */
90
118
  name?: string;
91
- /** Absolute follower origin, e.g. `https://silver.example.com`. */
119
+ /** Absolute follower origin, e.g. `https://chapter.example.com`. */
92
120
  url: string;
93
121
  /** Leader-vault key holding this follower's share secret. Defaults to
94
122
  * `network_share_<id-with-underscores>`. */
@@ -159,10 +187,10 @@ interface ChapterScheduling {
159
187
  }
160
188
  /** How a signed-in user's role is determined.
161
189
  *
162
- * - `"claim"` reads a role ladder from a JWT claim (Silver & Salt's model:
190
+ * - `"claim"` reads a role ladder from a JWT claim (the member-site model:
163
191
  * `provisional → member → admin`). Admin is the last (highest) rung; the
164
192
  * read-only `superAdmins` tier sits above it.
165
- * - `"table"` gates on the deny-all `admins` allowlist (Built Not Found's model):
193
+ * - `"table"` gates on the deny-all `admins` allowlist (the hub model):
166
194
  * a binary "email is an admin or isn't", set only in odla Studio.
167
195
  *
168
196
  * Defaults: `"claim"` in `chapter` mode, `"table"` in `hub` mode. */
@@ -189,7 +217,7 @@ interface ResolvedAuth {
189
217
  }
190
218
  /** The application status pipeline. Which statuses exist, and the subsets a site
191
219
  * allows a call to be booked from / an application approved from. Defaults to
192
- * Silver & Salt's pipeline. Status never moves backwards (package-enforced). */
220
+ * the reference pipeline. Status never moves backwards (package-enforced). */
193
221
  interface ChapterPipeline {
194
222
  stages?: readonly string[];
195
223
  bookableFrom?: readonly string[];
@@ -206,8 +234,8 @@ interface ResolvedPipeline {
206
234
  }
207
235
  /** The application (join form) validation surface — which string fields are
208
236
  * 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
237
+ * submit validation + the CRM slot projection; defaults to the reference form.
238
+ * The `applications` schema attrs stay fixed; this is
211
239
  * validation config, not schema generation. */
212
240
  interface ChapterApplication {
213
241
  required?: readonly string[];
@@ -279,7 +307,7 @@ interface ChapterConfig {
279
307
  /** `notificationEmail` required in `chapter` mode. */
280
308
  emails?: ChapterEmails;
281
309
  scheduling?: ChapterScheduling;
282
- /** Application status pipeline (stages + bookable/approvable subsets). Defaults to S&S's. */
310
+ /** Application status pipeline (stages + bookable/approvable subsets). */
283
311
  pipeline?: ChapterPipeline;
284
312
  /** Join-form validation (required/optional fields, max lengths, body cap). */
285
313
  application?: ChapterApplication;
@@ -439,7 +467,7 @@ declare function createChapterIntegration(chapter: Chapter, options?: ChapterInt
439
467
  * `chapter` mode only. The auth tables follow {@link ResolvedAuth}: `source:
440
468
  * "table"` adds the `admins` allowlist (hub/BNF); `superAdmins` adds the
441
469
  * read-only super-admin tier (default on for the `"claim"` ladder). A `"claim"`
442
- * chapter therefore emits exactly Silver & Salt's namespace set — `applications`,
470
+ * chapter therefore emits the reference namespace set — `applications`,
443
471
  * `groups`, `meetings`, `emailLog`, `superAdmins` — with no `admins` table. */
444
472
  declare function chapterDb(mode: ChapterMode, auth: ResolvedAuth): {
445
473
  schema: DbSchema;
@@ -457,8 +485,8 @@ declare function buildGroupSeed(config: ChapterConfig): Record<string, unknown>;
457
485
  /**
458
486
  * Apply defaults + validate the auth config into a {@link ResolvedAuth}. Defaults
459
487
  * 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.
488
+ * `superAdmins` tier; `hub` → the `admins` allowlist table with no super tier.
489
+ * Throws at import on a bad policy.
462
490
  */
463
491
  declare function resolveAuth(mode: ChapterMode, auth: ChapterAuth | undefined): ResolvedAuth;
464
492
  /** The role from a verified JWT payload, per the resolved policy. An unknown or
@@ -633,8 +661,8 @@ declare function sendTemplated(deps: NotifyDeps, input: NotifyInput): Promise<No
633
661
  declare function emailGroupFrom(row: Record<string, unknown>): EmailGroup;
634
662
 
635
663
  /**
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
664
+ * Apply defaults + validate the pipeline config. With no config, the complete
665
+ * reference pipeline. With `stages` given but the subsets omitted, the subsets
638
666
  * default to empty (a site opts in to bookable/approvable states explicitly).
639
667
  * Throws at import on a bad pipeline (empty/duplicate stages, an initial or a
640
668
  * subset entry not on the ladder).
@@ -678,8 +706,7 @@ declare function paymentsReady(group: PaymentsGroup, hasSecretKey: boolean): boo
678
706
  * of nested objects into bracket syntax (`metadata[applicationId]=...`). */
679
707
  declare function stripeForm(params: Record<string, unknown>): string;
680
708
  /** 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). */
709
+ * per application, so a client retry can't orphan a second subscription. */
683
710
  declare function subscriptionIdempotencyKey(applicationId: string): string;
684
711
  /** The db mutationId for a webhook-driven write — exactly-once per Stripe event,
685
712
  * so replays are deduped at the db layer. */
@@ -941,6 +968,22 @@ declare function bucketSeries(points: Array<{
941
968
  weekStart: number;
942
969
  value: number;
943
970
  }>;
971
+ /** Labels and current/previous values for one dashboard comparison window. */
972
+ interface DashboardMetricSeries {
973
+ labels: string[];
974
+ current: number[];
975
+ previous: number[];
976
+ }
977
+ /** Week-over-week and month-over-month series consumed by MetricWidget. */
978
+ interface DashboardMetricData {
979
+ wow: DashboardMetricSeries;
980
+ mom: DashboardMetricSeries;
981
+ }
982
+ /** MetricWidget-ready daily and weekly comparison windows. */
983
+ declare function dashboardMetricData(points: Array<{
984
+ t: number;
985
+ v: number;
986
+ }>, now: number): DashboardMetricData;
944
987
  /** Annualized cents for a Stripe subscription: sum each item's
945
988
  * `unit_amount * quantity`, ×12 for monthly intervals. A yearly interval is
946
989
  * taken as-is. Returns 0 for a shape with no priced items. */
@@ -1183,7 +1226,9 @@ declare function memberSession(user: SessionUser, opts: {
1183
1226
  * theme. These are brand OVERRIDES on top of a base theme — they do not replace
1184
1227
  * the theme layer the components need (see {@link brandTokens} usage in the docs).
1185
1228
  */
1186
- declare function brandTokens(brand: ChapterBrand | undefined): string;
1229
+ declare function brandTokens(brand: ChapterBrand | undefined, options?: {
1230
+ selector?: string;
1231
+ }): string;
1187
1232
 
1188
1233
  /** A `meetings` row, as far as reconciliation cares. */
1189
1234
  interface MeetingForReconcile {
@@ -1235,13 +1280,13 @@ interface ResolvedScheduling {
1235
1280
  windowDays: number;
1236
1281
  summaryTemplate: string;
1237
1282
  }
1238
- /** The S&S-proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1283
+ /** Proven defaults: 45-minute weekday slots, 9–5 Pacific, 24h notice,
1239
1284
  * a 14-day window. The summary is generic (a chapter's group seed supplies a
1240
1285
  * name-branded one). */
1241
1286
  declare const SCHEDULING_DEFAULTS: ResolvedScheduling;
1242
1287
  /**
1243
1288
  * 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
1289
+ * bound at config-write time (throws on a bad config, so a
1245
1290
  * misconfiguration surfaces immediately rather than yielding empty slots).
1246
1291
  * `windowDays` is capped at 62 because Google FreeBusy is.
1247
1292
  */
@@ -1345,4 +1390,4 @@ type ApplicationBookingPatch = {
1345
1390
  * already there (never backward). */
1346
1391
  declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
1347
1392
 
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 };
1393
+ 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,44 @@ 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
+ chartPositive: "--ui-chart-pos",
1414
+ chartNegative: "--ui-chart-neg"
1415
+ };
1416
+ function semanticDecls(tokens) {
1417
+ return Object.entries(tokens ?? {}).flatMap(
1418
+ ([key, value]) => typeof value === "string" && value.trim() ? [`${TOKEN_VARS[key]}: ${cleanValue(value)};`] : []
1419
+ );
1420
+ }
1421
+ function brandTokens(brand, options = {}) {
1371
1422
  if (!brand) return "";
1372
- const light = paletteDecls(brand.palette);
1423
+ const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];
1373
1424
  const fonts = brand.fonts;
1374
1425
  if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);
1375
1426
  if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);
1376
1427
  if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);
1377
- const dark = paletteDecls(brand.paletteDark);
1378
- let css = light.length ? `:root {
1428
+ const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];
1429
+ const selector = options.selector ?? ":root";
1430
+ const darkSelector = selector === ":root" ? ':root[data-theme="dark"]' : `${selector}[data-theme="dark"]`;
1431
+ const systemSelector = selector === ":root" ? ':root:not([data-theme="light"])' : `${selector}:not([data-theme="light"])`;
1432
+ let css = light.length ? `${selector} {
1379
1433
  ${light.join("\n ")}
1380
1434
  }
1381
1435
  ` : "";
@@ -1383,9 +1437,9 @@ function brandTokens(brand) {
1383
1437
  const block = `{
1384
1438
  ${dark.join("\n ")}
1385
1439
  }`;
1386
- css += `:root[data-theme="dark"] ${block}
1440
+ css += `${darkSelector} ${block}
1387
1441
  @media (prefers-color-scheme: dark) {
1388
- :root:not([data-theme="light"]) ${block}
1442
+ ${systemSelector} ${block}
1389
1443
  }
1390
1444
  `;
1391
1445
  }
@@ -1558,6 +1612,7 @@ export {
1558
1612
  createChapterIntegration,
1559
1613
  createClerkInvitation,
1560
1614
  createClerkUser,
1615
+ dashboardMetricData,
1561
1616
  defaultCrm,
1562
1617
  defineChapter,
1563
1618
  emailGroupFrom,