@odla-ai/chapter 0.19.0 → 0.20.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 +204 -21
- package/dist/{chunk-GWY2ZTN7.js → chunk-LME2IHY5.js} +2 -50
- package/dist/chunk-LME2IHY5.js.map +1 -0
- package/dist/chunk-MKXHZMAP.js +51 -0
- package/dist/chunk-MKXHZMAP.js.map +1 -0
- package/dist/{chunk-D3QMK3ED.js → chunk-UIWFEESI.js} +611 -470
- package/dist/chunk-UIWFEESI.js.map +1 -0
- package/dist/index.cjs +176 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -9
- package/dist/index.d.ts +83 -9
- package/dist/index.js +176 -11
- package/dist/index.js.map +1 -1
- package/dist/types-D2fGiNG8.d.ts +366 -0
- package/dist/ui/admin/index.d.ts +37 -3
- package/dist/ui/admin/index.js +10 -1
- package/dist/ui/index.d.ts +2 -1
- package/dist/ui/index.js +13 -3
- package/dist/ui/member/index.d.ts +2 -25
- package/dist/ui/member/index.js +4 -2
- package/dist/worker/index.cjs +225 -24
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +45 -0
- package/dist/worker/index.d.ts +45 -0
- package/dist/worker/index.js +225 -24
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-D3QMK3ED.js.map +0 -1
- package/dist/chunk-GWY2ZTN7.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CrmConfig, Crm } from '@odla-ai/crm';
|
|
1
|
+
import { CrmConfig, Crm, CrmRecord } from '@odla-ai/crm';
|
|
2
2
|
|
|
3
3
|
/** Which feature profile a site runs. `chapter` is the full public member site
|
|
4
4
|
* (join, Stripe membership, booking, member area, admin, CRM); `hub` is
|
|
@@ -68,6 +68,9 @@ interface ChapterBrand {
|
|
|
68
68
|
body?: string;
|
|
69
69
|
numeral?: string;
|
|
70
70
|
};
|
|
71
|
+
/** Short glyph used in compact admin chrome. Defaults to the first three
|
|
72
|
+
* letters of the chapter name. */
|
|
73
|
+
badge?: string;
|
|
71
74
|
wordmark?: string;
|
|
72
75
|
tagline?: string;
|
|
73
76
|
/** Header/footer nav sections for the web-component chrome: label → entries. */
|
|
@@ -77,6 +80,40 @@ interface ChapterBrand {
|
|
|
77
80
|
}>>;
|
|
78
81
|
logos?: string;
|
|
79
82
|
}
|
|
83
|
+
/** One follower site a leader can push CRM records to. The secret VALUE never
|
|
84
|
+
* lives in config: `secretName` names the leader tenant's vaulted copy of the
|
|
85
|
+
* follower's `network_share_secret`. */
|
|
86
|
+
interface ChapterNetworkTarget {
|
|
87
|
+
/** Stable lowercase slug used by the UI, audit tags, and default secret name. */
|
|
88
|
+
id: string;
|
|
89
|
+
/** Human-facing site name. Defaults to `id`. */
|
|
90
|
+
name?: string;
|
|
91
|
+
/** Absolute follower origin, e.g. `https://silver.example.com`. */
|
|
92
|
+
url: string;
|
|
93
|
+
/** Leader-vault key holding this follower's share secret. Defaults to
|
|
94
|
+
* `network_share_<id-with-underscores>`. */
|
|
95
|
+
secretName?: string;
|
|
96
|
+
/** Per-record-type field allowlist. If present, omitted types cannot be sent
|
|
97
|
+
* to this target. Without it, package-safe person/company defaults apply. */
|
|
98
|
+
fields?: Record<string, readonly string[]>;
|
|
99
|
+
}
|
|
100
|
+
/** Leader/follower network configuration. Followers need no code config to
|
|
101
|
+
* receive records; they only vault `network_share_secret`. */
|
|
102
|
+
interface ChapterNetwork {
|
|
103
|
+
targets?: readonly ChapterNetworkTarget[];
|
|
104
|
+
}
|
|
105
|
+
/** A validated network target carried on the resolved chapter engine. */
|
|
106
|
+
interface ResolvedNetworkTarget {
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
url: string;
|
|
110
|
+
secretName: string;
|
|
111
|
+
fields?: Record<string, readonly string[]>;
|
|
112
|
+
}
|
|
113
|
+
/** Fully-resolved leader network configuration. */
|
|
114
|
+
interface ResolvedNetwork {
|
|
115
|
+
targets: readonly ResolvedNetworkTarget[];
|
|
116
|
+
}
|
|
80
117
|
/** Membership pricing for the group row (chapter mode). */
|
|
81
118
|
interface ChapterPrices {
|
|
82
119
|
standardCents: number;
|
|
@@ -232,6 +269,9 @@ interface ChapterConfig {
|
|
|
232
269
|
/** A `defineCrm()` config or a resolved `Crm`. Omit for the per-mode default. */
|
|
233
270
|
crm?: CrmConfig | Crm;
|
|
234
271
|
brand?: ChapterBrand;
|
|
272
|
+
/** Leader → follower delivery targets. Secret values stay in the leader
|
|
273
|
+
* tenant vault; see {@link ChapterNetworkTarget}. */
|
|
274
|
+
network?: ChapterNetwork;
|
|
235
275
|
thesis?: unknown;
|
|
236
276
|
/** Required in `chapter` mode. */
|
|
237
277
|
prices?: ChapterPrices;
|
|
@@ -318,6 +358,11 @@ interface Chapter {
|
|
|
318
358
|
name: string;
|
|
319
359
|
url?: string;
|
|
320
360
|
mode: ChapterMode;
|
|
361
|
+
/** Resolved site identity. `wordmark` always falls back to `name`, so the
|
|
362
|
+
* admin/member UI never needs a second brand declaration. */
|
|
363
|
+
brand: ChapterBrand;
|
|
364
|
+
/** Validated follower targets for leader-driven record pushes. */
|
|
365
|
+
network: ResolvedNetwork;
|
|
321
366
|
/** Resolved CRM engine (from `defineCrm`). */
|
|
322
367
|
crm: Crm;
|
|
323
368
|
/** Resolved auth policy (source, claim, ladder, super-admin tier). */
|
|
@@ -401,9 +446,9 @@ declare function chapterDb(mode: ChapterMode, auth: ResolvedAuth): {
|
|
|
401
446
|
rules: DbRules;
|
|
402
447
|
};
|
|
403
448
|
|
|
404
|
-
/** The
|
|
405
|
-
*
|
|
406
|
-
declare function defaultCrm(
|
|
449
|
+
/** The default shared CRM graph: people (with the application/member pipeline)
|
|
450
|
+
* plus businesses and the `works_at` relation, in both modes. */
|
|
451
|
+
declare function defaultCrm(_mode: ChapterMode): CrmConfig;
|
|
407
452
|
|
|
408
453
|
/** The `groups` row (attrs) for `chapter` mode. Missing config falls back to
|
|
409
454
|
* empty copy / defaults, so a minimal config still provisions cleanly. */
|
|
@@ -783,6 +828,27 @@ interface SharedPerson {
|
|
|
783
828
|
linkedin?: string;
|
|
784
829
|
hubRecordId: string;
|
|
785
830
|
}
|
|
831
|
+
/** Convenience wire shape for sharing the default `company` CRM type. */
|
|
832
|
+
interface SharedBusiness {
|
|
833
|
+
type: "company";
|
|
834
|
+
name: string;
|
|
835
|
+
domain?: string;
|
|
836
|
+
industry?: string;
|
|
837
|
+
location?: string;
|
|
838
|
+
linkedin?: string;
|
|
839
|
+
notes?: string;
|
|
840
|
+
hubRecordId: string;
|
|
841
|
+
}
|
|
842
|
+
/** Versioned generic wire shape. `input` is validated against the follower's
|
|
843
|
+
* own CRM type before any write, so a leader cannot smuggle undeclared fields. */
|
|
844
|
+
interface SharedRecord {
|
|
845
|
+
version: 1;
|
|
846
|
+
type: string;
|
|
847
|
+
hubRecordId: string;
|
|
848
|
+
input: Record<string, unknown>;
|
|
849
|
+
}
|
|
850
|
+
/** Safe defaults when a target does not declare an explicit field allowlist. */
|
|
851
|
+
declare const DEFAULT_SHARE_FIELDS: Readonly<Record<string, readonly string[]>>;
|
|
786
852
|
/** Map a shared prospect to a crm `person` input (only the fields the default
|
|
787
853
|
* person type accepts). Name falls back to first+last, then the email. */
|
|
788
854
|
declare function sharedPersonInput(person: SharedPerson): Record<string, unknown>;
|
|
@@ -794,12 +860,20 @@ interface ProjectionDeps {
|
|
|
794
860
|
now: () => number;
|
|
795
861
|
newId: () => string;
|
|
796
862
|
}
|
|
863
|
+
/** CRM provenance tag used as the durable leader-id → local-record mapping. */
|
|
864
|
+
declare function networkSourceTag(type: string, hubRecordId: string): string;
|
|
865
|
+
/** Normalize the backwards-compatible person/business shapes into the versioned
|
|
866
|
+
* generic record envelope the receiver writes. */
|
|
867
|
+
declare function normalizeSharedRecord(record: SharedPerson | SharedBusiness | SharedRecord): SharedRecord;
|
|
868
|
+
/** Build the allowlisted payload sent from one leader CRM record to a target.
|
|
869
|
+
* A target with an explicit `fields` map only accepts the types it lists. */
|
|
870
|
+
declare function sharedRecordFromCrm(crm: Crm, record: CrmRecord, target: ResolvedNetworkTarget): SharedRecord;
|
|
797
871
|
/**
|
|
798
|
-
* Upsert a
|
|
799
|
-
*
|
|
800
|
-
*
|
|
872
|
+
* Upsert a leader-shared CRM record into this follower. Accepts the original
|
|
873
|
+
* person wire shape plus the versioned generic shape. Re-shares resolve through
|
|
874
|
+
* a durable provenance tag; people/businesses also converge by natural key.
|
|
801
875
|
*/
|
|
802
|
-
declare function projectSharedRecord(deps: ProjectionDeps,
|
|
876
|
+
declare function projectSharedRecord(deps: ProjectionDeps, shared: SharedPerson | SharedBusiness | SharedRecord): Promise<{
|
|
803
877
|
recordId: string;
|
|
804
878
|
}>;
|
|
805
879
|
/** An arriving applicant, as far as the CRM projection cares. `extra` carries the
|
|
@@ -1271,4 +1345,4 @@ type ApplicationBookingPatch = {
|
|
|
1271
1345
|
* already there (never backward). */
|
|
1272
1346
|
declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
|
|
1273
1347
|
|
|
1274
|
-
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 ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedPerson, type StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
|
|
1348
|
+
export { type AccountModel, type AdminNotificationTrigger, type Applicant, type ApplicationBookingPatch, type ApplicationRecord, type ApplicationSummary, type Attr, type AttrType, type Chapter, type ChapterApplication, type ChapterAuth, type ChapterBrand, type ChapterConfig, type ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, DEFAULT_SHARE_FIELDS, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedNetwork, type ResolvedNetworkTarget, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedBusiness, type SharedPerson, type SharedRecord, type StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CrmConfig, Crm } from '@odla-ai/crm';
|
|
1
|
+
import { CrmConfig, Crm, CrmRecord } from '@odla-ai/crm';
|
|
2
2
|
|
|
3
3
|
/** Which feature profile a site runs. `chapter` is the full public member site
|
|
4
4
|
* (join, Stripe membership, booking, member area, admin, CRM); `hub` is
|
|
@@ -68,6 +68,9 @@ interface ChapterBrand {
|
|
|
68
68
|
body?: string;
|
|
69
69
|
numeral?: string;
|
|
70
70
|
};
|
|
71
|
+
/** Short glyph used in compact admin chrome. Defaults to the first three
|
|
72
|
+
* letters of the chapter name. */
|
|
73
|
+
badge?: string;
|
|
71
74
|
wordmark?: string;
|
|
72
75
|
tagline?: string;
|
|
73
76
|
/** Header/footer nav sections for the web-component chrome: label → entries. */
|
|
@@ -77,6 +80,40 @@ interface ChapterBrand {
|
|
|
77
80
|
}>>;
|
|
78
81
|
logos?: string;
|
|
79
82
|
}
|
|
83
|
+
/** One follower site a leader can push CRM records to. The secret VALUE never
|
|
84
|
+
* lives in config: `secretName` names the leader tenant's vaulted copy of the
|
|
85
|
+
* follower's `network_share_secret`. */
|
|
86
|
+
interface ChapterNetworkTarget {
|
|
87
|
+
/** Stable lowercase slug used by the UI, audit tags, and default secret name. */
|
|
88
|
+
id: string;
|
|
89
|
+
/** Human-facing site name. Defaults to `id`. */
|
|
90
|
+
name?: string;
|
|
91
|
+
/** Absolute follower origin, e.g. `https://silver.example.com`. */
|
|
92
|
+
url: string;
|
|
93
|
+
/** Leader-vault key holding this follower's share secret. Defaults to
|
|
94
|
+
* `network_share_<id-with-underscores>`. */
|
|
95
|
+
secretName?: string;
|
|
96
|
+
/** Per-record-type field allowlist. If present, omitted types cannot be sent
|
|
97
|
+
* to this target. Without it, package-safe person/company defaults apply. */
|
|
98
|
+
fields?: Record<string, readonly string[]>;
|
|
99
|
+
}
|
|
100
|
+
/** Leader/follower network configuration. Followers need no code config to
|
|
101
|
+
* receive records; they only vault `network_share_secret`. */
|
|
102
|
+
interface ChapterNetwork {
|
|
103
|
+
targets?: readonly ChapterNetworkTarget[];
|
|
104
|
+
}
|
|
105
|
+
/** A validated network target carried on the resolved chapter engine. */
|
|
106
|
+
interface ResolvedNetworkTarget {
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
url: string;
|
|
110
|
+
secretName: string;
|
|
111
|
+
fields?: Record<string, readonly string[]>;
|
|
112
|
+
}
|
|
113
|
+
/** Fully-resolved leader network configuration. */
|
|
114
|
+
interface ResolvedNetwork {
|
|
115
|
+
targets: readonly ResolvedNetworkTarget[];
|
|
116
|
+
}
|
|
80
117
|
/** Membership pricing for the group row (chapter mode). */
|
|
81
118
|
interface ChapterPrices {
|
|
82
119
|
standardCents: number;
|
|
@@ -232,6 +269,9 @@ interface ChapterConfig {
|
|
|
232
269
|
/** A `defineCrm()` config or a resolved `Crm`. Omit for the per-mode default. */
|
|
233
270
|
crm?: CrmConfig | Crm;
|
|
234
271
|
brand?: ChapterBrand;
|
|
272
|
+
/** Leader → follower delivery targets. Secret values stay in the leader
|
|
273
|
+
* tenant vault; see {@link ChapterNetworkTarget}. */
|
|
274
|
+
network?: ChapterNetwork;
|
|
235
275
|
thesis?: unknown;
|
|
236
276
|
/** Required in `chapter` mode. */
|
|
237
277
|
prices?: ChapterPrices;
|
|
@@ -318,6 +358,11 @@ interface Chapter {
|
|
|
318
358
|
name: string;
|
|
319
359
|
url?: string;
|
|
320
360
|
mode: ChapterMode;
|
|
361
|
+
/** Resolved site identity. `wordmark` always falls back to `name`, so the
|
|
362
|
+
* admin/member UI never needs a second brand declaration. */
|
|
363
|
+
brand: ChapterBrand;
|
|
364
|
+
/** Validated follower targets for leader-driven record pushes. */
|
|
365
|
+
network: ResolvedNetwork;
|
|
321
366
|
/** Resolved CRM engine (from `defineCrm`). */
|
|
322
367
|
crm: Crm;
|
|
323
368
|
/** Resolved auth policy (source, claim, ladder, super-admin tier). */
|
|
@@ -401,9 +446,9 @@ declare function chapterDb(mode: ChapterMode, auth: ResolvedAuth): {
|
|
|
401
446
|
rules: DbRules;
|
|
402
447
|
};
|
|
403
448
|
|
|
404
|
-
/** The
|
|
405
|
-
*
|
|
406
|
-
declare function defaultCrm(
|
|
449
|
+
/** The default shared CRM graph: people (with the application/member pipeline)
|
|
450
|
+
* plus businesses and the `works_at` relation, in both modes. */
|
|
451
|
+
declare function defaultCrm(_mode: ChapterMode): CrmConfig;
|
|
407
452
|
|
|
408
453
|
/** The `groups` row (attrs) for `chapter` mode. Missing config falls back to
|
|
409
454
|
* empty copy / defaults, so a minimal config still provisions cleanly. */
|
|
@@ -783,6 +828,27 @@ interface SharedPerson {
|
|
|
783
828
|
linkedin?: string;
|
|
784
829
|
hubRecordId: string;
|
|
785
830
|
}
|
|
831
|
+
/** Convenience wire shape for sharing the default `company` CRM type. */
|
|
832
|
+
interface SharedBusiness {
|
|
833
|
+
type: "company";
|
|
834
|
+
name: string;
|
|
835
|
+
domain?: string;
|
|
836
|
+
industry?: string;
|
|
837
|
+
location?: string;
|
|
838
|
+
linkedin?: string;
|
|
839
|
+
notes?: string;
|
|
840
|
+
hubRecordId: string;
|
|
841
|
+
}
|
|
842
|
+
/** Versioned generic wire shape. `input` is validated against the follower's
|
|
843
|
+
* own CRM type before any write, so a leader cannot smuggle undeclared fields. */
|
|
844
|
+
interface SharedRecord {
|
|
845
|
+
version: 1;
|
|
846
|
+
type: string;
|
|
847
|
+
hubRecordId: string;
|
|
848
|
+
input: Record<string, unknown>;
|
|
849
|
+
}
|
|
850
|
+
/** Safe defaults when a target does not declare an explicit field allowlist. */
|
|
851
|
+
declare const DEFAULT_SHARE_FIELDS: Readonly<Record<string, readonly string[]>>;
|
|
786
852
|
/** Map a shared prospect to a crm `person` input (only the fields the default
|
|
787
853
|
* person type accepts). Name falls back to first+last, then the email. */
|
|
788
854
|
declare function sharedPersonInput(person: SharedPerson): Record<string, unknown>;
|
|
@@ -794,12 +860,20 @@ interface ProjectionDeps {
|
|
|
794
860
|
now: () => number;
|
|
795
861
|
newId: () => string;
|
|
796
862
|
}
|
|
863
|
+
/** CRM provenance tag used as the durable leader-id → local-record mapping. */
|
|
864
|
+
declare function networkSourceTag(type: string, hubRecordId: string): string;
|
|
865
|
+
/** Normalize the backwards-compatible person/business shapes into the versioned
|
|
866
|
+
* generic record envelope the receiver writes. */
|
|
867
|
+
declare function normalizeSharedRecord(record: SharedPerson | SharedBusiness | SharedRecord): SharedRecord;
|
|
868
|
+
/** Build the allowlisted payload sent from one leader CRM record to a target.
|
|
869
|
+
* A target with an explicit `fields` map only accepts the types it lists. */
|
|
870
|
+
declare function sharedRecordFromCrm(crm: Crm, record: CrmRecord, target: ResolvedNetworkTarget): SharedRecord;
|
|
797
871
|
/**
|
|
798
|
-
* Upsert a
|
|
799
|
-
*
|
|
800
|
-
*
|
|
872
|
+
* Upsert a leader-shared CRM record into this follower. Accepts the original
|
|
873
|
+
* person wire shape plus the versioned generic shape. Re-shares resolve through
|
|
874
|
+
* a durable provenance tag; people/businesses also converge by natural key.
|
|
801
875
|
*/
|
|
802
|
-
declare function projectSharedRecord(deps: ProjectionDeps,
|
|
876
|
+
declare function projectSharedRecord(deps: ProjectionDeps, shared: SharedPerson | SharedBusiness | SharedRecord): Promise<{
|
|
803
877
|
recordId: string;
|
|
804
878
|
}>;
|
|
805
879
|
/** An arriving applicant, as far as the CRM projection cares. `extra` carries the
|
|
@@ -1271,4 +1345,4 @@ type ApplicationBookingPatch = {
|
|
|
1271
1345
|
* already there (never backward). */
|
|
1272
1346
|
declare function applicationBookingUpdate(currentStatus: string, startAt: number, htmlLink?: string | null): ApplicationBookingPatch;
|
|
1273
1347
|
|
|
1274
|
-
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 ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedPerson, type StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
|
|
1348
|
+
export { type AccountModel, type AdminNotificationTrigger, type Applicant, type ApplicationBookingPatch, type ApplicationRecord, type ApplicationSummary, type Attr, type AttrType, type Chapter, type ChapterApplication, type ChapterAuth, type ChapterBrand, type ChapterConfig, type ChapterDb, type ChapterEmails, type ChapterIntegrationDescriptor, type ChapterIntegrationOptions, type ChapterMode, type ChapterNetwork, type ChapterNetworkTarget, type ChapterOperations, type ChapterPipeline, type ChapterPolicy, type ChapterPrices, type ChapterScheduling, type ChapterSends, type ClerkInviteInput, type ClerkResult, type ClerkUserInput, type ClerkUserRecord, DEFAULT_SHARE_FIELDS, type DbOp, type DbRules, type DbSchema, type DeliveryDecision, type EmailGroup, type EmailTemplate, type EmailTemplateRow, type Entity, type ExistingMeeting, type GuardResult, type IntegrationDescriptor, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type IntegrationSync, type JoinConfigGroup, type LiveEvent, type MailSender, type MeetingForReconcile, type MeetingRecord, type MeetingReschedulePatch, type MemberApplication, type MemberSession, type NewMeetingRow, type NotifyDeps, type NotifyInput, type NotifyResult, type PaymentsGroup, type ProjectionDeps, type ReconcileDecision, type ResolvedApplication, type ResolvedAuth, type ResolvedNetwork, type ResolvedNetworkTarget, type ResolvedOperations, type ResolvedPipeline, type ResolvedScheduling, type ResolvedSends, type RoleChangeContext, type Rule, SCHEDULING_DEFAULTS, type SchedulingErrors, type SecretMode, type SecretStore, type SessionUser, type SharedBusiness, type SharedPerson, type SharedRecord, type StripeEvent, type StripeResult, type SubmitResult, type WebhookEvent, applicantProfile, applicationBookingUpdate, applicationSummary, backfillCrm, bookingDecision, brandTokens, bucketSeries, buildGroupSeed, canApprove, canBook, canChangeRole, canTransition, canceledPatch, chapterDb, clampArray, clerkGetUser, clerkGetUserByEmail, clerkIntegration, clerkInviteRequest, clerkListUsers, clerkSetRole, clerkUserRequest, createChapterIntegration, createClerkInvitation, createClerkUser, defaultCrm, defineChapter, emailGroupFrom, endForSlot, findApplicationRef, firstPaymentPatch, getVaultSecret, hasDisclaimerAck, introIdempotencyKey, isAdminRole, isAlreadySent, isReconcilable, isSlotAvailable, isValidEmail, joinConfig, meetingCreateRow, meetingRescheduleUpdate, memberApplication, memberSession, networkSourceTag, normalizeSharedRecord, normalizeWebhookEvent, paymentsReady, personInputFromApp, planDelivery, projectApplicant, projectSharedRecord, reconcileMeetings, refundedPatch, render, renderSummary, renderTemplateBody, renewalPatch, resolveApplication, resolveAuth, resolvePipeline, resolveScheduling, roleFromClaim, sendTemplated, sharedPersonInput, sharedRecordFromCrm, slotWindow, stageIndex, stripeCall, stripeForm, subAnnualCents, submitApplication, subscriptionIdempotencyKey, syncApplicationToCrm, validateScheduling, verifyStripeSignature, webhookMutationId };
|
package/dist/index.js
CHANGED
|
@@ -195,16 +195,10 @@ function companyType() {
|
|
|
195
195
|
facets: { rank: "manual" }
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
|
-
function defaultCrm(
|
|
199
|
-
if (mode === "hub") {
|
|
200
|
-
return {
|
|
201
|
-
types: { person: personType(), company: companyType() },
|
|
202
|
-
relations: { works_at: { from: "person", to: "company", label: "works at", reverseLabel: "team" } },
|
|
203
|
-
templates: TEMPLATES
|
|
204
|
-
};
|
|
205
|
-
}
|
|
198
|
+
function defaultCrm(_mode) {
|
|
206
199
|
return {
|
|
207
|
-
types: { person: personType() },
|
|
200
|
+
types: { person: personType(), company: companyType() },
|
|
201
|
+
relations: { works_at: { from: "person", to: "company", label: "works at", reverseLabel: "team" } },
|
|
208
202
|
templates: TEMPLATES
|
|
209
203
|
};
|
|
210
204
|
}
|
|
@@ -487,6 +481,62 @@ function joinConfig(group, paymentsReady2) {
|
|
|
487
481
|
|
|
488
482
|
// src/config.ts
|
|
489
483
|
var SLUG = /^[a-z0-9][a-z0-9-]{1,62}$/;
|
|
484
|
+
var FIELD = /^[a-z][a-zA-Z0-9_]*$/;
|
|
485
|
+
function resolveNetwork(config, crm) {
|
|
486
|
+
const seen = /* @__PURE__ */ new Set();
|
|
487
|
+
const targets = [];
|
|
488
|
+
for (const target of config.network?.targets ?? []) {
|
|
489
|
+
if (!SLUG.test(target.id)) {
|
|
490
|
+
throw new Error(`defineChapter.network.targets.id: must be a lowercase slug \u2014 got ${JSON.stringify(target.id)}`);
|
|
491
|
+
}
|
|
492
|
+
if (seen.has(target.id)) throw new Error(`defineChapter.network.targets: duplicate target "${target.id}"`);
|
|
493
|
+
seen.add(target.id);
|
|
494
|
+
let url;
|
|
495
|
+
try {
|
|
496
|
+
url = new URL(target.url);
|
|
497
|
+
} catch {
|
|
498
|
+
throw new Error(`defineChapter.network.targets.${target.id}.url: must be an absolute URL`);
|
|
499
|
+
}
|
|
500
|
+
const local = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
|
|
501
|
+
if (url.protocol !== "https:" && !(local && url.protocol === "http:")) {
|
|
502
|
+
throw new Error(`defineChapter.network.targets.${target.id}.url: must use https (http is allowed only for localhost)`);
|
|
503
|
+
}
|
|
504
|
+
const fields = target.fields;
|
|
505
|
+
for (const [type, names] of Object.entries(fields ?? {})) {
|
|
506
|
+
if (!FIELD.test(type) || names.length === 0 || names.some((name) => !FIELD.test(name))) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
`defineChapter.network.targets.${target.id}.fields.${type}: type and field names must be non-empty CRM identifiers`
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
const def = crm.config.types[type];
|
|
512
|
+
if (!def) throw new Error(`defineChapter.network.targets.${target.id}.fields: unknown leader CRM type "${type}"`);
|
|
513
|
+
const unknown = names.filter((field) => !def.fields[field]);
|
|
514
|
+
if (unknown.length > 0) {
|
|
515
|
+
throw new Error(
|
|
516
|
+
`defineChapter.network.targets.${target.id}.fields.${type}: ${unknown.map((field) => `"${field}"`).join(", ")} not declared on the leader CRM type`
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
const nameField = def.nameField ?? "name";
|
|
520
|
+
if (!names.includes(nameField)) {
|
|
521
|
+
throw new Error(
|
|
522
|
+
`defineChapter.network.targets.${target.id}.fields.${type}: must include required name field "${nameField}"`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const secretName = target.secretName?.trim() || `network_share_${target.id.replaceAll("-", "_")}`;
|
|
527
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_-]{1,127}$/.test(secretName)) {
|
|
528
|
+
throw new Error(`defineChapter.network.targets.${target.id}.secretName: must be a vault-key identifier`);
|
|
529
|
+
}
|
|
530
|
+
targets.push({
|
|
531
|
+
id: target.id,
|
|
532
|
+
name: target.name?.trim() || target.id,
|
|
533
|
+
url: url.origin,
|
|
534
|
+
secretName,
|
|
535
|
+
...fields ? { fields } : {}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return { targets };
|
|
539
|
+
}
|
|
490
540
|
function isResolvedCrm(x) {
|
|
491
541
|
return !!x && typeof x === "object" && "prepare" in x && typeof x.prepare === "function";
|
|
492
542
|
}
|
|
@@ -511,6 +561,8 @@ function defineChapter(config) {
|
|
|
511
561
|
const auth = resolveAuth(mode, config.auth);
|
|
512
562
|
const pipeline = resolvePipeline(config.pipeline);
|
|
513
563
|
const application = resolveApplication(config.application);
|
|
564
|
+
const brand = { ...config.brand, wordmark: config.brand?.wordmark ?? name };
|
|
565
|
+
const network = resolveNetwork(config, crm);
|
|
514
566
|
const { schema, rules } = chapterDb(mode, auth);
|
|
515
567
|
const services = config.services ?? ["db", "calendar", "o11y"];
|
|
516
568
|
const account = config.account ?? "none";
|
|
@@ -563,6 +615,8 @@ function defineChapter(config) {
|
|
|
563
615
|
id: id2,
|
|
564
616
|
name,
|
|
565
617
|
mode,
|
|
618
|
+
brand,
|
|
619
|
+
network,
|
|
566
620
|
crm,
|
|
567
621
|
auth,
|
|
568
622
|
pipeline,
|
|
@@ -811,6 +865,10 @@ function canceledPatch() {
|
|
|
811
865
|
|
|
812
866
|
// src/network.ts
|
|
813
867
|
import { createRecord, updateRecord } from "@odla-ai/crm";
|
|
868
|
+
var DEFAULT_SHARE_FIELDS = {
|
|
869
|
+
person: ["name", "email", "firstName", "lastName", "phone", "linkedin"],
|
|
870
|
+
company: ["name", "domain", "industry", "location", "linkedin", "notes"]
|
|
871
|
+
};
|
|
814
872
|
function sharedPersonInput(person) {
|
|
815
873
|
const email = person.email.toLowerCase();
|
|
816
874
|
const fullName = [person.firstName, person.lastName].filter(Boolean).join(" ").trim();
|
|
@@ -822,6 +880,52 @@ function sharedPersonInput(person) {
|
|
|
822
880
|
if (person.linkedin) input.linkedin = person.linkedin;
|
|
823
881
|
return input;
|
|
824
882
|
}
|
|
883
|
+
function shortHash(value) {
|
|
884
|
+
let a = 2166136261;
|
|
885
|
+
let b = 2654435769;
|
|
886
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
887
|
+
const n = value.charCodeAt(i);
|
|
888
|
+
a = Math.imul(a ^ n, 16777619);
|
|
889
|
+
b = Math.imul(b ^ n, 2246822507);
|
|
890
|
+
}
|
|
891
|
+
return `${(a >>> 0).toString(36)}${(b >>> 0).toString(36)}`;
|
|
892
|
+
}
|
|
893
|
+
function networkSourceTag(type, hubRecordId) {
|
|
894
|
+
const typeKey = type.toLowerCase();
|
|
895
|
+
const readable = /^[a-z0-9_-]+$/.test(hubRecordId);
|
|
896
|
+
const raw = `network:${typeKey}:${hubRecordId}`;
|
|
897
|
+
if (readable && raw.length <= 64) return raw;
|
|
898
|
+
return `network:${typeKey.slice(0, 20)}:${shortHash(`${type}\0${hubRecordId}`)}`;
|
|
899
|
+
}
|
|
900
|
+
function normalizeSharedRecord(record) {
|
|
901
|
+
if ("input" in record) return { version: 1, type: record.type, hubRecordId: record.hubRecordId, input: record.input };
|
|
902
|
+
if ("type" in record && record.type === "company") {
|
|
903
|
+
const input = { name: record.name };
|
|
904
|
+
for (const key of ["domain", "industry", "location", "linkedin", "notes"]) {
|
|
905
|
+
if (record[key]) input[key] = record[key];
|
|
906
|
+
}
|
|
907
|
+
return { version: 1, type: "company", hubRecordId: record.hubRecordId, input };
|
|
908
|
+
}
|
|
909
|
+
return { version: 1, type: "person", hubRecordId: record.hubRecordId, input: sharedPersonInput(record) };
|
|
910
|
+
}
|
|
911
|
+
function sharedRecordFromCrm(crm, record, target) {
|
|
912
|
+
if (target.fields && !target.fields[record.type]) {
|
|
913
|
+
throw new Error(`${target.name} does not accept "${record.type}" records`);
|
|
914
|
+
}
|
|
915
|
+
const def = crm.type(record.type);
|
|
916
|
+
const fields = target.fields?.[record.type] ?? DEFAULT_SHARE_FIELDS[record.type];
|
|
917
|
+
if (!fields) {
|
|
918
|
+
throw new Error(`${target.name} requires an explicit field allowlist for "${record.type}" records`);
|
|
919
|
+
}
|
|
920
|
+
const nameField = def.nameField ?? "name";
|
|
921
|
+
const input = {};
|
|
922
|
+
for (const field of /* @__PURE__ */ new Set([nameField, ...fields])) {
|
|
923
|
+
const value = record.fields?.[field];
|
|
924
|
+
if (value !== void 0) input[field] = value;
|
|
925
|
+
}
|
|
926
|
+
if (input[nameField] === void 0) input[nameField] = record.name;
|
|
927
|
+
return { version: 1, type: record.type, hubRecordId: record.id, input };
|
|
928
|
+
}
|
|
825
929
|
async function upsertPerson(deps, opts) {
|
|
826
930
|
const email = opts.email.toLowerCase();
|
|
827
931
|
const crmDeps = { crm: deps.crm, db: deps.db, now: deps.now, newId: deps.newId };
|
|
@@ -834,8 +938,65 @@ async function upsertPerson(deps, opts) {
|
|
|
834
938
|
const created = await createRecord(crmDeps, { type: "person", input: opts.input, mutationId: opts.mutationId });
|
|
835
939
|
return { recordId: created.id };
|
|
836
940
|
}
|
|
837
|
-
async function
|
|
838
|
-
|
|
941
|
+
async function findSharedRecord(deps, record, tag) {
|
|
942
|
+
const mapped = await deps.db.query({ crm_tag: { $: { where: { tag }, limit: 1 } } });
|
|
943
|
+
const mappedId = mapped.crm_tag?.[0]?.recordId;
|
|
944
|
+
if (typeof mappedId === "string") {
|
|
945
|
+
const found = await deps.db.query({ crm_record: { $: { where: { id: mappedId }, limit: 1 } } });
|
|
946
|
+
if (found.crm_record?.[0]) return found.crm_record[0];
|
|
947
|
+
}
|
|
948
|
+
const def = deps.crm.type(record.type);
|
|
949
|
+
const emailField = def.emailField;
|
|
950
|
+
if (emailField && typeof record.input[emailField] === "string") {
|
|
951
|
+
const primaryEmail = record.input[emailField].toLowerCase();
|
|
952
|
+
const found = await deps.db.query({
|
|
953
|
+
crm_record: { $: { where: { type: record.type, primaryEmail }, limit: 1 } }
|
|
954
|
+
});
|
|
955
|
+
if (found.crm_record?.[0]) return found.crm_record[0];
|
|
956
|
+
}
|
|
957
|
+
const domain = record.input.domain;
|
|
958
|
+
const domainSlot = def.fields.domain?.slot;
|
|
959
|
+
if (typeof domain === "string" && domainSlot) {
|
|
960
|
+
const found = await deps.db.query({
|
|
961
|
+
crm_record: { $: { where: { type: record.type, [domainSlot]: domain }, limit: 1 } }
|
|
962
|
+
});
|
|
963
|
+
if (found.crm_record?.[0]) return found.crm_record[0];
|
|
964
|
+
}
|
|
965
|
+
const nameField = def.nameField ?? "name";
|
|
966
|
+
const name = record.input[nameField];
|
|
967
|
+
if (record.type === "company" && typeof name === "string" && name.trim()) {
|
|
968
|
+
const found = await deps.db.query({
|
|
969
|
+
crm_record: { $: { where: { type: record.type, name: name.trim() }, limit: 1 } }
|
|
970
|
+
});
|
|
971
|
+
if (found.crm_record?.[0]) return found.crm_record[0];
|
|
972
|
+
}
|
|
973
|
+
return void 0;
|
|
974
|
+
}
|
|
975
|
+
async function projectSharedRecord(deps, shared) {
|
|
976
|
+
const record = normalizeSharedRecord(shared);
|
|
977
|
+
if (!record.type.trim() || !record.hubRecordId.trim()) {
|
|
978
|
+
throw new Error("type and hubRecordId must be non-empty");
|
|
979
|
+
}
|
|
980
|
+
const tag = networkSourceTag(record.type, record.hubRecordId);
|
|
981
|
+
const crmDeps = { crm: deps.crm, db: deps.db, now: deps.now, newId: deps.newId };
|
|
982
|
+
const existing = await findSharedRecord(deps, record, tag);
|
|
983
|
+
let recordId;
|
|
984
|
+
if (existing && typeof existing.id === "string") {
|
|
985
|
+
await updateRecord(crmDeps, { id: existing.id, input: record.input });
|
|
986
|
+
recordId = existing.id;
|
|
987
|
+
} else {
|
|
988
|
+
recordId = `network_${shortHash(`${record.type}\0${record.hubRecordId}`)}`;
|
|
989
|
+
await createRecord({ ...crmDeps, newId: () => recordId }, {
|
|
990
|
+
type: record.type,
|
|
991
|
+
input: record.input,
|
|
992
|
+
mutationId: `share-create:${tag}`
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
await deps.db.transact(
|
|
996
|
+
[{ t: "update", ns: "crm_tag", id: tag, attrs: { key: `${recordId}:${tag}`, recordId, tag, createdAt: deps.now() } }],
|
|
997
|
+
{ mutationId: `share-map:${tag}:${recordId}` }
|
|
998
|
+
);
|
|
999
|
+
return { recordId };
|
|
839
1000
|
}
|
|
840
1001
|
async function projectApplicant(deps, applicant) {
|
|
841
1002
|
const base = sharedPersonInput({
|
|
@@ -1370,6 +1531,7 @@ function applicationBookingUpdate(currentStatus, startAt, htmlLink) {
|
|
|
1370
1531
|
};
|
|
1371
1532
|
}
|
|
1372
1533
|
export {
|
|
1534
|
+
DEFAULT_SHARE_FIELDS,
|
|
1373
1535
|
SCHEDULING_DEFAULTS,
|
|
1374
1536
|
applicantProfile,
|
|
1375
1537
|
applicationBookingUpdate,
|
|
@@ -1415,6 +1577,8 @@ export {
|
|
|
1415
1577
|
meetingRescheduleUpdate,
|
|
1416
1578
|
memberApplication,
|
|
1417
1579
|
memberSession,
|
|
1580
|
+
networkSourceTag,
|
|
1581
|
+
normalizeSharedRecord,
|
|
1418
1582
|
normalizeWebhookEvent,
|
|
1419
1583
|
paymentsReady,
|
|
1420
1584
|
personInputFromApp,
|
|
@@ -1434,6 +1598,7 @@ export {
|
|
|
1434
1598
|
roleFromClaim,
|
|
1435
1599
|
sendTemplated,
|
|
1436
1600
|
sharedPersonInput,
|
|
1601
|
+
sharedRecordFromCrm,
|
|
1437
1602
|
slotWindow,
|
|
1438
1603
|
stageIndex,
|
|
1439
1604
|
stripeCall,
|