@odla-ai/chapter 0.22.1 → 0.24.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-4COPR4SY.js → chunk-GMGTJIY3.js} +292 -97
- package/dist/chunk-GMGTJIY3.js.map +1 -0
- package/dist/chunk-WGQO4WDJ.js +412 -0
- package/dist/chunk-WGQO4WDJ.js.map +1 -0
- package/dist/{chunk-C4XQSRW7.js → chunk-ZPAJL7KG.js} +225 -190
- package/dist/chunk-ZPAJL7KG.js.map +1 -0
- package/dist/{types-CgWUQ2MH.d.ts → copy-context-Dp5hvj5W.d.ts} +85 -20
- package/dist/index.cjs +340 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +111 -24
- package/dist/index.d.ts +111 -24
- package/dist/index.js +340 -9
- 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 +274 -213
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +74 -19
- package/dist/worker/index.d.ts +74 -19
- package/dist/worker/index.js +274 -213
- 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,58 @@
|
|
|
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
|
+
/**
|
|
19
|
+
* `consent` is the affirmative statement a member agrees to before the card
|
|
20
|
+
* form appears, rendered beside the checkbox and beneath the policy text.
|
|
21
|
+
* It is what the member is shown at the moment of consent, so it says they
|
|
22
|
+
* agree rather than merely restating the policy. Set it to an empty string
|
|
23
|
+
* to render the checkbox with the policy as its only label.
|
|
24
|
+
*/
|
|
25
|
+
payment: TextFields<"setupFailed" | "preparing" | "pending" | "processing" | "payAndContinue" | "incomplete" | "consent">;
|
|
26
|
+
done: TextFields<"label" | "calendarInvite" | "memberArea">;
|
|
27
|
+
};
|
|
28
|
+
members: {
|
|
29
|
+
loadFailed: string;
|
|
30
|
+
account: TextFields<"signOut" | "adminConsole">;
|
|
31
|
+
provisional: TextFields<"cardLabel" | "applicationNeeded" | "applicationNeededBody" | "apply" | "refunded" | "refundedBody" | "active" | "renews" | "introductionCall" | "calendarInvite" | "joinCall" | "bookCall" | "bookCallBody" | "chooseTime">;
|
|
32
|
+
full: TextFields<"welcome">;
|
|
33
|
+
reschedule: TextFields<"changeTime" | "unavailable" | "loadFailed" | "slotGone" | "loading" | "noTimes" | "rescheduling" | "keepTime">;
|
|
34
|
+
};
|
|
35
|
+
admin: {
|
|
36
|
+
auth: TextFields<"checking" | "notAuthorized" | "accountNotAuthorized" | "thisAccount" | "signOut" | "loading" | "signInNotConfigured" | "missingPublishableKey" | "noWorkspaces" | "signInTagline">;
|
|
37
|
+
shell: TextFields<"adminRole" | "adminConsole" | "adminName" | "navigationLabel">;
|
|
38
|
+
workspaces: TextFields<"dashboard" | "overview" | "billing" | "people" | "settings" | "calendar" | "email" | "dashboardViewsLabel" | "settingsViewsLabel" | "collectionsLabel">;
|
|
39
|
+
dashboard: TextFields<"loading" | "loadFailed" | "upcomingCalls" | "callsNeedAttention" | "noUpcomingCalls" | "drift" | "open" | "meet" | "applications" | "newMembers" | "newMembersUnavailable" | "revenueAdded" | "revenue" | "revenueUnavailable" | "pipeline" | "pipelineLabel" | "thisWeek" | "noChange" | "activeMemberships" | "annualRunRate" | "testMode">;
|
|
40
|
+
billing: TextFields<"loading" | "loadFailed" | "notConfigured" | "active" | "annualized" | "renewingSoon" | "pastDue" | "subscriptions" | "testMode" | "truncated" | "name" | "email" | "application" | "subscription" | "cancelling" | "amount" | "renews">;
|
|
41
|
+
availability: TextFields<"loading" | "loadFailed" | "saved" | "title" | "days" | "startHour" | "endHour" | "slotMinutes" | "minNoticeHours" | "windowDays" | "timezone" | "summaryTemplate" | "save"> & {
|
|
42
|
+
dayLabels: readonly string[];
|
|
43
|
+
};
|
|
44
|
+
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">;
|
|
45
|
+
meetings: TextFields<"loading" | "loadFailed" | "cancelConfirm" | "newStartPrompt" | "parseFailed" | "agenda" | "empty" | "unknown" | "drift" | "meet" | "reschedule" | "cancel">;
|
|
46
|
+
network: TextFields<"title" | "allowlistDescription" | "sharing" | "shareWith" | "shared" | "shareFailed" | "deliveryFailed">;
|
|
47
|
+
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">;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
type DeepPartial<T> = T extends readonly (infer Item)[] ? readonly Item[] : T extends object ? {
|
|
51
|
+
[Key in keyof T]?: DeepPartial<T[Key]>;
|
|
52
|
+
} : T;
|
|
53
|
+
/** Recursively partial copy overrides accepted by `defineChapter({ copy })`. */
|
|
54
|
+
type ChapterCopyInput = DeepPartial<ChapterCopy>;
|
|
55
|
+
|
|
4
56
|
/** Which feature profile a site runs. `chapter` is the full public member site
|
|
5
57
|
* (join, Stripe membership, booking, member area, admin, CRM); `hub` is
|
|
6
58
|
* admin-only and CRM-focused (a directory/registry over the same CRM). */
|
|
@@ -72,6 +124,8 @@ interface ChapterBrandTokens {
|
|
|
72
124
|
interface ChapterBrand {
|
|
73
125
|
/** Preloaded @odla-ai/ui theme name used by ThemeScope. */
|
|
74
126
|
theme?: string;
|
|
127
|
+
/** Named accent family supplied by the selected @odla-ai/ui theme. */
|
|
128
|
+
accent?: string;
|
|
75
129
|
/** Admin/application color mode. Explicit light is the safe default. */
|
|
76
130
|
colorScheme?: "light" | "dark" | "system";
|
|
77
131
|
tokens?: ChapterBrandTokens;
|
|
@@ -84,6 +138,9 @@ interface ChapterBrand {
|
|
|
84
138
|
* `:root[data-theme="dark"]` and `@media (prefers-color-scheme: dark)`, so a
|
|
85
139
|
* site brands both modes. */
|
|
86
140
|
paletteDark?: Record<string, string>;
|
|
141
|
+
/** Full token map for `.ui-invert` islands. A compiled custom theme normally
|
|
142
|
+
* uses its dark map here so inverted content never inherits stale composites. */
|
|
143
|
+
paletteInvert?: Record<string, string>;
|
|
87
144
|
fonts?: {
|
|
88
145
|
display?: string;
|
|
89
146
|
body?: string;
|
|
@@ -239,17 +296,13 @@ interface ChapterApplication {
|
|
|
239
296
|
/** Max JSON request body in bytes. Default 32768. */
|
|
240
297
|
bodyCap?: number;
|
|
241
298
|
/** 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. */
|
|
299
|
+
* writing a row with no consent record. Default `true`. Set `false`
|
|
300
|
+
* deliberately only when the site renders no consent control. */
|
|
246
301
|
requireDisclaimerAck?: boolean;
|
|
247
302
|
/** 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. */
|
|
303
|
+
* `public_metadata.profile`. Default `[]`, so application details remain
|
|
304
|
+
* db-only. Set a curated list (e.g. `["phone", "state", "focus"]`) for
|
|
305
|
+
* fields the browser may read. */
|
|
253
306
|
profileFields?: readonly string[];
|
|
254
307
|
/** Extra application fields carried into the one-way CRM projection, on top of
|
|
255
308
|
* the built-in identity/contact set. Each MUST be declared on your crm person
|
|
@@ -273,8 +326,8 @@ interface ResolvedApplication {
|
|
|
273
326
|
defaultMaxLen: number;
|
|
274
327
|
bodyCap: number;
|
|
275
328
|
requireDisclaimerAck: boolean;
|
|
276
|
-
/** Resolved Clerk-metadata allowlist
|
|
277
|
-
profileFields: readonly string[]
|
|
329
|
+
/** Resolved Clerk-metadata allowlist. Empty means no profile projection. */
|
|
330
|
+
profileFields: readonly string[];
|
|
278
331
|
crmFields: readonly string[];
|
|
279
332
|
maxArrayLen: number;
|
|
280
333
|
validateEmail: boolean;
|
|
@@ -290,6 +343,9 @@ interface ChapterConfig {
|
|
|
290
343
|
/** A `defineCrm()` config or a resolved `Crm`. Omit for the per-mode default. */
|
|
291
344
|
crm?: CrmConfig | Crm;
|
|
292
345
|
brand?: ChapterBrand;
|
|
346
|
+
/** User-visible language for packaged Chapter surfaces. Recursively partial;
|
|
347
|
+
* `defineChapter()` resolves every omitted leaf from the package defaults. */
|
|
348
|
+
copy?: ChapterCopyInput;
|
|
293
349
|
/** Leader → follower delivery targets. Secret values stay in the leader
|
|
294
350
|
* tenant vault; see {@link ChapterNetworkTarget}. */
|
|
295
351
|
network?: ChapterNetwork;
|
|
@@ -308,13 +364,10 @@ interface ChapterConfig {
|
|
|
308
364
|
auth?: ChapterAuth;
|
|
309
365
|
/** odla services (db implied). Default `["db","calendar","o11y"]`. */
|
|
310
366
|
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. */
|
|
367
|
+
/** Apply-time account provisioning. Required in `chapter` mode so a site must
|
|
368
|
+
* make the decision explicitly. `"none"` provisions nothing, `"create"` makes
|
|
369
|
+
* the Clerk account server-side, and `"invite"` emails a real invitation.
|
|
370
|
+
* Both side-effecting models need a `clerk_secret_key` vault secret. */
|
|
318
371
|
account?: AccountModel;
|
|
319
372
|
/** WHEN lifecycle email fires. Addressing and content live on the group row
|
|
320
373
|
* (owner-editable at runtime); this is the trigger, which is a build-time
|
|
@@ -382,6 +435,8 @@ interface Chapter {
|
|
|
382
435
|
/** Resolved site identity. `wordmark` always falls back to `name`, so the
|
|
383
436
|
* admin/member UI never needs a second brand declaration. */
|
|
384
437
|
brand: ChapterBrand;
|
|
438
|
+
/** Fully resolved user-visible text for packaged Chapter surfaces. */
|
|
439
|
+
copy: ChapterCopy;
|
|
385
440
|
/** Validated follower targets for leader-driven record pushes. */
|
|
386
441
|
network: ResolvedNetwork;
|
|
387
442
|
/** Resolved CRM engine (from `defineCrm`). */
|
|
@@ -396,7 +451,7 @@ interface Chapter {
|
|
|
396
451
|
schema: DbSchema;
|
|
397
452
|
rules: DbRules;
|
|
398
453
|
services: readonly string[];
|
|
399
|
-
/** Resolved apply-time account provisioning model
|
|
454
|
+
/** Resolved apply-time account provisioning model. */
|
|
400
455
|
account: AccountModel;
|
|
401
456
|
/** Resolved send policy — when each lifecycle email fires. */
|
|
402
457
|
sends: ResolvedSends;
|
package/dist/worker/index.d.ts
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
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
|
+
/**
|
|
19
|
+
* `consent` is the affirmative statement a member agrees to before the card
|
|
20
|
+
* form appears, rendered beside the checkbox and beneath the policy text.
|
|
21
|
+
* It is what the member is shown at the moment of consent, so it says they
|
|
22
|
+
* agree rather than merely restating the policy. Set it to an empty string
|
|
23
|
+
* to render the checkbox with the policy as its only label.
|
|
24
|
+
*/
|
|
25
|
+
payment: TextFields<"setupFailed" | "preparing" | "pending" | "processing" | "payAndContinue" | "incomplete" | "consent">;
|
|
26
|
+
done: TextFields<"label" | "calendarInvite" | "memberArea">;
|
|
27
|
+
};
|
|
28
|
+
members: {
|
|
29
|
+
loadFailed: string;
|
|
30
|
+
account: TextFields<"signOut" | "adminConsole">;
|
|
31
|
+
provisional: TextFields<"cardLabel" | "applicationNeeded" | "applicationNeededBody" | "apply" | "refunded" | "refundedBody" | "active" | "renews" | "introductionCall" | "calendarInvite" | "joinCall" | "bookCall" | "bookCallBody" | "chooseTime">;
|
|
32
|
+
full: TextFields<"welcome">;
|
|
33
|
+
reschedule: TextFields<"changeTime" | "unavailable" | "loadFailed" | "slotGone" | "loading" | "noTimes" | "rescheduling" | "keepTime">;
|
|
34
|
+
};
|
|
35
|
+
admin: {
|
|
36
|
+
auth: TextFields<"checking" | "notAuthorized" | "accountNotAuthorized" | "thisAccount" | "signOut" | "loading" | "signInNotConfigured" | "missingPublishableKey" | "noWorkspaces" | "signInTagline">;
|
|
37
|
+
shell: TextFields<"adminRole" | "adminConsole" | "adminName" | "navigationLabel">;
|
|
38
|
+
workspaces: TextFields<"dashboard" | "overview" | "billing" | "people" | "settings" | "calendar" | "email" | "dashboardViewsLabel" | "settingsViewsLabel" | "collectionsLabel">;
|
|
39
|
+
dashboard: TextFields<"loading" | "loadFailed" | "upcomingCalls" | "callsNeedAttention" | "noUpcomingCalls" | "drift" | "open" | "meet" | "applications" | "newMembers" | "newMembersUnavailable" | "revenueAdded" | "revenue" | "revenueUnavailable" | "pipeline" | "pipelineLabel" | "thisWeek" | "noChange" | "activeMemberships" | "annualRunRate" | "testMode">;
|
|
40
|
+
billing: TextFields<"loading" | "loadFailed" | "notConfigured" | "active" | "annualized" | "renewingSoon" | "pastDue" | "subscriptions" | "testMode" | "truncated" | "name" | "email" | "application" | "subscription" | "cancelling" | "amount" | "renews">;
|
|
41
|
+
availability: TextFields<"loading" | "loadFailed" | "saved" | "title" | "days" | "startHour" | "endHour" | "slotMinutes" | "minNoticeHours" | "windowDays" | "timezone" | "summaryTemplate" | "save"> & {
|
|
42
|
+
dayLabels: readonly string[];
|
|
43
|
+
};
|
|
44
|
+
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">;
|
|
45
|
+
meetings: TextFields<"loading" | "loadFailed" | "cancelConfirm" | "newStartPrompt" | "parseFailed" | "agenda" | "empty" | "unknown" | "drift" | "meet" | "reschedule" | "cancel">;
|
|
46
|
+
network: TextFields<"title" | "allowlistDescription" | "sharing" | "shareWith" | "shared" | "shareFailed" | "deliveryFailed">;
|
|
47
|
+
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">;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
type DeepPartial<T> = T extends readonly (infer Item)[] ? readonly Item[] : T extends object ? {
|
|
51
|
+
[Key in keyof T]?: DeepPartial<T[Key]>;
|
|
52
|
+
} : T;
|
|
53
|
+
/** Recursively partial copy overrides accepted by `defineChapter({ copy })`. */
|
|
54
|
+
type ChapterCopyInput = DeepPartial<ChapterCopy>;
|
|
55
|
+
|
|
4
56
|
/** Which feature profile a site runs. `chapter` is the full public member site
|
|
5
57
|
* (join, Stripe membership, booking, member area, admin, CRM); `hub` is
|
|
6
58
|
* admin-only and CRM-focused (a directory/registry over the same CRM). */
|
|
@@ -72,6 +124,8 @@ interface ChapterBrandTokens {
|
|
|
72
124
|
interface ChapterBrand {
|
|
73
125
|
/** Preloaded @odla-ai/ui theme name used by ThemeScope. */
|
|
74
126
|
theme?: string;
|
|
127
|
+
/** Named accent family supplied by the selected @odla-ai/ui theme. */
|
|
128
|
+
accent?: string;
|
|
75
129
|
/** Admin/application color mode. Explicit light is the safe default. */
|
|
76
130
|
colorScheme?: "light" | "dark" | "system";
|
|
77
131
|
tokens?: ChapterBrandTokens;
|
|
@@ -84,6 +138,9 @@ interface ChapterBrand {
|
|
|
84
138
|
* `:root[data-theme="dark"]` and `@media (prefers-color-scheme: dark)`, so a
|
|
85
139
|
* site brands both modes. */
|
|
86
140
|
paletteDark?: Record<string, string>;
|
|
141
|
+
/** Full token map for `.ui-invert` islands. A compiled custom theme normally
|
|
142
|
+
* uses its dark map here so inverted content never inherits stale composites. */
|
|
143
|
+
paletteInvert?: Record<string, string>;
|
|
87
144
|
fonts?: {
|
|
88
145
|
display?: string;
|
|
89
146
|
body?: string;
|
|
@@ -239,17 +296,13 @@ interface ChapterApplication {
|
|
|
239
296
|
/** Max JSON request body in bytes. Default 32768. */
|
|
240
297
|
bodyCap?: number;
|
|
241
298
|
/** 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. */
|
|
299
|
+
* writing a row with no consent record. Default `true`. Set `false`
|
|
300
|
+
* deliberately only when the site renders no consent control. */
|
|
246
301
|
requireDisclaimerAck?: boolean;
|
|
247
302
|
/** 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. */
|
|
303
|
+
* `public_metadata.profile`. Default `[]`, so application details remain
|
|
304
|
+
* db-only. Set a curated list (e.g. `["phone", "state", "focus"]`) for
|
|
305
|
+
* fields the browser may read. */
|
|
253
306
|
profileFields?: readonly string[];
|
|
254
307
|
/** Extra application fields carried into the one-way CRM projection, on top of
|
|
255
308
|
* the built-in identity/contact set. Each MUST be declared on your crm person
|
|
@@ -273,8 +326,8 @@ interface ResolvedApplication {
|
|
|
273
326
|
defaultMaxLen: number;
|
|
274
327
|
bodyCap: number;
|
|
275
328
|
requireDisclaimerAck: boolean;
|
|
276
|
-
/** Resolved Clerk-metadata allowlist
|
|
277
|
-
profileFields: readonly string[]
|
|
329
|
+
/** Resolved Clerk-metadata allowlist. Empty means no profile projection. */
|
|
330
|
+
profileFields: readonly string[];
|
|
278
331
|
crmFields: readonly string[];
|
|
279
332
|
maxArrayLen: number;
|
|
280
333
|
validateEmail: boolean;
|
|
@@ -290,6 +343,9 @@ interface ChapterConfig {
|
|
|
290
343
|
/** A `defineCrm()` config or a resolved `Crm`. Omit for the per-mode default. */
|
|
291
344
|
crm?: CrmConfig | Crm;
|
|
292
345
|
brand?: ChapterBrand;
|
|
346
|
+
/** User-visible language for packaged Chapter surfaces. Recursively partial;
|
|
347
|
+
* `defineChapter()` resolves every omitted leaf from the package defaults. */
|
|
348
|
+
copy?: ChapterCopyInput;
|
|
293
349
|
/** Leader → follower delivery targets. Secret values stay in the leader
|
|
294
350
|
* tenant vault; see {@link ChapterNetworkTarget}. */
|
|
295
351
|
network?: ChapterNetwork;
|
|
@@ -308,13 +364,10 @@ interface ChapterConfig {
|
|
|
308
364
|
auth?: ChapterAuth;
|
|
309
365
|
/** odla services (db implied). Default `["db","calendar","o11y"]`. */
|
|
310
366
|
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. */
|
|
367
|
+
/** Apply-time account provisioning. Required in `chapter` mode so a site must
|
|
368
|
+
* make the decision explicitly. `"none"` provisions nothing, `"create"` makes
|
|
369
|
+
* the Clerk account server-side, and `"invite"` emails a real invitation.
|
|
370
|
+
* Both side-effecting models need a `clerk_secret_key` vault secret. */
|
|
318
371
|
account?: AccountModel;
|
|
319
372
|
/** WHEN lifecycle email fires. Addressing and content live on the group row
|
|
320
373
|
* (owner-editable at runtime); this is the trigger, which is a build-time
|
|
@@ -382,6 +435,8 @@ interface Chapter {
|
|
|
382
435
|
/** Resolved site identity. `wordmark` always falls back to `name`, so the
|
|
383
436
|
* admin/member UI never needs a second brand declaration. */
|
|
384
437
|
brand: ChapterBrand;
|
|
438
|
+
/** Fully resolved user-visible text for packaged Chapter surfaces. */
|
|
439
|
+
copy: ChapterCopy;
|
|
385
440
|
/** Validated follower targets for leader-driven record pushes. */
|
|
386
441
|
network: ResolvedNetwork;
|
|
387
442
|
/** Resolved CRM engine (from `defineCrm`). */
|
|
@@ -396,7 +451,7 @@ interface Chapter {
|
|
|
396
451
|
schema: DbSchema;
|
|
397
452
|
rules: DbRules;
|
|
398
453
|
services: readonly string[];
|
|
399
|
-
/** Resolved apply-time account provisioning model
|
|
454
|
+
/** Resolved apply-time account provisioning model. */
|
|
400
455
|
account: AccountModel;
|
|
401
456
|
/** Resolved send policy — when each lifecycle email fires. */
|
|
402
457
|
sends: ResolvedSends;
|