@odla-ai/chapter 0.22.1 → 0.23.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/README.md +99 -26
- package/dist/chunk-NTZSXAFT.js +411 -0
- package/dist/chunk-NTZSXAFT.js.map +1 -0
- package/dist/{chunk-4COPR4SY.js → chunk-OYXNI6VR.js} +266 -84
- package/dist/chunk-OYXNI6VR.js.map +1 -0
- package/dist/{chunk-C4XQSRW7.js → chunk-VCC4S6JU.js} +199 -171
- package/dist/chunk-VCC4S6JU.js.map +1 -0
- package/dist/{types-CgWUQ2MH.d.ts → copy-context-pDogTR4x.d.ts} +78 -20
- package/dist/index.cjs +322 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -24
- package/dist/index.d.ts +90 -24
- package/dist/index.js +322 -3
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +5 -2
- package/dist/ui/admin/index.js +8 -3
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +11 -5
- package/dist/ui/member/index.d.ts +118 -2
- package/dist/ui/member/index.js +10 -4
- package/dist/worker/index.cjs +180 -130
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +67 -19
- package/dist/worker/index.d.ts +67 -19
- package/dist/worker/index.js +180 -130
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/runbooks/adopt-existing.md +23 -5
- package/runbooks/greenfield.md +85 -4
- package/dist/chunk-3QKGKUTX.js +0 -91
- package/dist/chunk-3QKGKUTX.js.map +0 -1
- package/dist/chunk-4COPR4SY.js.map +0 -1
- package/dist/chunk-C4XQSRW7.js.map +0 -1
package/dist/worker/index.d.cts
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
import { CrmConfig, Crm } from '@odla-ai/crm';
|
|
2
2
|
import { initAdmin } from '@odla-ai/db';
|
|
3
3
|
|
|
4
|
+
type TextFields<Key extends string> = {
|
|
5
|
+
[Field in Key]: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Complete user-visible language owned by Chapter's packaged surfaces.
|
|
9
|
+
*
|
|
10
|
+
* Copy is serializable text; use render slots when presentation needs markup and
|
|
11
|
+
* `formatChapterCopy()` for typed placeholders.
|
|
12
|
+
*/
|
|
13
|
+
interface ChapterCopy {
|
|
14
|
+
common: TextFields<"loading" | "saved" | "unknown">;
|
|
15
|
+
join: {
|
|
16
|
+
form: TextFields<"submit" | "submitting" | "submitFailed" | "unexpectedFailure">;
|
|
17
|
+
booking: TextFields<"unavailable" | "loadFailed" | "slotTaken" | "failed" | "loading" | "book" | "booking">;
|
|
18
|
+
payment: TextFields<"setupFailed" | "preparing" | "pending" | "processing" | "payAndContinue" | "incomplete">;
|
|
19
|
+
done: TextFields<"label" | "calendarInvite" | "memberArea">;
|
|
20
|
+
};
|
|
21
|
+
members: {
|
|
22
|
+
loadFailed: string;
|
|
23
|
+
account: TextFields<"signOut" | "adminConsole">;
|
|
24
|
+
provisional: TextFields<"cardLabel" | "applicationNeeded" | "applicationNeededBody" | "apply" | "refunded" | "refundedBody" | "active" | "renews" | "introductionCall" | "calendarInvite" | "joinCall" | "bookCall" | "bookCallBody" | "chooseTime">;
|
|
25
|
+
full: TextFields<"welcome">;
|
|
26
|
+
reschedule: TextFields<"changeTime" | "unavailable" | "loadFailed" | "slotGone" | "loading" | "noTimes" | "rescheduling" | "keepTime">;
|
|
27
|
+
};
|
|
28
|
+
admin: {
|
|
29
|
+
auth: TextFields<"checking" | "notAuthorized" | "accountNotAuthorized" | "thisAccount" | "signOut" | "loading" | "signInNotConfigured" | "missingPublishableKey" | "noWorkspaces" | "signInTagline">;
|
|
30
|
+
shell: TextFields<"adminRole" | "adminConsole" | "adminName" | "navigationLabel">;
|
|
31
|
+
workspaces: TextFields<"dashboard" | "overview" | "billing" | "people" | "settings" | "calendar" | "email" | "dashboardViewsLabel" | "settingsViewsLabel" | "collectionsLabel">;
|
|
32
|
+
dashboard: TextFields<"loading" | "loadFailed" | "upcomingCalls" | "callsNeedAttention" | "noUpcomingCalls" | "drift" | "open" | "meet" | "applications" | "newMembers" | "newMembersUnavailable" | "revenueAdded" | "revenue" | "revenueUnavailable" | "pipeline" | "pipelineLabel" | "thisWeek" | "noChange" | "activeMemberships" | "annualRunRate" | "testMode">;
|
|
33
|
+
billing: TextFields<"loading" | "loadFailed" | "notConfigured" | "active" | "annualized" | "renewingSoon" | "pastDue" | "subscriptions" | "testMode" | "truncated" | "name" | "email" | "application" | "subscription" | "cancelling" | "amount" | "renews">;
|
|
34
|
+
availability: TextFields<"loading" | "loadFailed" | "saved" | "title" | "days" | "startHour" | "endHour" | "slotMinutes" | "minNoticeHours" | "windowDays" | "timezone" | "summaryTemplate" | "save"> & {
|
|
35
|
+
dayLabels: readonly string[];
|
|
36
|
+
};
|
|
37
|
+
email: TextFields<"loading" | "loadFailed" | "saved" | "sent" | "testFailed" | "delivery" | "notificationAddress" | "replyTo" | "debugInbox" | "debugInboxHint" | "sendTest" | "enabled" | "subject" | "body" | "save" | "sendLog" | "sentColumn" | "templateColumn" | "statusColumn" | "toColumn" | "failed" | "redirected" | "delivered">;
|
|
38
|
+
meetings: TextFields<"loading" | "loadFailed" | "cancelConfirm" | "newStartPrompt" | "parseFailed" | "agenda" | "empty" | "unknown" | "drift" | "meet" | "reschedule" | "cancel">;
|
|
39
|
+
network: TextFields<"title" | "allowlistDescription" | "sharing" | "shareWith" | "shared" | "shareFailed" | "deliveryFailed">;
|
|
40
|
+
records: TextFields<"workflowMissing" | "comms" | "commsHistory" | "scheduling" | "billing" | "notes" | "access" | "sharing" | "lifecycle" | "role" | "superAdminHint" | "noAccount" | "approve" | "refund" | "approved" | "refunded" | "roleSet" | "messageSent" | "messageNotSent" | "loadingTemplates" | "template" | "chooseTemplate" | "sending" | "send" | "noMessages" | "message" | "newStartPrompt" | "parseFailed" | "cancelConfirm" | "callCancelled" | "callRescheduled" | "noApplication" | "loadingMeetings" | "noMeetings" | "joinMeeting" | "openCalendar" | "reschedule" | "cancel">;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
type DeepPartial<T> = T extends readonly (infer Item)[] ? readonly Item[] : T extends object ? {
|
|
44
|
+
[Key in keyof T]?: DeepPartial<T[Key]>;
|
|
45
|
+
} : T;
|
|
46
|
+
/** Recursively partial copy overrides accepted by `defineChapter({ copy })`. */
|
|
47
|
+
type ChapterCopyInput = DeepPartial<ChapterCopy>;
|
|
48
|
+
|
|
4
49
|
/** Which feature profile a site runs. `chapter` is the full public member site
|
|
5
50
|
* (join, Stripe membership, booking, member area, admin, CRM); `hub` is
|
|
6
51
|
* admin-only and CRM-focused (a directory/registry over the same CRM). */
|
|
@@ -72,6 +117,8 @@ interface ChapterBrandTokens {
|
|
|
72
117
|
interface ChapterBrand {
|
|
73
118
|
/** Preloaded @odla-ai/ui theme name used by ThemeScope. */
|
|
74
119
|
theme?: string;
|
|
120
|
+
/** Named accent family supplied by the selected @odla-ai/ui theme. */
|
|
121
|
+
accent?: string;
|
|
75
122
|
/** Admin/application color mode. Explicit light is the safe default. */
|
|
76
123
|
colorScheme?: "light" | "dark" | "system";
|
|
77
124
|
tokens?: ChapterBrandTokens;
|
|
@@ -84,6 +131,9 @@ interface ChapterBrand {
|
|
|
84
131
|
* `:root[data-theme="dark"]` and `@media (prefers-color-scheme: dark)`, so a
|
|
85
132
|
* site brands both modes. */
|
|
86
133
|
paletteDark?: Record<string, string>;
|
|
134
|
+
/** Full token map for `.ui-invert` islands. A compiled custom theme normally
|
|
135
|
+
* uses its dark map here so inverted content never inherits stale composites. */
|
|
136
|
+
paletteInvert?: Record<string, string>;
|
|
87
137
|
fonts?: {
|
|
88
138
|
display?: string;
|
|
89
139
|
body?: string;
|
|
@@ -239,17 +289,13 @@ interface ChapterApplication {
|
|
|
239
289
|
/** Max JSON request body in bytes. Default 32768. */
|
|
240
290
|
bodyCap?: number;
|
|
241
291
|
/** Reject a submit that carries no truthy `disclaimerAck` (400), instead of
|
|
242
|
-
* writing a row with no consent record. Default `false`
|
|
243
|
-
*
|
|
244
|
-
* otherwise silent, permanent and unreconstructible. Failure is deterministic
|
|
245
|
-
* and surfaces on the first test submit, not intermittently in production. */
|
|
292
|
+
* writing a row with no consent record. Default `true`. Set `false`
|
|
293
|
+
* deliberately only when the site renders no consent control. */
|
|
246
294
|
requireDisclaimerAck?: boolean;
|
|
247
295
|
/** Allowlist of fields that reach the Clerk account's client-readable
|
|
248
|
-
* `public_metadata.profile`. Default
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* (e.g. `["phone", "state", "focus"]`) to keep confidential fields db-only.
|
|
252
|
-
* Expected to become required-in-spirit at 1.0. */
|
|
296
|
+
* `public_metadata.profile`. Default `[]`, so application details remain
|
|
297
|
+
* db-only. Set a curated list (e.g. `["phone", "state", "focus"]`) for
|
|
298
|
+
* fields the browser may read. */
|
|
253
299
|
profileFields?: readonly string[];
|
|
254
300
|
/** Extra application fields carried into the one-way CRM projection, on top of
|
|
255
301
|
* the built-in identity/contact set. Each MUST be declared on your crm person
|
|
@@ -273,8 +319,8 @@ interface ResolvedApplication {
|
|
|
273
319
|
defaultMaxLen: number;
|
|
274
320
|
bodyCap: number;
|
|
275
321
|
requireDisclaimerAck: boolean;
|
|
276
|
-
/** Resolved Clerk-metadata allowlist
|
|
277
|
-
profileFields: readonly string[]
|
|
322
|
+
/** Resolved Clerk-metadata allowlist. Empty means no profile projection. */
|
|
323
|
+
profileFields: readonly string[];
|
|
278
324
|
crmFields: readonly string[];
|
|
279
325
|
maxArrayLen: number;
|
|
280
326
|
validateEmail: boolean;
|
|
@@ -290,6 +336,9 @@ interface ChapterConfig {
|
|
|
290
336
|
/** A `defineCrm()` config or a resolved `Crm`. Omit for the per-mode default. */
|
|
291
337
|
crm?: CrmConfig | Crm;
|
|
292
338
|
brand?: ChapterBrand;
|
|
339
|
+
/** User-visible language for packaged Chapter surfaces. Recursively partial;
|
|
340
|
+
* `defineChapter()` resolves every omitted leaf from the package defaults. */
|
|
341
|
+
copy?: ChapterCopyInput;
|
|
293
342
|
/** Leader → follower delivery targets. Secret values stay in the leader
|
|
294
343
|
* tenant vault; see {@link ChapterNetworkTarget}. */
|
|
295
344
|
network?: ChapterNetwork;
|
|
@@ -308,13 +357,10 @@ interface ChapterConfig {
|
|
|
308
357
|
auth?: ChapterAuth;
|
|
309
358
|
/** odla services (db implied). Default `["db","calendar","o11y"]`. */
|
|
310
359
|
services?: readonly string[];
|
|
311
|
-
/** Apply-time account provisioning.
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* `"invite"` **emails the applicant a Clerk invitation**. Both non-default
|
|
316
|
-
* models need a `clerk_secret_key` vault secret to act. Opt in explicitly —
|
|
317
|
-
* leaving this unset provisions no accounts. */
|
|
360
|
+
/** Apply-time account provisioning. Required in `chapter` mode so a site must
|
|
361
|
+
* make the decision explicitly. `"none"` provisions nothing, `"create"` makes
|
|
362
|
+
* the Clerk account server-side, and `"invite"` emails a real invitation.
|
|
363
|
+
* Both side-effecting models need a `clerk_secret_key` vault secret. */
|
|
318
364
|
account?: AccountModel;
|
|
319
365
|
/** WHEN lifecycle email fires. Addressing and content live on the group row
|
|
320
366
|
* (owner-editable at runtime); this is the trigger, which is a build-time
|
|
@@ -382,6 +428,8 @@ interface Chapter {
|
|
|
382
428
|
/** Resolved site identity. `wordmark` always falls back to `name`, so the
|
|
383
429
|
* admin/member UI never needs a second brand declaration. */
|
|
384
430
|
brand: ChapterBrand;
|
|
431
|
+
/** Fully resolved user-visible text for packaged Chapter surfaces. */
|
|
432
|
+
copy: ChapterCopy;
|
|
385
433
|
/** Validated follower targets for leader-driven record pushes. */
|
|
386
434
|
network: ResolvedNetwork;
|
|
387
435
|
/** Resolved CRM engine (from `defineCrm`). */
|
|
@@ -396,7 +444,7 @@ interface Chapter {
|
|
|
396
444
|
schema: DbSchema;
|
|
397
445
|
rules: DbRules;
|
|
398
446
|
services: readonly string[];
|
|
399
|
-
/** Resolved apply-time account provisioning model
|
|
447
|
+
/** Resolved apply-time account provisioning model. */
|
|
400
448
|
account: AccountModel;
|
|
401
449
|
/** Resolved send policy — when each lifecycle email fires. */
|
|
402
450
|
sends: ResolvedSends;
|
package/dist/worker/index.d.ts
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
import { CrmConfig, Crm } from '@odla-ai/crm';
|
|
2
2
|
import { initAdmin } from '@odla-ai/db';
|
|
3
3
|
|
|
4
|
+
type TextFields<Key extends string> = {
|
|
5
|
+
[Field in Key]: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Complete user-visible language owned by Chapter's packaged surfaces.
|
|
9
|
+
*
|
|
10
|
+
* Copy is serializable text; use render slots when presentation needs markup and
|
|
11
|
+
* `formatChapterCopy()` for typed placeholders.
|
|
12
|
+
*/
|
|
13
|
+
interface ChapterCopy {
|
|
14
|
+
common: TextFields<"loading" | "saved" | "unknown">;
|
|
15
|
+
join: {
|
|
16
|
+
form: TextFields<"submit" | "submitting" | "submitFailed" | "unexpectedFailure">;
|
|
17
|
+
booking: TextFields<"unavailable" | "loadFailed" | "slotTaken" | "failed" | "loading" | "book" | "booking">;
|
|
18
|
+
payment: TextFields<"setupFailed" | "preparing" | "pending" | "processing" | "payAndContinue" | "incomplete">;
|
|
19
|
+
done: TextFields<"label" | "calendarInvite" | "memberArea">;
|
|
20
|
+
};
|
|
21
|
+
members: {
|
|
22
|
+
loadFailed: string;
|
|
23
|
+
account: TextFields<"signOut" | "adminConsole">;
|
|
24
|
+
provisional: TextFields<"cardLabel" | "applicationNeeded" | "applicationNeededBody" | "apply" | "refunded" | "refundedBody" | "active" | "renews" | "introductionCall" | "calendarInvite" | "joinCall" | "bookCall" | "bookCallBody" | "chooseTime">;
|
|
25
|
+
full: TextFields<"welcome">;
|
|
26
|
+
reschedule: TextFields<"changeTime" | "unavailable" | "loadFailed" | "slotGone" | "loading" | "noTimes" | "rescheduling" | "keepTime">;
|
|
27
|
+
};
|
|
28
|
+
admin: {
|
|
29
|
+
auth: TextFields<"checking" | "notAuthorized" | "accountNotAuthorized" | "thisAccount" | "signOut" | "loading" | "signInNotConfigured" | "missingPublishableKey" | "noWorkspaces" | "signInTagline">;
|
|
30
|
+
shell: TextFields<"adminRole" | "adminConsole" | "adminName" | "navigationLabel">;
|
|
31
|
+
workspaces: TextFields<"dashboard" | "overview" | "billing" | "people" | "settings" | "calendar" | "email" | "dashboardViewsLabel" | "settingsViewsLabel" | "collectionsLabel">;
|
|
32
|
+
dashboard: TextFields<"loading" | "loadFailed" | "upcomingCalls" | "callsNeedAttention" | "noUpcomingCalls" | "drift" | "open" | "meet" | "applications" | "newMembers" | "newMembersUnavailable" | "revenueAdded" | "revenue" | "revenueUnavailable" | "pipeline" | "pipelineLabel" | "thisWeek" | "noChange" | "activeMemberships" | "annualRunRate" | "testMode">;
|
|
33
|
+
billing: TextFields<"loading" | "loadFailed" | "notConfigured" | "active" | "annualized" | "renewingSoon" | "pastDue" | "subscriptions" | "testMode" | "truncated" | "name" | "email" | "application" | "subscription" | "cancelling" | "amount" | "renews">;
|
|
34
|
+
availability: TextFields<"loading" | "loadFailed" | "saved" | "title" | "days" | "startHour" | "endHour" | "slotMinutes" | "minNoticeHours" | "windowDays" | "timezone" | "summaryTemplate" | "save"> & {
|
|
35
|
+
dayLabels: readonly string[];
|
|
36
|
+
};
|
|
37
|
+
email: TextFields<"loading" | "loadFailed" | "saved" | "sent" | "testFailed" | "delivery" | "notificationAddress" | "replyTo" | "debugInbox" | "debugInboxHint" | "sendTest" | "enabled" | "subject" | "body" | "save" | "sendLog" | "sentColumn" | "templateColumn" | "statusColumn" | "toColumn" | "failed" | "redirected" | "delivered">;
|
|
38
|
+
meetings: TextFields<"loading" | "loadFailed" | "cancelConfirm" | "newStartPrompt" | "parseFailed" | "agenda" | "empty" | "unknown" | "drift" | "meet" | "reschedule" | "cancel">;
|
|
39
|
+
network: TextFields<"title" | "allowlistDescription" | "sharing" | "shareWith" | "shared" | "shareFailed" | "deliveryFailed">;
|
|
40
|
+
records: TextFields<"workflowMissing" | "comms" | "commsHistory" | "scheduling" | "billing" | "notes" | "access" | "sharing" | "lifecycle" | "role" | "superAdminHint" | "noAccount" | "approve" | "refund" | "approved" | "refunded" | "roleSet" | "messageSent" | "messageNotSent" | "loadingTemplates" | "template" | "chooseTemplate" | "sending" | "send" | "noMessages" | "message" | "newStartPrompt" | "parseFailed" | "cancelConfirm" | "callCancelled" | "callRescheduled" | "noApplication" | "loadingMeetings" | "noMeetings" | "joinMeeting" | "openCalendar" | "reschedule" | "cancel">;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
type DeepPartial<T> = T extends readonly (infer Item)[] ? readonly Item[] : T extends object ? {
|
|
44
|
+
[Key in keyof T]?: DeepPartial<T[Key]>;
|
|
45
|
+
} : T;
|
|
46
|
+
/** Recursively partial copy overrides accepted by `defineChapter({ copy })`. */
|
|
47
|
+
type ChapterCopyInput = DeepPartial<ChapterCopy>;
|
|
48
|
+
|
|
4
49
|
/** Which feature profile a site runs. `chapter` is the full public member site
|
|
5
50
|
* (join, Stripe membership, booking, member area, admin, CRM); `hub` is
|
|
6
51
|
* admin-only and CRM-focused (a directory/registry over the same CRM). */
|
|
@@ -72,6 +117,8 @@ interface ChapterBrandTokens {
|
|
|
72
117
|
interface ChapterBrand {
|
|
73
118
|
/** Preloaded @odla-ai/ui theme name used by ThemeScope. */
|
|
74
119
|
theme?: string;
|
|
120
|
+
/** Named accent family supplied by the selected @odla-ai/ui theme. */
|
|
121
|
+
accent?: string;
|
|
75
122
|
/** Admin/application color mode. Explicit light is the safe default. */
|
|
76
123
|
colorScheme?: "light" | "dark" | "system";
|
|
77
124
|
tokens?: ChapterBrandTokens;
|
|
@@ -84,6 +131,9 @@ interface ChapterBrand {
|
|
|
84
131
|
* `:root[data-theme="dark"]` and `@media (prefers-color-scheme: dark)`, so a
|
|
85
132
|
* site brands both modes. */
|
|
86
133
|
paletteDark?: Record<string, string>;
|
|
134
|
+
/** Full token map for `.ui-invert` islands. A compiled custom theme normally
|
|
135
|
+
* uses its dark map here so inverted content never inherits stale composites. */
|
|
136
|
+
paletteInvert?: Record<string, string>;
|
|
87
137
|
fonts?: {
|
|
88
138
|
display?: string;
|
|
89
139
|
body?: string;
|
|
@@ -239,17 +289,13 @@ interface ChapterApplication {
|
|
|
239
289
|
/** Max JSON request body in bytes. Default 32768. */
|
|
240
290
|
bodyCap?: number;
|
|
241
291
|
/** Reject a submit that carries no truthy `disclaimerAck` (400), instead of
|
|
242
|
-
* writing a row with no consent record. Default `false`
|
|
243
|
-
*
|
|
244
|
-
* otherwise silent, permanent and unreconstructible. Failure is deterministic
|
|
245
|
-
* and surfaces on the first test submit, not intermittently in production. */
|
|
292
|
+
* writing a row with no consent record. Default `true`. Set `false`
|
|
293
|
+
* deliberately only when the site renders no consent control. */
|
|
246
294
|
requireDisclaimerAck?: boolean;
|
|
247
295
|
/** Allowlist of fields that reach the Clerk account's client-readable
|
|
248
|
-
* `public_metadata.profile`. Default
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* (e.g. `["phone", "state", "focus"]`) to keep confidential fields db-only.
|
|
252
|
-
* Expected to become required-in-spirit at 1.0. */
|
|
296
|
+
* `public_metadata.profile`. Default `[]`, so application details remain
|
|
297
|
+
* db-only. Set a curated list (e.g. `["phone", "state", "focus"]`) for
|
|
298
|
+
* fields the browser may read. */
|
|
253
299
|
profileFields?: readonly string[];
|
|
254
300
|
/** Extra application fields carried into the one-way CRM projection, on top of
|
|
255
301
|
* the built-in identity/contact set. Each MUST be declared on your crm person
|
|
@@ -273,8 +319,8 @@ interface ResolvedApplication {
|
|
|
273
319
|
defaultMaxLen: number;
|
|
274
320
|
bodyCap: number;
|
|
275
321
|
requireDisclaimerAck: boolean;
|
|
276
|
-
/** Resolved Clerk-metadata allowlist
|
|
277
|
-
profileFields: readonly string[]
|
|
322
|
+
/** Resolved Clerk-metadata allowlist. Empty means no profile projection. */
|
|
323
|
+
profileFields: readonly string[];
|
|
278
324
|
crmFields: readonly string[];
|
|
279
325
|
maxArrayLen: number;
|
|
280
326
|
validateEmail: boolean;
|
|
@@ -290,6 +336,9 @@ interface ChapterConfig {
|
|
|
290
336
|
/** A `defineCrm()` config or a resolved `Crm`. Omit for the per-mode default. */
|
|
291
337
|
crm?: CrmConfig | Crm;
|
|
292
338
|
brand?: ChapterBrand;
|
|
339
|
+
/** User-visible language for packaged Chapter surfaces. Recursively partial;
|
|
340
|
+
* `defineChapter()` resolves every omitted leaf from the package defaults. */
|
|
341
|
+
copy?: ChapterCopyInput;
|
|
293
342
|
/** Leader → follower delivery targets. Secret values stay in the leader
|
|
294
343
|
* tenant vault; see {@link ChapterNetworkTarget}. */
|
|
295
344
|
network?: ChapterNetwork;
|
|
@@ -308,13 +357,10 @@ interface ChapterConfig {
|
|
|
308
357
|
auth?: ChapterAuth;
|
|
309
358
|
/** odla services (db implied). Default `["db","calendar","o11y"]`. */
|
|
310
359
|
services?: readonly string[];
|
|
311
|
-
/** Apply-time account provisioning.
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* `"invite"` **emails the applicant a Clerk invitation**. Both non-default
|
|
316
|
-
* models need a `clerk_secret_key` vault secret to act. Opt in explicitly —
|
|
317
|
-
* leaving this unset provisions no accounts. */
|
|
360
|
+
/** Apply-time account provisioning. Required in `chapter` mode so a site must
|
|
361
|
+
* make the decision explicitly. `"none"` provisions nothing, `"create"` makes
|
|
362
|
+
* the Clerk account server-side, and `"invite"` emails a real invitation.
|
|
363
|
+
* Both side-effecting models need a `clerk_secret_key` vault secret. */
|
|
318
364
|
account?: AccountModel;
|
|
319
365
|
/** WHEN lifecycle email fires. Addressing and content live on the group row
|
|
320
366
|
* (owner-editable at runtime); this is the trigger, which is a build-time
|
|
@@ -382,6 +428,8 @@ interface Chapter {
|
|
|
382
428
|
/** Resolved site identity. `wordmark` always falls back to `name`, so the
|
|
383
429
|
* admin/member UI never needs a second brand declaration. */
|
|
384
430
|
brand: ChapterBrand;
|
|
431
|
+
/** Fully resolved user-visible text for packaged Chapter surfaces. */
|
|
432
|
+
copy: ChapterCopy;
|
|
385
433
|
/** Validated follower targets for leader-driven record pushes. */
|
|
386
434
|
network: ResolvedNetwork;
|
|
387
435
|
/** Resolved CRM engine (from `defineCrm`). */
|
|
@@ -396,7 +444,7 @@ interface Chapter {
|
|
|
396
444
|
schema: DbSchema;
|
|
397
445
|
rules: DbRules;
|
|
398
446
|
services: readonly string[];
|
|
399
|
-
/** Resolved apply-time account provisioning model
|
|
447
|
+
/** Resolved apply-time account provisioning model. */
|
|
400
448
|
account: AccountModel;
|
|
401
449
|
/** Resolved send policy — when each lifecycle email fires. */
|
|
402
450
|
sends: ResolvedSends;
|