@odla-ai/chapter 0.8.0 → 0.10.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 +55 -17
- package/dist/chunk-YO6DY5DL.js +281 -0
- package/dist/chunk-YO6DY5DL.js.map +1 -0
- package/dist/chunk-ZCZK6QLC.js +537 -0
- package/dist/chunk-ZCZK6QLC.js.map +1 -0
- package/dist/index.cjs +61 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -14
- package/dist/index.d.ts +60 -14
- package/dist/index.js +61 -16
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +71 -0
- package/dist/ui/admin/index.js +9 -0
- package/dist/ui/admin/index.js.map +1 -0
- package/dist/ui/index.d.ts +4 -219
- package/dist/ui/index.js +20 -795
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/member/index.d.ts +152 -0
- package/dist/ui/member/index.js +33 -0
- package/dist/ui/member/index.js.map +1 -0
- package/dist/worker/index.cjs +105 -30
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +17 -0
- package/dist/worker/index.d.ts +17 -0
- package/dist/worker/index.js +105 -30
- package/dist/worker/index.js.map +1 -1
- package/package.json +9 -5
package/dist/index.d.cts
CHANGED
|
@@ -215,9 +215,24 @@ interface ChapterConfig {
|
|
|
215
215
|
* account is ready), `"none"` skips it. Any of these needs a `clerk_secret_key`
|
|
216
216
|
* vault secret to act. */
|
|
217
217
|
account?: AccountModel;
|
|
218
|
+
/** WHEN lifecycle email fires. Addressing and content live on the group row
|
|
219
|
+
* (owner-editable at runtime); this is the trigger, which is a build-time
|
|
220
|
+
* decision. See {@link ChapterSends}. */
|
|
221
|
+
sends?: ChapterSends;
|
|
218
222
|
}
|
|
219
223
|
/** Apply-time Clerk account provisioning model. */
|
|
220
224
|
type AccountModel = "invite" | "create" | "none";
|
|
225
|
+
/** When the admin notification fires: on application `submit` (default), on the
|
|
226
|
+
* first successful `payment`, or `never` (the site drives it itself). */
|
|
227
|
+
type AdminNotificationTrigger = "submit" | "payment" | "never";
|
|
228
|
+
/** Send-policy config: the trigger for each lifecycle email chapter owns. */
|
|
229
|
+
interface ChapterSends {
|
|
230
|
+
adminNotification?: AdminNotificationTrigger;
|
|
231
|
+
}
|
|
232
|
+
/** Resolved send policy (every trigger present). */
|
|
233
|
+
interface ResolvedSends {
|
|
234
|
+
adminNotification: AdminNotificationTrigger;
|
|
235
|
+
}
|
|
221
236
|
/** The resolved engine `defineChapter()` returns. */
|
|
222
237
|
interface Chapter {
|
|
223
238
|
config: ChapterConfig;
|
|
@@ -239,6 +254,8 @@ interface Chapter {
|
|
|
239
254
|
services: readonly string[];
|
|
240
255
|
/** Resolved apply-time account provisioning model (default `"invite"`). */
|
|
241
256
|
account: AccountModel;
|
|
257
|
+
/** Resolved send policy — when each lifecycle email fires. */
|
|
258
|
+
sends: ResolvedSends;
|
|
242
259
|
/** The seed `groups` row derived from config (chapter mode), else `null`. */
|
|
243
260
|
groupSeed(): Record<string, unknown> | null;
|
|
244
261
|
}
|
|
@@ -701,11 +718,24 @@ declare function projectApplicant(deps: ProjectionDeps, applicant: Applicant): P
|
|
|
701
718
|
recordId: string;
|
|
702
719
|
}>;
|
|
703
720
|
|
|
721
|
+
/** The outcome of a Clerk provisioning call. `existed` marks the heal case: Clerk
|
|
722
|
+
* rejected it because the account/invitation is already there, which is the end
|
|
723
|
+
* state we wanted anyway. */
|
|
724
|
+
interface ClerkResult {
|
|
725
|
+
ok: boolean;
|
|
726
|
+
status: number;
|
|
727
|
+
existed?: boolean;
|
|
728
|
+
/** Set when an `existed` heal also refreshed the account's public_metadata. */
|
|
729
|
+
refreshed?: boolean;
|
|
730
|
+
}
|
|
704
731
|
/** Inputs for a Clerk invitation. */
|
|
705
732
|
interface ClerkInviteInput {
|
|
706
733
|
email: string;
|
|
707
734
|
/** Where the accept-invitation link lands (usually the member area). */
|
|
708
735
|
redirectUrl?: string;
|
|
736
|
+
/** Written to the invitation's `public_metadata`, so the accepted account
|
|
737
|
+
* carries your own profile fields. */
|
|
738
|
+
publicMetadata?: Record<string, unknown>;
|
|
709
739
|
}
|
|
710
740
|
/** Build the Clerk Backend API invitation request (path + JSON body). Pure, so
|
|
711
741
|
* the wire shape is testable without a network call. */
|
|
@@ -713,18 +743,18 @@ declare function clerkInviteRequest(input: ClerkInviteInput): {
|
|
|
713
743
|
path: string;
|
|
714
744
|
body: Record<string, unknown>;
|
|
715
745
|
};
|
|
716
|
-
/** POST the invitation to the Clerk Backend API.
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
declare function createClerkInvitation(secretKey: string, input: ClerkInviteInput, fetchImpl?: typeof fetch): Promise<
|
|
720
|
-
ok: boolean;
|
|
721
|
-
status: number;
|
|
722
|
-
}>;
|
|
746
|
+
/** POST the invitation to the Clerk Backend API. A repeat invite for an
|
|
747
|
+
* already-invited email heals to `{ ok: true, existed: true }` rather than
|
|
748
|
+
* reporting a failure the caller would have to special-case. */
|
|
749
|
+
declare function createClerkInvitation(secretKey: string, input: ClerkInviteInput, fetchImpl?: typeof fetch): Promise<ClerkResult>;
|
|
723
750
|
/** Inputs for a server-side Clerk user create. */
|
|
724
751
|
interface ClerkUserInput {
|
|
725
752
|
email: string;
|
|
726
753
|
firstName?: string;
|
|
727
754
|
lastName?: string;
|
|
755
|
+
/** Written to the user's `public_metadata` — the site's own profile fields
|
|
756
|
+
* (role, tier, whatever the member area reads). */
|
|
757
|
+
publicMetadata?: Record<string, unknown>;
|
|
728
758
|
}
|
|
729
759
|
/** Build the Clerk Backend API user-create request. The account is created
|
|
730
760
|
* passwordless (the member signs in via the site's Clerk flow), so join step 3
|
|
@@ -733,12 +763,12 @@ declare function clerkUserRequest(input: ClerkUserInput): {
|
|
|
733
763
|
path: string;
|
|
734
764
|
body: Record<string, unknown>;
|
|
735
765
|
};
|
|
736
|
-
/** Create the applicant's Clerk account server-side. A repeat for
|
|
737
|
-
*
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
766
|
+
/** Create the applicant's Clerk account server-side. A repeat for an email that
|
|
767
|
+
* already has an account heals to `{ ok: true, existed: true }` — the account
|
|
768
|
+
* exists, which is the state apply-time provisioning wanted — and, when
|
|
769
|
+
* `publicMetadata` was supplied, refreshes it on the existing account so a
|
|
770
|
+
* re-application repairs a previously missed create (`refreshed: true`). */
|
|
771
|
+
declare function createClerkUser(secretKey: string, input: ClerkUserInput, fetchImpl?: typeof fetch): Promise<ClerkResult>;
|
|
742
772
|
|
|
743
773
|
/** An application row, as far as the session cares about it. */
|
|
744
774
|
interface ApplicationRecord {
|
|
@@ -879,6 +909,22 @@ declare const SCHEDULING_DEFAULTS: ResolvedScheduling;
|
|
|
879
909
|
* `windowDays` is capped at 62 because Google FreeBusy is.
|
|
880
910
|
*/
|
|
881
911
|
declare function resolveScheduling(config?: ChapterScheduling): ResolvedScheduling;
|
|
912
|
+
/** Validation messages keyed by form field. */
|
|
913
|
+
type SchedulingErrors = Record<string, string>;
|
|
914
|
+
/**
|
|
915
|
+
* Validate a scheduling config field by field, collecting owner-readable messages
|
|
916
|
+
* rather than throwing on the first problem. An admin settings form needs to say
|
|
917
|
+
* *which* field is wrong and why — "pick at least one day" beats a stack trace —
|
|
918
|
+
* so the admin route returns these directly. {@link resolveScheduling} is the
|
|
919
|
+
* throwing wrapper for internal/config-time use.
|
|
920
|
+
*/
|
|
921
|
+
declare function validateScheduling(config?: ChapterScheduling): {
|
|
922
|
+
ok: true;
|
|
923
|
+
value: ResolvedScheduling;
|
|
924
|
+
} | {
|
|
925
|
+
ok: false;
|
|
926
|
+
errors: SchedulingErrors;
|
|
927
|
+
};
|
|
882
928
|
/** Statuses a member may book/reschedule from (early pipeline only). */
|
|
883
929
|
/** The availability window: `[now, now + windowDays]` in epoch ms. */
|
|
884
930
|
declare function slotWindow(now: number, windowDays: number): {
|
|
@@ -962,4 +1008,4 @@ type ApplicationBookingPatch = {
|
|
|
962
1008
|
* already there (never backward). */
|
|
963
1009
|
declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
|
|
964
1010
|
|
|
965
|
-
export { type AccountModel, 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 ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ClerkInviteInput, type ClerkUserInput, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, 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 ResolvedPipeline, type ResolvedScheduling, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SecretStore, type SessionUser, type SharedPerson, type StripeEvent, type SubmitResult, type WebhookEvent, applicationBookingUpdate, applicationSummary, bookingDecision, brandTokens, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clerkInviteRequest, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, normalizeWebhookEvent, paymentsReady, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, slotWindow, stageIndex, stripeForm, submitApplication, subscriptionIdempotencyKey, verifyStripeSignature, webhookMutationId };
|
|
1011
|
+
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 ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, 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 ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretStore, type SessionUser, type SharedPerson, type StripeEvent, type SubmitResult, type WebhookEvent, applicationBookingUpdate, applicationSummary, bookingDecision, brandTokens, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clerkInviteRequest, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, normalizeWebhookEvent, paymentsReady, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, slotWindow, stageIndex, stripeForm, submitApplication, subscriptionIdempotencyKey, validateScheduling, verifyStripeSignature, webhookMutationId };
|
package/dist/index.d.ts
CHANGED
|
@@ -215,9 +215,24 @@ interface ChapterConfig {
|
|
|
215
215
|
* account is ready), `"none"` skips it. Any of these needs a `clerk_secret_key`
|
|
216
216
|
* vault secret to act. */
|
|
217
217
|
account?: AccountModel;
|
|
218
|
+
/** WHEN lifecycle email fires. Addressing and content live on the group row
|
|
219
|
+
* (owner-editable at runtime); this is the trigger, which is a build-time
|
|
220
|
+
* decision. See {@link ChapterSends}. */
|
|
221
|
+
sends?: ChapterSends;
|
|
218
222
|
}
|
|
219
223
|
/** Apply-time Clerk account provisioning model. */
|
|
220
224
|
type AccountModel = "invite" | "create" | "none";
|
|
225
|
+
/** When the admin notification fires: on application `submit` (default), on the
|
|
226
|
+
* first successful `payment`, or `never` (the site drives it itself). */
|
|
227
|
+
type AdminNotificationTrigger = "submit" | "payment" | "never";
|
|
228
|
+
/** Send-policy config: the trigger for each lifecycle email chapter owns. */
|
|
229
|
+
interface ChapterSends {
|
|
230
|
+
adminNotification?: AdminNotificationTrigger;
|
|
231
|
+
}
|
|
232
|
+
/** Resolved send policy (every trigger present). */
|
|
233
|
+
interface ResolvedSends {
|
|
234
|
+
adminNotification: AdminNotificationTrigger;
|
|
235
|
+
}
|
|
221
236
|
/** The resolved engine `defineChapter()` returns. */
|
|
222
237
|
interface Chapter {
|
|
223
238
|
config: ChapterConfig;
|
|
@@ -239,6 +254,8 @@ interface Chapter {
|
|
|
239
254
|
services: readonly string[];
|
|
240
255
|
/** Resolved apply-time account provisioning model (default `"invite"`). */
|
|
241
256
|
account: AccountModel;
|
|
257
|
+
/** Resolved send policy — when each lifecycle email fires. */
|
|
258
|
+
sends: ResolvedSends;
|
|
242
259
|
/** The seed `groups` row derived from config (chapter mode), else `null`. */
|
|
243
260
|
groupSeed(): Record<string, unknown> | null;
|
|
244
261
|
}
|
|
@@ -701,11 +718,24 @@ declare function projectApplicant(deps: ProjectionDeps, applicant: Applicant): P
|
|
|
701
718
|
recordId: string;
|
|
702
719
|
}>;
|
|
703
720
|
|
|
721
|
+
/** The outcome of a Clerk provisioning call. `existed` marks the heal case: Clerk
|
|
722
|
+
* rejected it because the account/invitation is already there, which is the end
|
|
723
|
+
* state we wanted anyway. */
|
|
724
|
+
interface ClerkResult {
|
|
725
|
+
ok: boolean;
|
|
726
|
+
status: number;
|
|
727
|
+
existed?: boolean;
|
|
728
|
+
/** Set when an `existed` heal also refreshed the account's public_metadata. */
|
|
729
|
+
refreshed?: boolean;
|
|
730
|
+
}
|
|
704
731
|
/** Inputs for a Clerk invitation. */
|
|
705
732
|
interface ClerkInviteInput {
|
|
706
733
|
email: string;
|
|
707
734
|
/** Where the accept-invitation link lands (usually the member area). */
|
|
708
735
|
redirectUrl?: string;
|
|
736
|
+
/** Written to the invitation's `public_metadata`, so the accepted account
|
|
737
|
+
* carries your own profile fields. */
|
|
738
|
+
publicMetadata?: Record<string, unknown>;
|
|
709
739
|
}
|
|
710
740
|
/** Build the Clerk Backend API invitation request (path + JSON body). Pure, so
|
|
711
741
|
* the wire shape is testable without a network call. */
|
|
@@ -713,18 +743,18 @@ declare function clerkInviteRequest(input: ClerkInviteInput): {
|
|
|
713
743
|
path: string;
|
|
714
744
|
body: Record<string, unknown>;
|
|
715
745
|
};
|
|
716
|
-
/** POST the invitation to the Clerk Backend API.
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
declare function createClerkInvitation(secretKey: string, input: ClerkInviteInput, fetchImpl?: typeof fetch): Promise<
|
|
720
|
-
ok: boolean;
|
|
721
|
-
status: number;
|
|
722
|
-
}>;
|
|
746
|
+
/** POST the invitation to the Clerk Backend API. A repeat invite for an
|
|
747
|
+
* already-invited email heals to `{ ok: true, existed: true }` rather than
|
|
748
|
+
* reporting a failure the caller would have to special-case. */
|
|
749
|
+
declare function createClerkInvitation(secretKey: string, input: ClerkInviteInput, fetchImpl?: typeof fetch): Promise<ClerkResult>;
|
|
723
750
|
/** Inputs for a server-side Clerk user create. */
|
|
724
751
|
interface ClerkUserInput {
|
|
725
752
|
email: string;
|
|
726
753
|
firstName?: string;
|
|
727
754
|
lastName?: string;
|
|
755
|
+
/** Written to the user's `public_metadata` — the site's own profile fields
|
|
756
|
+
* (role, tier, whatever the member area reads). */
|
|
757
|
+
publicMetadata?: Record<string, unknown>;
|
|
728
758
|
}
|
|
729
759
|
/** Build the Clerk Backend API user-create request. The account is created
|
|
730
760
|
* passwordless (the member signs in via the site's Clerk flow), so join step 3
|
|
@@ -733,12 +763,12 @@ declare function clerkUserRequest(input: ClerkUserInput): {
|
|
|
733
763
|
path: string;
|
|
734
764
|
body: Record<string, unknown>;
|
|
735
765
|
};
|
|
736
|
-
/** Create the applicant's Clerk account server-side. A repeat for
|
|
737
|
-
*
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
766
|
+
/** Create the applicant's Clerk account server-side. A repeat for an email that
|
|
767
|
+
* already has an account heals to `{ ok: true, existed: true }` — the account
|
|
768
|
+
* exists, which is the state apply-time provisioning wanted — and, when
|
|
769
|
+
* `publicMetadata` was supplied, refreshes it on the existing account so a
|
|
770
|
+
* re-application repairs a previously missed create (`refreshed: true`). */
|
|
771
|
+
declare function createClerkUser(secretKey: string, input: ClerkUserInput, fetchImpl?: typeof fetch): Promise<ClerkResult>;
|
|
742
772
|
|
|
743
773
|
/** An application row, as far as the session cares about it. */
|
|
744
774
|
interface ApplicationRecord {
|
|
@@ -879,6 +909,22 @@ declare const SCHEDULING_DEFAULTS: ResolvedScheduling;
|
|
|
879
909
|
* `windowDays` is capped at 62 because Google FreeBusy is.
|
|
880
910
|
*/
|
|
881
911
|
declare function resolveScheduling(config?: ChapterScheduling): ResolvedScheduling;
|
|
912
|
+
/** Validation messages keyed by form field. */
|
|
913
|
+
type SchedulingErrors = Record<string, string>;
|
|
914
|
+
/**
|
|
915
|
+
* Validate a scheduling config field by field, collecting owner-readable messages
|
|
916
|
+
* rather than throwing on the first problem. An admin settings form needs to say
|
|
917
|
+
* *which* field is wrong and why — "pick at least one day" beats a stack trace —
|
|
918
|
+
* so the admin route returns these directly. {@link resolveScheduling} is the
|
|
919
|
+
* throwing wrapper for internal/config-time use.
|
|
920
|
+
*/
|
|
921
|
+
declare function validateScheduling(config?: ChapterScheduling): {
|
|
922
|
+
ok: true;
|
|
923
|
+
value: ResolvedScheduling;
|
|
924
|
+
} | {
|
|
925
|
+
ok: false;
|
|
926
|
+
errors: SchedulingErrors;
|
|
927
|
+
};
|
|
882
928
|
/** Statuses a member may book/reschedule from (early pipeline only). */
|
|
883
929
|
/** The availability window: `[now, now + windowDays]` in epoch ms. */
|
|
884
930
|
declare function slotWindow(now: number, windowDays: number): {
|
|
@@ -962,4 +1008,4 @@ type ApplicationBookingPatch = {
|
|
|
962
1008
|
* already there (never backward). */
|
|
963
1009
|
declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
|
|
964
1010
|
|
|
965
|
-
export { type AccountModel, 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 ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ClerkInviteInput, type ClerkUserInput, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, 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 ResolvedPipeline, type ResolvedScheduling, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SecretStore, type SessionUser, type SharedPerson, type StripeEvent, type SubmitResult, type WebhookEvent, applicationBookingUpdate, applicationSummary, bookingDecision, brandTokens, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clerkInviteRequest, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, normalizeWebhookEvent, paymentsReady, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, slotWindow, stageIndex, stripeForm, submitApplication, subscriptionIdempotencyKey, verifyStripeSignature, webhookMutationId };
|
|
1011
|
+
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 ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, 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 ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretStore, type SessionUser, type SharedPerson, type StripeEvent, type SubmitResult, type WebhookEvent, applicationBookingUpdate, applicationSummary, bookingDecision, brandTokens, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clerkInviteRequest, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, normalizeWebhookEvent, paymentsReady, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, slotWindow, stageIndex, stripeForm, submitApplication, subscriptionIdempotencyKey, validateScheduling, verifyStripeSignature, webhookMutationId };
|
package/dist/index.js
CHANGED
|
@@ -474,6 +474,13 @@ function defineChapter(config) {
|
|
|
474
474
|
if (account !== "invite" && account !== "create" && account !== "none") {
|
|
475
475
|
throw new Error(`defineChapter.account: must be "invite", "create", or "none" \u2014 got "${String(account)}"`);
|
|
476
476
|
}
|
|
477
|
+
const adminNotification = config.sends?.adminNotification ?? "submit";
|
|
478
|
+
if (adminNotification !== "submit" && adminNotification !== "payment" && adminNotification !== "never") {
|
|
479
|
+
throw new Error(
|
|
480
|
+
`defineChapter.sends.adminNotification: must be "submit", "payment", or "never" \u2014 got "${String(adminNotification)}"`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
const sends = { adminNotification };
|
|
477
484
|
const chapter = {
|
|
478
485
|
config,
|
|
479
486
|
id: id2,
|
|
@@ -487,6 +494,7 @@ function defineChapter(config) {
|
|
|
487
494
|
rules,
|
|
488
495
|
services,
|
|
489
496
|
account,
|
|
497
|
+
sends,
|
|
490
498
|
groupSeed: () => mode === "chapter" ? buildGroupSeed(config) : null
|
|
491
499
|
};
|
|
492
500
|
if (config.url !== void 0) chapter.url = config.url;
|
|
@@ -764,13 +772,15 @@ async function projectApplicant(deps, applicant) {
|
|
|
764
772
|
}
|
|
765
773
|
|
|
766
774
|
// src/clerk.ts
|
|
775
|
+
var heal = (status) => status === 422 ? { ok: true, status, existed: true } : { ok: false, status };
|
|
767
776
|
function clerkInviteRequest(input) {
|
|
768
777
|
return {
|
|
769
778
|
path: "/v1/invitations",
|
|
770
779
|
body: {
|
|
771
780
|
email_address: input.email,
|
|
772
781
|
notify: true,
|
|
773
|
-
...input.redirectUrl ? { redirect_url: input.redirectUrl } : {}
|
|
782
|
+
...input.redirectUrl ? { redirect_url: input.redirectUrl } : {},
|
|
783
|
+
...input.publicMetadata ? { public_metadata: input.publicMetadata } : {}
|
|
774
784
|
}
|
|
775
785
|
};
|
|
776
786
|
}
|
|
@@ -781,7 +791,7 @@ async function createClerkInvitation(secretKey, input, fetchImpl = fetch) {
|
|
|
781
791
|
headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
|
|
782
792
|
body: JSON.stringify(body)
|
|
783
793
|
});
|
|
784
|
-
return { ok:
|
|
794
|
+
return res.ok ? { ok: true, status: res.status } : heal(res.status);
|
|
785
795
|
}
|
|
786
796
|
function clerkUserRequest(input) {
|
|
787
797
|
return {
|
|
@@ -790,10 +800,25 @@ function clerkUserRequest(input) {
|
|
|
790
800
|
email_address: [input.email],
|
|
791
801
|
skip_password_requirement: true,
|
|
792
802
|
...input.firstName ? { first_name: input.firstName } : {},
|
|
793
|
-
...input.lastName ? { last_name: input.lastName } : {}
|
|
803
|
+
...input.lastName ? { last_name: input.lastName } : {},
|
|
804
|
+
...input.publicMetadata ? { public_metadata: input.publicMetadata } : {}
|
|
794
805
|
}
|
|
795
806
|
};
|
|
796
807
|
}
|
|
808
|
+
async function refreshUserMetadata(secretKey, email, publicMetadata, fetchImpl) {
|
|
809
|
+
const auth = { authorization: `Bearer ${secretKey}` };
|
|
810
|
+
const found = await fetchImpl(`https://api.clerk.com/v1/users?email_address=${encodeURIComponent(email)}&limit=1`, { headers: auth });
|
|
811
|
+
if (!found.ok) return false;
|
|
812
|
+
const users = await found.json().catch(() => null);
|
|
813
|
+
const id2 = Array.isArray(users) && typeof users[0]?.id === "string" ? users[0].id : void 0;
|
|
814
|
+
if (!id2) return false;
|
|
815
|
+
const patched = await fetchImpl(`https://api.clerk.com/v1/users/${id2}/metadata`, {
|
|
816
|
+
method: "PATCH",
|
|
817
|
+
headers: { ...auth, "content-type": "application/json" },
|
|
818
|
+
body: JSON.stringify({ public_metadata: publicMetadata })
|
|
819
|
+
});
|
|
820
|
+
return patched.ok;
|
|
821
|
+
}
|
|
797
822
|
async function createClerkUser(secretKey, input, fetchImpl = fetch) {
|
|
798
823
|
const { path, body } = clerkUserRequest(input);
|
|
799
824
|
const res = await fetchImpl(`https://api.clerk.com${path}`, {
|
|
@@ -801,7 +826,11 @@ async function createClerkUser(secretKey, input, fetchImpl = fetch) {
|
|
|
801
826
|
headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
|
|
802
827
|
body: JSON.stringify(body)
|
|
803
828
|
});
|
|
804
|
-
return { ok:
|
|
829
|
+
if (res.ok) return { ok: true, status: res.status };
|
|
830
|
+
const healed = heal(res.status);
|
|
831
|
+
if (!healed.existed || !input.publicMetadata) return healed;
|
|
832
|
+
const refreshed = await refreshUserMetadata(secretKey, input.email, input.publicMetadata, fetchImpl).catch(() => false);
|
|
833
|
+
return { ...healed, refreshed };
|
|
805
834
|
}
|
|
806
835
|
|
|
807
836
|
// src/session.ts
|
|
@@ -920,6 +949,12 @@ function isValidTimeZone(tz) {
|
|
|
920
949
|
}
|
|
921
950
|
}
|
|
922
951
|
function resolveScheduling(config) {
|
|
952
|
+
const result = validateScheduling(config);
|
|
953
|
+
if (result.ok) return result.value;
|
|
954
|
+
const detail = Object.entries(result.errors).map(([field, message]) => `${field}: ${message}`).join(" ");
|
|
955
|
+
throw new Error(`scheduling: ${detail}`);
|
|
956
|
+
}
|
|
957
|
+
function validateScheduling(config) {
|
|
923
958
|
const d = config ?? {};
|
|
924
959
|
const c = {
|
|
925
960
|
slotMinutes: d.slotMinutes ?? SCHEDULING_DEFAULTS.slotMinutes,
|
|
@@ -931,20 +966,29 @@ function resolveScheduling(config) {
|
|
|
931
966
|
windowDays: d.windowDays ?? SCHEDULING_DEFAULTS.windowDays,
|
|
932
967
|
summaryTemplate: d.summaryTemplate ?? SCHEDULING_DEFAULTS.summaryTemplate
|
|
933
968
|
};
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
if (!(c.windowDays >= 1 && c.windowDays <= 62))
|
|
939
|
-
|
|
940
|
-
|
|
969
|
+
const errors = {};
|
|
970
|
+
if (!(c.slotMinutes >= 15 && c.slotMinutes <= 240)) {
|
|
971
|
+
errors.slotMinutes = "Slot length must be between 15 and 240 minutes.";
|
|
972
|
+
}
|
|
973
|
+
if (!(c.windowDays >= 1 && c.windowDays <= 62)) {
|
|
974
|
+
errors.windowDays = "Booking window must be between 1 and 62 days (the calendar caps look-ahead at 62).";
|
|
975
|
+
}
|
|
976
|
+
if (!(c.minNoticeHours >= 0 && c.minNoticeHours <= 336)) {
|
|
977
|
+
errors.minNoticeHours = "Minimum notice must be between 0 and 336 hours.";
|
|
978
|
+
}
|
|
979
|
+
if (!(c.startHour >= 0 && c.startHour < c.endHour && c.endHour <= 24)) {
|
|
980
|
+
errors.hours = "Hours must satisfy 0 \u2264 start < end \u2264 24.";
|
|
981
|
+
}
|
|
941
982
|
const days = [...c.days];
|
|
942
|
-
if (!days.length
|
|
943
|
-
|
|
983
|
+
if (!days.length) errors.days = "Pick at least one day.";
|
|
984
|
+
else if (!days.every((n) => Number.isInteger(n) && n >= 0 && n <= 6)) {
|
|
985
|
+
errors.days = "Days must be weekday numbers, 0 (Sunday) through 6 (Saturday).";
|
|
986
|
+
}
|
|
987
|
+
if (typeof c.timezone !== "string" || !isValidTimeZone(c.timezone)) {
|
|
988
|
+
errors.timezone = `"${String(c.timezone)}" is not a valid IANA timezone (for example "America/Los_Angeles").`;
|
|
944
989
|
}
|
|
945
|
-
if (typeof c.
|
|
946
|
-
|
|
947
|
-
return { ...c, days };
|
|
990
|
+
if (typeof c.summaryTemplate !== "string") errors.summaryTemplate = "Calendar summary template must be text.";
|
|
991
|
+
return Object.keys(errors).length > 0 ? { ok: false, errors } : { ok: true, value: { ...c, days } };
|
|
948
992
|
}
|
|
949
993
|
function slotWindow(now, windowDays) {
|
|
950
994
|
return { from: now, to: now + windowDays * 864e5 };
|
|
@@ -1049,6 +1093,7 @@ export {
|
|
|
1049
1093
|
stripeForm,
|
|
1050
1094
|
submitApplication,
|
|
1051
1095
|
subscriptionIdempotencyKey,
|
|
1096
|
+
validateScheduling,
|
|
1052
1097
|
verifyStripeSignature,
|
|
1053
1098
|
webhookMutationId
|
|
1054
1099
|
};
|