@odla-ai/chapter 0.51.0 → 0.52.1
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 +110 -8
- package/dist/{chunk-JXX4BFDP.js → chunk-4M2PN5K3.js} +8 -2
- package/dist/chunk-4M2PN5K3.js.map +1 -0
- package/dist/{chunk-UAXWA5BL.js → chunk-TEHCORNC.js} +31 -12
- package/dist/chunk-TEHCORNC.js.map +1 -0
- package/dist/{chunk-4EA5V4JE.js → chunk-WWF2TO3H.js} +239 -136
- package/dist/chunk-WWF2TO3H.js.map +1 -0
- package/dist/{copy-context-Ddat__H5.d.ts → copy-context-qvFo6eA7.d.ts} +12 -6
- package/dist/index.cjs +91 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -13
- package/dist/index.d.ts +71 -13
- package/dist/index.js +91 -8
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +14 -3
- package/dist/ui/admin/index.js +4 -2
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +5 -3
- package/dist/ui/member/index.d.ts +20 -14
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.cjs +912 -354
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +48 -10
- package/dist/worker/index.d.ts +48 -10
- package/dist/worker/index.js +912 -354
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-4EA5V4JE.js.map +0 -1
- package/dist/chunk-JXX4BFDP.js.map +0 -1
- package/dist/chunk-UAXWA5BL.js.map +0 -1
package/README.md
CHANGED
|
@@ -185,6 +185,25 @@ This carries the compiler's complete light map, derived dark map, and invert
|
|
|
185
185
|
map into Chapter without adding an `@odla-ai/brand` runtime dependency to
|
|
186
186
|
Chapter itself.
|
|
187
187
|
|
|
188
|
+
#### Packaged workspaces are styled by Chapter, through `@odla-ai/ui`
|
|
189
|
+
|
|
190
|
+
Chapter owns the styling of every workspace it ships (Admission, Builder
|
|
191
|
+
Library, People, Settings, and the rest) and does it only through `@odla-ai/ui`
|
|
192
|
+
form components and the `--ui-*` tokens the active theme scope supplies. A
|
|
193
|
+
packaged picker is always the `@odla-ai/ui` `Select`, a native select carrying
|
|
194
|
+
the `select` class, so it takes the host's theme without any host CSS, keeps
|
|
195
|
+
its accessible name through its `Field` label, and keeps native keyboard
|
|
196
|
+
behaviour. A guard test in the package fails if a bare select tag reappears in
|
|
197
|
+
a Chapter UI source.
|
|
198
|
+
|
|
199
|
+
The boundary runs the other way too. A host must not patch package-owned
|
|
200
|
+
markup with its own CSS: if a packaged control looks browser-default, the theme
|
|
201
|
+
layer is missing (see the banner above) or the package has a bug to report,
|
|
202
|
+
never a selector to override. Any custom picker a host adds beside a packaged
|
|
203
|
+
workspace should be the same `@odla-ai/ui` `Select` (with `Field` for its
|
|
204
|
+
label) rather than a bare element, so host and package controls read as one
|
|
205
|
+
surface.
|
|
206
|
+
|
|
188
207
|
## API quick start
|
|
189
208
|
|
|
190
209
|
```ts
|
|
@@ -932,6 +951,32 @@ removing it changes no customer-facing behavior. The BNF frontend may be down
|
|
|
932
951
|
while the webhook continues to ingest. Registering a Stripe endpoint and
|
|
933
952
|
enabling production remain separate reviewed operations.
|
|
934
953
|
|
|
954
|
+
A second delivery of a retained event id is a replay when its digest matches, or
|
|
955
|
+
when its canonical fact (id, type, created, and the allowlisted `data.object`
|
|
956
|
+
fields) equals the retained fact however the delivery envelope changed. A
|
|
957
|
+
canonical replay also resolves an open collision for that event, recording a
|
|
958
|
+
`paymentRecoveryReviews` row with the system as actor, unless the collision
|
|
959
|
+
recorded a different fact. Only a delivery whose fact differs registers a
|
|
960
|
+
collision, and it stays active through rebuilds until an operator decides:
|
|
961
|
+
`POST` with `{ command: "resolve", eventId, mutationId, reason }` records the
|
|
962
|
+
actor and reason, dismisses the colliding delivery so the same one replays
|
|
963
|
+
quietly, and keeps the retained fact as the evidence of record; it refuses
|
|
964
|
+
without a valid `mutationId` and a reason. `POST` with
|
|
965
|
+
`{ command: "recover-event", eventId }` instead trusts an independent read of
|
|
966
|
+
the provider Event and rewrites the retained digest when every fact matches.
|
|
967
|
+
|
|
968
|
+
`/api/admin/payment-recovery` is the drain on both sides. A chapter lists and
|
|
969
|
+
retries its quarantined Stripe receipts; a hub that owns the read model lists
|
|
970
|
+
every active commercial exception (`count` plus provider ids, no PII) and
|
|
971
|
+
retries one through the same ingest the signed webhook uses after an
|
|
972
|
+
independent provider read, with a review row per retry.
|
|
973
|
+
|
|
974
|
+
Both Workers raise an error-level operator alert when a receipt enters
|
|
975
|
+
quarantine or a commercial exception becomes active, carrying the current count
|
|
976
|
+
and provider event ids only. Pass `recordError` from `@odla-ai/o11y` as
|
|
977
|
+
`chapterWorker({ chapter, recordError })` to deliver those alerts to o11y; the
|
|
978
|
+
default is one structured `console.error` line per alert.
|
|
979
|
+
|
|
935
980
|
`POST` with `{ command: "reconcile-chapters" }` is a separate, two-sided opt-in
|
|
936
981
|
comparison. A hub target and the matching follower reader must both set
|
|
937
982
|
`commercialParity: true`; neither record browsing nor Stripe shadow mode grants
|
|
@@ -1107,10 +1152,15 @@ name is public configuration, not a credential. If a configured binding is
|
|
|
1107
1152
|
missing at runtime, the target fails explicitly instead of falling back to a
|
|
1108
1153
|
potentially misrouted public fetch.
|
|
1109
1154
|
|
|
1110
|
-
|
|
1111
|
-
`network_share_example_chapter` in the leader (or
|
|
1112
|
-
`secretName`)
|
|
1113
|
-
|
|
1155
|
+
Pair the edge by vaulting one generated value as `network_share_secret` in the
|
|
1156
|
+
follower and as `network_share_example_chapter` in the leader (or the target's
|
|
1157
|
+
`secretName`), never printed. The integration descriptor declares both slots,
|
|
1158
|
+
so provisioning tooling can hold them against the vault, and until both hold a
|
|
1159
|
+
value every leader-side surface reports the edge as `code: "edge_unpaired"`
|
|
1160
|
+
with a `hint` naming the CLI pairing command (`chapter edge pair`, which
|
|
1161
|
+
writes both slots from one generated value and ships with the next
|
|
1162
|
+
`@odla-ai/cli` release). No secret is placed in source, browser data, or
|
|
1163
|
+
provisioning config. The standard collection drawer discovers the target and calls the
|
|
1114
1164
|
admin-gated push route. A follower must declare the receiving CRM type/fields;
|
|
1115
1165
|
otherwise it rejects the record cleanly instead of dropping fields.
|
|
1116
1166
|
|
|
@@ -1234,8 +1284,11 @@ Use distinct development tenants and follower origins for the first delivery:
|
|
|
1234
1284
|
|
|
1235
1285
|
1. Confirm every target `id` exactly equals the follower's `chapter.id`. For
|
|
1236
1286
|
Cloudflare Workers, confirm its `binding` exists in the leader's development
|
|
1237
|
-
Wrangler environment.
|
|
1238
|
-
the follower and under the target's resolved
|
|
1287
|
+
Wrangler environment. Pair the edge: vault one generated value as
|
|
1288
|
+
`network_share_secret` in the follower and under the target's resolved
|
|
1289
|
+
`secretName` in the leader (the CLI's `chapter edge pair` command does both
|
|
1290
|
+
writes from the next `@odla-ai/cli` release on). The leader rollup reports
|
|
1291
|
+
`edge_unpaired` until both slots hold a value.
|
|
1239
1292
|
2. Confirm `GET /api/admin/network/targets` lists the development follower with
|
|
1240
1293
|
the expected compatible record types.
|
|
1241
1294
|
3. Share one test person and one test business from the leader's record drawer.
|
|
@@ -1464,13 +1517,38 @@ These bite silently — a smoke test won't catch them:
|
|
|
1464
1517
|
| Template | Fires from | Configurable |
|
|
1465
1518
|
|---|---|---|
|
|
1466
1519
|
| `adminNotification` | `POST /api/applications` (submit) — or the Stripe webhook (first payment) | `sends.adminNotification: "submit" \| "payment" \| "never"` (default `"submit"`) |
|
|
1467
|
-
| `
|
|
1520
|
+
| `submitConfirmation` | `POST /api/applications` (submit) — a free tier always; a paid tier only when `paymentConfirmation` is disabled, so a paid applicant is confirmed once | template `enabled` flag |
|
|
1521
|
+
| `prepEmail` | `POST /api/schedule/book` — once per application; a reschedule sends no second one | template `enabled` flag |
|
|
1468
1522
|
| `paymentConfirmation` | Stripe webhook, first successful invoice | template `enabled` flag |
|
|
1523
|
+
| `seatAccepted` | `POST /api/named-seat/accept` — to the seat's purchaser, once per seat, naming the recipient and tier | template `enabled` flag; the seeded default copy applies until the group row carries its own `emailTemplates.seatAccepted` |
|
|
1469
1524
|
|
|
1470
1525
|
```ts
|
|
1471
1526
|
defineChapter({ /* … */ sends: { adminNotification: "payment" } });
|
|
1472
1527
|
```
|
|
1473
1528
|
|
|
1529
|
+
**Template variables.** Every value comes from stored data — the frozen
|
|
1530
|
+
checkout the applicant consented to, the tier catalog, the booked meeting,
|
|
1531
|
+
the request origin — never from a price or a name written into code. An
|
|
1532
|
+
unknown placeholder renders empty, so copy can use any subset.
|
|
1533
|
+
|
|
1534
|
+
| Variable | Templates | Value |
|
|
1535
|
+
|---|---|---|
|
|
1536
|
+
| `firstName`, `lastName`, `email`, `phone`, `state` | all | the application row |
|
|
1537
|
+
| `tier`, `tierName` | `adminNotification`, `submitConfirmation`, `prepEmail` | the frozen checkout quote's tier name, else the catalog name for `applications.tierId` |
|
|
1538
|
+
| `paymentAmount`, `paymentAmountSummary` | `submitConfirmation`, `paymentConfirmation`, `prepEmail` | `$900.00` and ` of $900.00` from the frozen checkout; both blank for a free tier or before checkout |
|
|
1539
|
+
| `submitNextStep` | `submitConfirmation` | "Next, schedule your introduction call." / "Next, complete your membership payment." / "Your membership is approved." |
|
|
1540
|
+
| `paymentNextStep` | `paymentConfirmation` | the post-payment step (schedule, waived, or approved) |
|
|
1541
|
+
| `startAt`, `endAt`, `timezone` | `prepEmail` | the booked call rendered in the group's `scheduling.timezone`, e.g. `Wednesday, October 7, 2026 at 10:00 AM EDT` |
|
|
1542
|
+
| `meetUrl`, `htmlLink` | `prepEmail` | the booked meeting's Google Meet and calendar links (blank when the provider returned none) |
|
|
1543
|
+
| `meetingTime`, `meetingLink` | `prepEmail` | aliases of `startAt` and `meetUrl` (falling back to `htmlLink`) for copy written before those existed |
|
|
1544
|
+
| `adminUrl`, `membersUrl` | `adminNotification`, `submitConfirmation`, `onboardingInvite` | `<origin>/admin/` and `<origin>/members/` from the request that fired the send |
|
|
1545
|
+
| `refundPolicyText`, `commitmentText`, `normsText` | all | the group row's policy copy |
|
|
1546
|
+
|
|
1547
|
+
**From display name.** `emails.fromName` (seeded to the group row, editable
|
|
1548
|
+
in Settings → Email) turns the From header into `{ name, email }` —
|
|
1549
|
+
`Tori Horton <tori@…>` in an inbox — while the address stays the Worker's
|
|
1550
|
+
`EMAIL_FROM`. A group with no name sends the bare address exactly as before.
|
|
1551
|
+
|
|
1474
1552
|
**Re-check this on every chapter upgrade.** Wiring a send changes a site's
|
|
1475
1553
|
outbound mail with no local diff — release notes call out send changes
|
|
1476
1554
|
explicitly for that reason.
|
|
@@ -1634,7 +1712,11 @@ These bite silently — a smoke test won't catch them:
|
|
|
1634
1712
|
`drainMembershipEffects` recovers pending provider work and cancels unpaid
|
|
1635
1713
|
seats within one exact configured runtime. It never refunds an unaccepted
|
|
1636
1714
|
seat: a purchased seat waits for its recipient for the whole paid term, and a
|
|
1637
|
-
refund is a super admin's decision on request.
|
|
1715
|
+
refund is a super admin's decision on request. When the recipient accepts,
|
|
1716
|
+
the chapter emails the purchaser one `seatAccepted` notice with the
|
|
1717
|
+
recipient's name and tier, deduplicated per seat through the email log; a
|
|
1718
|
+
chapter edits or disables it on its group row, and the default copy applies
|
|
1719
|
+
until then. Refunding the recipient's own
|
|
1638
1720
|
application (`POST /api/admin/applications/:id/refund`) requires a super
|
|
1639
1721
|
admin when it carries a gifted seat. On a chapter,
|
|
1640
1722
|
`GET /api/admin/applications/:id/named-seat` shows a purchaser's seat and
|
|
@@ -1672,6 +1754,14 @@ These bite silently — a smoke test won't catch them:
|
|
|
1672
1754
|
lifts it once per chapter with `POST /api/admin/membership-seats/lift-purchase-window`
|
|
1673
1755
|
(super admin; only ever widens).
|
|
1674
1756
|
|
|
1757
|
+
On the payment step, a difference between the standard and due amounts is
|
|
1758
|
+
labelled a founding discount only when the quote carries `founding` from the
|
|
1759
|
+
authority. When its only basis is the signed catalog's `compareAtPriceCents`,
|
|
1760
|
+
the step shows "Standard rate" and "Your rate" and claims no discount, because
|
|
1761
|
+
a comparison the catalog declares is not a price anyone was charged. A site's
|
|
1762
|
+
`renderPriceLines` receives the same `founding` and `presentation` fields and
|
|
1763
|
+
should label its lines the same way.
|
|
1764
|
+
|
|
1675
1765
|
The public join config asks the authority, per paid tier, what a founding
|
|
1676
1766
|
applicant pays today (`offer-preview`, read-only: nothing is reserved) and
|
|
1677
1767
|
exposes it as `tiers[].founding` ({ cohortLabel, dueTodayCents, discountCents,
|
|
@@ -1680,6 +1770,18 @@ These bite silently — a smoke test won't catch them:
|
|
|
1680
1770
|
read the same field instead of hard-coding a founding price. The issued offer
|
|
1681
1771
|
at checkout remains the only promise.
|
|
1682
1772
|
|
|
1773
|
+
On the network owner, `GET /api/admin/membership-policy-versions` (super
|
|
1774
|
+
admin; `?chapterId=` narrows it to one chapter) lists every offering and
|
|
1775
|
+
discount policy version the authority holds, newest first, with the state
|
|
1776
|
+
issuance would give it now (active, scheduled, superseded with the date it
|
|
1777
|
+
was passed over, draft, retired), its effective or qualifying window, the
|
|
1778
|
+
provider mechanism a discount uses, and whatever `source`, `actor` and
|
|
1779
|
+
`recordedAt` the writing host stored on the row. `MembershipPolicyVersions`
|
|
1780
|
+
from `@odla-ai/chapter/ui/admin` renders it read-only beside
|
|
1781
|
+
`NetworkSeatBilling`, so an operator can see why a card shows a list price
|
|
1782
|
+
and a payable price without reading the database; nothing on it edits a
|
|
1783
|
+
version.
|
|
1784
|
+
|
|
1683
1785
|
On a chapter, a person record with no linked application — someone invited or
|
|
1684
1786
|
a candidate — is managed by the chapter's own admins the way the network
|
|
1685
1787
|
owner manages it: the record's Invitation tab sets or revokes the local
|
|
@@ -108,6 +108,8 @@ var DEFAULT_ADMIN_COPY = {
|
|
|
108
108
|
delivery: "Delivery",
|
|
109
109
|
notificationAddress: "Notification address",
|
|
110
110
|
replyTo: "Reply-to",
|
|
111
|
+
fromName: "From name",
|
|
112
|
+
fromNameHint: "Shown as the sender; the address stays the Worker's configured one.",
|
|
111
113
|
debugInbox: "Debug inbox",
|
|
112
114
|
debugInboxHint: "Non-production sends redirect here.",
|
|
113
115
|
sendTest: "Send test",
|
|
@@ -179,6 +181,8 @@ var DEFAULT_ADMIN_COPY = {
|
|
|
179
181
|
transportReachable: "transport reachable",
|
|
180
182
|
transportUnavailable: "transport unavailable",
|
|
181
183
|
transportNotChecked: "transport not checked",
|
|
184
|
+
edgeUnpaired: "edge unpaired",
|
|
185
|
+
edgeUnpairedHint: "This follower's shared secret is not in the leader vault, so nothing can be signed. Pair both vaults from an enrolled machine:",
|
|
182
186
|
bindingTransport: "service binding {binding}",
|
|
183
187
|
urlTransport: "public URL transport",
|
|
184
188
|
activeConfiguration: "active configuration r{revision}",
|
|
@@ -278,7 +282,9 @@ var DEFAULT_JOIN_COPY = {
|
|
|
278
282
|
incomplete: "The payment did not complete. Please try again.",
|
|
279
283
|
consent: "I have read and agree to the pricing and refund policy above.",
|
|
280
284
|
foundingMember: "Founding member rate",
|
|
281
|
-
foundingDiscount: "Founding discount"
|
|
285
|
+
foundingDiscount: "Founding discount",
|
|
286
|
+
standardRate: "Standard rate",
|
|
287
|
+
yourRate: "Your rate"
|
|
282
288
|
},
|
|
283
289
|
done: {
|
|
284
290
|
label: "You're booked",
|
|
@@ -516,4 +522,4 @@ export {
|
|
|
516
522
|
useChapterCopy,
|
|
517
523
|
BrandStyle
|
|
518
524
|
};
|
|
519
|
-
//# sourceMappingURL=chunk-
|
|
525
|
+
//# sourceMappingURL=chunk-4M2PN5K3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/copy-context.tsx","../src/copy-defaults-admin.ts","../src/copy-defaults.ts","../src/copy-resolve.ts","../src/brand.ts","../src/ui/brand-style.tsx"],"sourcesContent":["import { createContext, type ComponentChildren } from \"preact\";\nimport { useContext } from \"preact/hooks\";\nimport {\n DEFAULT_CHAPTER_COPY,\n resolveChapterCopy,\n type ChapterCopy,\n type ChapterCopyInput,\n} from \"../copy.js\";\n\nconst ChapterCopyContext = createContext<ChapterCopy>(DEFAULT_CHAPTER_COPY);\n\n/** Place a resolved or partial copy contract around any Chapter UI surface. */\nexport function ChapterCopyProvider(props: {\n copy?: ChapterCopy | ChapterCopyInput;\n children: ComponentChildren;\n}) {\n return (\n <ChapterCopyContext.Provider value={resolveChapterCopy(props.copy)}>\n {props.children}\n </ChapterCopyContext.Provider>\n );\n}\n\n/** Read the nearest complete Chapter copy contract. */\nexport function useChapterCopy(): ChapterCopy {\n return useContext(ChapterCopyContext);\n}\n","import type { ChapterCopy } from \"./copy.js\";\n\nexport const DEFAULT_ADMIN_COPY: ChapterCopy[\"admin\"] = {\n auth: {\n checking: \"Checking access…\",\n notAuthorized: \"Not authorized\",\n accountNotAuthorized: \"{account} isn't on the admin list.\",\n thisAccount: \"This account\",\n signOut: \"Sign out\",\n loading: \"Loading…\",\n signInNotConfigured: \"Sign-in not configured\",\n missingPublishableKey: \"No Clerk publishable key is set for this environment yet.\",\n noWorkspaces: \"No admin workspaces are configured.\",\n signInTagline: \"Admin sign-in, invite only\",\n },\n shell: {\n adminRole: \"admin\",\n adminConsole: \"Admin Console\",\n adminName: \"Admin\",\n navigationLabel: \"Admin navigation\",\n },\n workspaces: {\n dashboard: \"Dashboard\",\n overview: \"Overview\",\n billing: \"Billing\",\n people: \"People\",\n admission: \"Admission\",\n network: \"Network\",\n portfolio: \"Portfolio\",\n settings: \"Settings\",\n calendar: \"Calendar\",\n email: \"Email\",\n chapters: \"Chapters\",\n formations: \"Formation applications\",\n dealFlow: \"Deal flow\",\n dashboardViewsLabel: \"Dashboard views\",\n settingsViewsLabel: \"Settings views\",\n collectionsLabel: \"CRM collections\",\n portfolioViewsLabel: \"Portfolio views\",\n },\n dashboard: {\n loading: \"Loading dashboard…\",\n loadFailed: \"Couldn't load the dashboard\",\n upcomingCalls: \"Upcoming calls\",\n callsNeedAttention: \"{count} need attention\",\n noUpcomingCalls: \"No upcoming calls.\",\n drift: \"drift\",\n open: \"Open\",\n meet: \"Meet\",\n applications: \"Applications\",\n newMembers: \"New members\",\n newMembersUnavailable: \"Appears once billing is connected.\",\n revenueAdded: \"Revenue · annual run rate added\",\n revenue: \"Revenue\",\n revenueUnavailable: \"Appears once billing is connected.\",\n pipeline: \"Pipeline\",\n pipelineLabel: \"Membership pipeline\",\n thisWeek: \"+{count} this week\",\n noChange: \"no change\",\n activeMemberships: \"Active memberships\",\n annualRunRate: \"Annual run rate\",\n testMode: \"test mode\",\n },\n billing: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load billing\",\n notConfigured: \"Billing isn't configured. No Stripe key is vaulted for this group.\",\n active: \"Active\",\n annualized: \"Annualized\",\n renewingSoon: \"Renewing within 60 days\",\n pastDue: \"Past due\",\n subscriptions: \"Subscriptions\",\n testMode: \"Stripe test mode\",\n truncated: \"Showing the first 100 subscriptions. The list is truncated.\",\n name: \"Name\",\n email: \"Email\",\n application: \"Application\",\n subscription: \"Subscription\",\n cancelling: \"cancelling\",\n amount: \"Amount\",\n renews: \"Renews\",\n },\n availability: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load availability\",\n saved: \"Saved.\",\n title: \"Booking availability\",\n days: \"Days\",\n startHour: \"Start hour (0–24)\",\n endHour: \"End hour (0–24)\",\n slotMinutes: \"Slot length (minutes)\",\n minNoticeHours: \"Minimum notice (hours)\",\n windowDays: \"Booking window (days)\",\n timezone: \"Timezone\",\n summaryTemplate: \"Event summary template\",\n save: \"Save availability\",\n dayLabels: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n },\n email: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load email config\",\n saved: \"Saved.\",\n sent: \"Sent {template} to {address}{redirected}.\",\n testFailed: \"Test send did not deliver.\",\n delivery: \"Delivery\",\n notificationAddress: \"Notification address\",\n replyTo: \"Reply-to\",\n fromName: \"From name\",\n fromNameHint: \"Shown as the sender; the address stays the Worker's configured one.\",\n debugInbox: \"Debug inbox\",\n debugInboxHint: \"Non-production sends redirect here.\",\n sendTest: \"Send test\",\n enabled: \"Enabled\",\n subject: \"Subject\",\n body: \"Body\",\n save: \"Save changes\",\n sendLog: \"Send log\",\n sentColumn: \"Sent\",\n templateColumn: \"Template\",\n statusColumn: \"Status\",\n toColumn: \"To\",\n failed: \"failed\",\n redirected: \"redirected\",\n delivered: \"delivered\",\n },\n meetings: {\n loading: \"Loading…\",\n loadFailed: \"Couldn't load meetings\",\n cancelConfirm: \"Cancel this call? The calendar provider notifies the attendee.\",\n newStartPrompt: \"New start time (for example, 2026-08-01 14:00):\",\n parseFailed: \"Couldn't parse that time.\",\n agenda: \"Agenda\",\n empty: \"No meetings.\",\n unknown: \"(unknown)\",\n drift: \"drift\",\n meet: \"Meet\",\n reschedule: \"Reschedule\",\n cancel: \"Cancel\",\n },\n admission: {\n title: \"Interview decisions\",\n description: \"Local decisions override Parent decisions. Requiring an interview locally suppresses a Parent waiver for the same email.\",\n email: \"Email\",\n decision: \"Decision\",\n expires: \"Expires\",\n note: \"Internal note\",\n waive: \"Waive interview\",\n require: \"Require interview\",\n add: \"Add decision\",\n ledger: \"Effective grant ledger\",\n loading: \"Loading…\",\n consumed: \"consumed\",\n revoked: \"revoked\",\n expired: \"expired\",\n active: \"active\",\n local: \"local\",\n parent: \"Parent\",\n revoke: \"Revoke\",\n },\n network: {\n title: \"Share to a follower\",\n allowlistDescription: \"Sends the target's allowlisted fields. Its pipeline and account state remain local.\",\n sharing: \"Sharing…\",\n shareWith: \"Share with {target}\",\n shared: \"Shared with {target}.\",\n shareFailed: \"Could not share with {target}.\",\n deliveryFailed: \"Delivery failed.\",\n loadingRollup: \"Loading network overview…\",\n loadRollupFailed: \"Couldn't load the network overview\",\n overview: \"Network overview\",\n configuredFollowers: \"Configured followers\",\n reachableFollowers: \"Reachable followers\",\n recordsAcrossFollowers: \"Records across followers\",\n noFollowers: \"No follower sites are configured.\",\n followers: \"Followers\",\n available: \"available\",\n unavailable: \"unavailable\",\n transportReachable: \"transport reachable\",\n transportUnavailable: \"transport unavailable\",\n transportNotChecked: \"transport not checked\",\n edgeUnpaired: \"edge unpaired\",\n edgeUnpairedHint: \"This follower's shared secret is not in the leader vault, so nothing can be signed. Pair both vaults from an enrolled machine:\",\n bindingTransport: \"service binding {binding}\",\n urlTransport: \"public URL transport\",\n activeConfiguration: \"active configuration r{revision}\",\n noActiveConfiguration: \"no active managed configuration\",\n publicSiteAvailable: \"public site reachable\",\n publicSiteUnavailable: \"public site unavailable\",\n publicSiteNotChecked: \"public site not probed\",\n recordTotals: \"Portfolio records\",\n noRecordTotals: \"No aggregate records are available yet.\",\n },\n records: {\n workflowMissing: \"This record is not linked to an application workflow.\",\n invitation: \"Invitation\",\n comms: \"Comms\",\n commsHistory: \"Comms history\",\n scheduling: \"Scheduling\",\n billing: \"Billing\",\n notes: \"Notes\",\n access: \"Access\",\n sharing: \"Sharing\",\n lifecycle: \"Access & lifecycle\",\n role: \"Role\",\n superAdminHint: \"Super-admin, managed in odla Studio.\",\n noAccount: \"No linked account yet. The role is set once they sign in.\",\n approve: \"Approve\",\n refund: \"Refund\",\n approved: \"Approved.\",\n refunded: \"Refund issued.\",\n roleSet: \"Role set to {role}.\",\n messageSent: \"Message sent.\",\n messageNotSent: \"Message was not sent.\",\n loadingTemplates: \"Loading templates…\",\n template: \"Template\",\n chooseTemplate: \"Choose a template…\",\n sending: \"Sending…\",\n send: \"Send\",\n noMessages: \"No messages recorded.\",\n message: \"Message\",\n newStartPrompt: \"New start time:\",\n parseFailed: \"Couldn't parse that time.\",\n cancelConfirm: \"Cancel this call?\",\n callCancelled: \"Call cancelled.\",\n callRescheduled: \"Call rescheduled.\",\n noApplication: \"No application is linked to this record.\",\n loadingMeetings: \"Loading meetings…\",\n noMeetings: \"No meetings recorded.\",\n joinMeeting: \"Join meeting\",\n openCalendar: \"Open calendar\",\n reschedule: \"Reschedule\",\n cancel: \"Cancel\",\n },\n};\n","import type { ChapterCopy } from \"./copy.js\";\n\nexport const DEFAULT_COMMON_COPY: ChapterCopy[\"common\"] = {\n loading: \"Loading…\",\n saved: \"Saved.\",\n unknown: \"(unknown)\",\n};\n\nexport const DEFAULT_JOIN_COPY: ChapterCopy[\"join\"] = {\n namedSeat: {\n title: \"Your membership invitation\",\n from: \"{purchaserName} invited you.\",\n prepaid: \"Your {tierName} seat is already paid for.\",\n body: \"Accept your invitation, then complete your own application requirements. You will not be asked to pay for this seat.\",\n consent: \"I accept this membership for myself. It cannot be transferred and remains current while the purchaser's membership and my seat are current.\",\n accept: \"Accept my invitation\", accepting: \"Confirming your invitation…\", loading: \"Loading your invitation…\",\n unavailable: \"Your invitation is unavailable. Sign in with the invited email address, then try again.\",\n retry: \"Try again\", failure: \"We could not confirm acceptance. Retry this invitation; you do not need to purchase another membership.\",\n },\n form: {\n submit: \"Submit application\",\n submitting: \"Submitting…\",\n submitFailed: \"Your application could not be submitted.\",\n unexpectedFailure: \"Something went wrong. Please try again.\",\n },\n booking: {\n title: \"Book your introduction\",\n intro: \"Choose a time to meet your chapter and get to know each other.\",\n unavailable: \"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\",\n loadFailed: \"Times are briefly unavailable. Please try again.\",\n slotTaken: \"That time was just taken. Here are the current openings.\",\n failed: \"The booking could not be completed.\",\n loading: \"Loading available times…\",\n book: \"Book this time\",\n booking: \"Booking…\",\n },\n payment: {\n setupFailed: \"Payment could not be set up. Please try again.\",\n preparing: \"Preparing secure payment…\",\n pending: \"Confirming your payment…\",\n processing: \"Processing…\",\n payAndContinue: \"Pay and continue\",\n incomplete: \"The payment did not complete. Please try again.\",\n consent: \"I have read and agree to the pricing and refund policy above.\",\n foundingMember: \"Founding member rate\",\n foundingDiscount: \"Founding discount\",\n standardRate: \"Standard rate\",\n yourRate: \"Your rate\",\n },\n done: {\n label: \"You're booked\",\n calendarInvite: \"A calendar invitation with the video call link is on its way to your email.\",\n memberArea: \"Go to your member area\",\n },\n accepted: {\n label: \"You're approved\",\n body: \"Your membership is approved. No introduction call is required.\",\n },\n gift: {\n chooseSelf: \"Buy membership for myself\",\n optionsLabel: \"Or give access to someone else\",\n label: \"Your gift code\",\n body: \"Share this one-time code with the recipient. It becomes active after payment is confirmed.\",\n save: \"Save it now. For security, this code is shown only once.\",\n rotate: \"Generate a new code\",\n rotateFailed: \"A new code could not be generated.\",\n claimTitle: \"Have a gift code?\",\n claimBody: \"Use it here instead of paying by card.\",\n code: \"One-time code\",\n redeem: \"Use gift code\",\n redeemed: \"Gift code applied.\",\n redeemFailed: \"The code is invalid, unavailable, or does not match this membership.\",\n },\n};\n\nexport const DEFAULT_MEMBERS_COPY: ChapterCopy[\"members\"] = {\n loadFailed: \"Sign in is briefly unavailable. Please refresh.\",\n account: {\n signOut: \"Sign out\",\n adminConsole: \"Admin console\",\n },\n provisional: {\n cardLabel: \"Your Application\",\n applicationNeeded: \"One step remains\",\n applicationNeededBody: \"Your account is ready, and the application that completes it takes a few minutes.\",\n apply: \"Apply for membership\",\n refunded: \"Membership refunded\",\n refundedBody: \"Your fee has been refunded in full and your membership is canceled.\",\n active: \"Your membership is active.\",\n renews: \"Your membership is active and renews {date}.\",\n introductionCall: \"Your introduction call\",\n calendarInvite: \"A calendar invitation with the video call link is in your email.\",\n joinCall: \"Join the video call\",\n bookCall: \"Book your introduction call\",\n bookCallBody: \"Your application is in. Choose a time below, and a calendar invitation will reach your email.\",\n chooseTime: \"Choose a time\",\n },\n full: {\n welcome: \"Welcome back.\",\n },\n gifts: {\n title: \"Give access\",\n body: \"Purchase access for someone else and share the one-time claim code.\",\n noneAvailable: \"No gift options are available right now.\",\n paid: \"Payment submitted. Save the code below while it is visible.\",\n saveCode: \"This code is shown once. Save it before leaving this page.\",\n purchases: \"Your gifts\",\n noPurchases: \"No gift purchases yet.\",\n pending: \"Pending\",\n redeemed: \"Redeemed\",\n expired: \"Expired\",\n revoked: \"Revoked\",\n rotate: \"Rotate code\",\n rotated: \"A new code was generated. Save it now.\",\n rotateFailed: \"The code could not be rotated.\",\n redeemTitle: \"Redeem a gift\",\n code: \"One-time code\",\n redeem: \"Redeem\",\n redeemFailed: \"The code is invalid or unavailable.\",\n },\n reschedule: {\n changeTime: \"Change your time\",\n unavailable: \"Scheduling is briefly unavailable. We'll reach out by email to arrange your call.\",\n loadFailed: \"Times are briefly unavailable. Please try again.\",\n slotGone: \"That time is no longer available. Please pick another.\",\n loading: \"Loading available times…\",\n noTimes: \"No open times right now. Please check back soon.\",\n rescheduling: \"Rescheduling…\",\n keepTime: \"Keep my current time\",\n },\n};\n","import { DEFAULT_ADMIN_COPY } from \"./copy-defaults-admin.js\";\nimport {\n DEFAULT_COMMON_COPY,\n DEFAULT_JOIN_COPY,\n DEFAULT_MEMBERS_COPY,\n} from \"./copy-defaults.js\";\nimport type { ChapterCopy, ChapterCopyInput } from \"./copy.js\";\n\n/** Complete default language resolved beneath partial Chapter copy overrides. */\nexport const DEFAULT_CHAPTER_COPY: ChapterCopy = {\n common: DEFAULT_COMMON_COPY,\n join: DEFAULT_JOIN_COPY,\n members: DEFAULT_MEMBERS_COPY,\n admin: DEFAULT_ADMIN_COPY,\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n\nfunction mergeCopy<T>(base: T, input: unknown, path: string): T {\n if (typeof base === \"string\") {\n if (input === undefined) return base;\n if (typeof input !== \"string\") throw new Error(`${path}: must be a string`);\n return input as T;\n }\n if (Array.isArray(base)) {\n if (input === undefined) return [...base] as T;\n if (!Array.isArray(input) || !input.every((item) => typeof item === \"string\")) {\n throw new Error(`${path}: must be an array of strings`);\n }\n return [...input] as T;\n }\n if (!isObject(base)) return base;\n if (input !== undefined && !isObject(input)) throw new Error(`${path}: must be an object`);\n const overrides = input as Record<string, unknown> | undefined;\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(base)) {\n result[key] = mergeCopy(value, overrides?.[key], `${path}.${key}`);\n }\n return result as T;\n}\n\n/** Resolve a partial copy contract and validate every supplied leaf. */\nexport function resolveChapterCopy(input?: ChapterCopyInput): ChapterCopy {\n return mergeCopy(DEFAULT_CHAPTER_COPY, input, \"defineChapter.copy\");\n}\n\n/** Replace `{name}` placeholders without interpreting the resulting text as HTML. */\nexport function formatChapterCopy(\n template: string,\n values: Readonly<Record<string, string | number>>,\n): string {\n return template.replace(/\\{([a-zA-Z][a-zA-Z0-9]*)\\}/g, (match, key: string) =>\n Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match,\n );\n}\n","// Brand tokens (H4). defineChapter accepts a `brand` block; this turns it into a\n// `:root { --…: … }` CSS block, so a chapter re-skins the WHOLE UI — the\n// @odla-ai/ui components, the admin shell, and the member islands, which all read\n// --ui-* design tokens — from one config instead of hand-writing inline CSS.\n//\n// Pure string generation, so it is unit-testable and can be emitted at\n// build/SSR time into the page <head> (no flash of unstyled content), or via the\n// <BrandStyle> component from @odla-ai/chapter/ui.\nimport type { ChapterBrand, ChapterBrandTokens } from \"./types\";\n\n// A palette entry is either a direct custom property (already `--…`, e.g.\n// `--ui-accent` to retheme components) or a bare name we expose as `--<name>`\n// (e.g. `moss` → `--moss`, for a site to reference in its own CSS).\nfunction paletteVar(key: string): string {\n return key.startsWith(\"--\") ? key : `--${key}`;\n}\n\n// Strip characters that could break out of a `--var: value;` declaration or the\n// surrounding <style>. Brand config is trusted author input, so this is a\n// belt-and-suspenders guard, not a security boundary.\nfunction cleanValue(value: string): string {\n return value.replace(/[<>{};]/g, \"\").trim();\n}\n\nfunction paletteDecls(palette: Record<string, string> | undefined): string[] {\n const decls: string[] = [];\n for (const [key, value] of Object.entries(palette ?? {})) {\n if (typeof value === \"string\" && value.trim()) decls.push(`${paletteVar(key)}: ${cleanValue(value)};`);\n }\n return decls;\n}\n\nconst TOKEN_VARS: Record<keyof ChapterBrandTokens, string> = {\n background: \"--ui-bg\",\n surface: \"--ui-surface\",\n surface2: \"--ui-surface-2\",\n text: \"--ui-text\",\n textMuted: \"--ui-text-muted\",\n textFaint: \"--ui-text-faint\",\n border: \"--ui-border\",\n borderStrong: \"--ui-border-strong\",\n accent: \"--ui-accent\",\n accentStrong: \"--ui-accent-strong\",\n accentSoft: \"--ui-accent-soft\",\n onAccent: \"--ui-on-accent\",\n good: \"--ui-good\",\n warn: \"--ui-warn\",\n danger: \"--ui-danger\",\n chart1: \"--ui-chart-1\",\n chart2: \"--ui-chart-2\",\n chart3: \"--ui-chart-3\",\n chart4: \"--ui-chart-4\",\n chart5: \"--ui-chart-5\",\n chart6: \"--ui-chart-6\",\n chartPositive: \"--ui-chart-pos\",\n chartNegative: \"--ui-chart-neg\",\n contentWidth: \"--ui-content-width\",\n panelRadius: \"--ui-radius-lg\",\n panelShadow: \"--ui-shadow\",\n masterDetailColumns: \"--ui-master-detail-columns\",\n masterDetailMinHeight: \"--ui-master-detail-min-height\",\n pagePadding: \"--chapter-admin-page-padding\",\n workspacePadding: \"--chapter-admin-workspace-padding\",\n};\n\nfunction semanticDecls(tokens: ChapterBrandTokens | undefined): string[] {\n return Object.entries(tokens ?? {}).flatMap(([key, value]) =>\n typeof value === \"string\" && value.trim()\n ? [`${TOKEN_VARS[key as keyof ChapterBrandTokens]}: ${cleanValue(value)};`]\n : [],\n );\n}\n\n/**\n * Build the CSS that maps a chapter's brand onto the design tokens the UI reads:\n * each `palette` entry becomes a custom property (light, and dark unless\n * `paletteDark` overrides), and `fonts` (display/body/numeral) map to\n * `--ui-font-display` / `--ui-font-sans` / `--ui-font-numeral`. The dark block is\n * emitted under both `:root[data-theme=\"dark\"]` (the odla-ui theme toggle) and\n * `@media (prefers-color-scheme: dark)`. Returns \"\" when there is nothing to\n * theme. These are brand OVERRIDES on top of a base theme — they do not replace\n * the theme layer the components need (see {@link brandTokens} usage in the docs).\n */\nexport function brandTokens(\n brand: ChapterBrand | undefined,\n options: { selector?: string } = {},\n): string {\n if (!brand) return \"\";\n const light = [...paletteDecls(brand.palette), ...semanticDecls(brand.tokens)];\n const fonts = brand.fonts;\n if (fonts?.display) light.push(`--ui-font-display: ${cleanValue(fonts.display)};`);\n if (fonts?.body) light.push(`--ui-font-sans: ${cleanValue(fonts.body)};`);\n if (fonts?.numeral) light.push(`--ui-font-numeral: ${cleanValue(fonts.numeral)};`);\n const dark = [...paletteDecls(brand.paletteDark), ...semanticDecls(brand.tokensDark)];\n const invert = paletteDecls(brand.paletteInvert);\n const selector = options.selector ?? \":root\";\n const darkSelector = selector === \":root\" ? ':root[data-theme=\"dark\"]' : `${selector}[data-theme=\"dark\"]`;\n const systemSelector = selector === \":root\"\n ? ':root:not([data-theme=\"light\"])'\n : `${selector}:not([data-theme=\"light\"])`;\n\n let css = light.length ? `${selector} {\\n ${light.join(\"\\n \")}\\n}\\n` : \"\";\n if (dark.length) {\n const block = `{\\n ${dark.join(\"\\n \")}\\n}`;\n css += `${darkSelector} ${block}\\n@media (prefers-color-scheme: dark) {\\n ${systemSelector} ${block}\\n}\\n`;\n }\n if (invert.length) {\n const invertSelector = selector === \":root\"\n ? \".ui-invert\"\n : `${selector}.ui-invert, ${selector} .ui-invert`;\n css += `${invertSelector} {\\n color-scheme: dark;\\n ${invert.join(\"\\n \")}\\n}\\n`;\n }\n return css;\n}\n\n/** Structural output accepted from `@odla-ai/brand`'s pure token compiler. */\nexport interface CompiledChapterBrandTokens {\n light: Record<string, string>;\n dark: Record<string, string>;\n}\n\n/**\n * Turn a complete compiled token pair into Chapter's brand contract without\n * coupling Chapter's runtime to the brand-book/agent package. Explicit values in\n * `base` win, so a host can make final per-site adjustments after compilation.\n */\nexport function chapterBrandFromTokens(\n compiled: CompiledChapterBrandTokens,\n base: ChapterBrand = {},\n): ChapterBrand {\n return {\n ...base,\n palette: { ...compiled.light, ...base.palette },\n paletteDark: { ...compiled.dark, ...base.paletteDark },\n paletteInvert: { ...compiled.dark, ...base.paletteInvert },\n };\n}\n","// Client-side convenience for brand tokens: render the chapter's brand as a\n// <style> tag. Prefer emitting brandTokens() into the page <head> at build/SSR\n// time (no flash); use this when that isn't available (e.g. a pure SPA mount).\nimport { brandTokens } from \"../brand.js\";\nimport type { ChapterBrand } from \"../types\";\n\n/** Props for {@link BrandStyle}. */\nexport interface BrandStyleProps {\n brand: ChapterBrand | undefined;\n /** CSS selector receiving the variables. Default `:root`. */\n selector?: string;\n}\n\n/** Render a chapter's brand tokens as an inline <style> block (or nothing when\n * there is no brand to theme). */\nexport function BrandStyle(props: BrandStyleProps) {\n const css = brandTokens(props.brand, { selector: props.selector });\n if (!css) return null;\n return <style>{css}</style>;\n}\n"],"mappings":"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","names":["jsx"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
DEFAULT_JOIN_COPY,
|
|
3
3
|
formatChapterCopy,
|
|
4
4
|
useChapterCopy
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4M2PN5K3.js";
|
|
6
6
|
import {
|
|
7
7
|
collectFormFields
|
|
8
8
|
} from "./chunk-IVXVECKR.js";
|
|
@@ -321,17 +321,36 @@ function MembershipPaymentStep(props) {
|
|
|
321
321
|
/* @__PURE__ */ jsxs3("section", { className: "payment-quote", "aria-label": "Membership price", children: [
|
|
322
322
|
/* @__PURE__ */ jsx3("h3", { children: quote.tierName }),
|
|
323
323
|
quote.founding ? /* @__PURE__ */ jsx3("p", { className: "payment-founding", role: "note", "aria-label": "Founding member", children: copy.foundingMember }) : null,
|
|
324
|
-
props.renderPriceLines ? props.renderPriceLines(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
324
|
+
props.renderPriceLines ? props.renderPriceLines({
|
|
325
|
+
...quote.lineItems,
|
|
326
|
+
...quote.founding ? { founding: quote.founding } : {},
|
|
327
|
+
...quote.presentation ? { presentation: quote.presentation } : {}
|
|
328
|
+
}) : /* @__PURE__ */ jsxs3("dl", { children: [
|
|
329
|
+
quote.lineItems.discountCents > 0 && !quote.founding && quote.presentation ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
330
|
+
/* @__PURE__ */ jsx3("dt", { children: copy.standardRate }),
|
|
331
|
+
/* @__PURE__ */ jsxs3("dd", { children: [
|
|
332
|
+
amount(quote.lineItems.standardCents),
|
|
333
|
+
" per ",
|
|
334
|
+
quote.interval
|
|
335
|
+
] }),
|
|
336
|
+
/* @__PURE__ */ jsx3("dt", { children: copy.yourRate }),
|
|
337
|
+
/* @__PURE__ */ jsxs3("dd", { children: [
|
|
338
|
+
amount(quote.lineItems.dueTodayCents),
|
|
339
|
+
" per ",
|
|
340
|
+
quote.interval
|
|
341
|
+
] })
|
|
342
|
+
] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
343
|
+
/* @__PURE__ */ jsx3("dt", { children: "Membership price" }),
|
|
344
|
+
/* @__PURE__ */ jsxs3("dd", { children: [
|
|
345
|
+
amount(quote.lineItems.standardCents),
|
|
346
|
+
" per ",
|
|
347
|
+
quote.interval
|
|
348
|
+
] }),
|
|
349
|
+
quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
350
|
+
/* @__PURE__ */ jsx3("dt", { children: quote.founding ? copy.foundingDiscount : "Discount" }),
|
|
351
|
+
/* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.discountCents) })
|
|
352
|
+
] }) : null
|
|
330
353
|
] }),
|
|
331
|
-
quote.lineItems.discountCents > 0 ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
332
|
-
/* @__PURE__ */ jsx3("dt", { children: quote.founding || quote.presentation ? copy.foundingDiscount : "Discount" }),
|
|
333
|
-
/* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.discountCents) })
|
|
334
|
-
] }) : null,
|
|
335
354
|
/* @__PURE__ */ jsx3("dt", { children: "Due today" }),
|
|
336
355
|
/* @__PURE__ */ jsx3("dd", { children: amount(quote.lineItems.dueTodayCents) })
|
|
337
356
|
] }),
|
|
@@ -2035,4 +2054,4 @@ export {
|
|
|
2035
2054
|
membershipApplicationHref,
|
|
2036
2055
|
MembershipCards
|
|
2037
2056
|
};
|
|
2038
|
-
//# sourceMappingURL=chunk-
|
|
2057
|
+
//# sourceMappingURL=chunk-TEHCORNC.js.map
|