@odla-ai/chapter 0.29.0 → 0.31.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/dist/chunk-IVXVECKR.js +15 -0
- package/dist/chunk-IVXVECKR.js.map +1 -0
- package/dist/{chunk-IOHVFBXH.js → chunk-QWMP6TSM.js} +75 -34
- package/dist/chunk-QWMP6TSM.js.map +1 -0
- package/dist/{copy-context-efEMF_aD.d.ts → copy-context-HAhNTZdl.d.ts} +54 -19
- package/dist/index.cjs +176 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -25
- package/dist/index.d.ts +174 -25
- package/dist/index.js +158 -6
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +2 -2
- package/dist/ui/conditions/index.d.ts +29 -0
- package/dist/ui/conditions/index.js +32 -0
- package/dist/ui/conditions/index.js.map +1 -0
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +10 -3
- package/dist/ui/member/index.d.ts +62 -3
- package/dist/ui/member/index.js +10 -3
- package/dist/worker/index.cjs +146 -39
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +54 -19
- package/dist/worker/index.d.ts +54 -19
- package/dist/worker/index.js +115 -8
- package/dist/worker/index.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunk-IOHVFBXH.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ __export(index_exports, {
|
|
|
31
31
|
brandTokens: () => brandTokens,
|
|
32
32
|
bucketSeries: () => bucketSeries,
|
|
33
33
|
buildGroupSeed: () => buildGroupSeed,
|
|
34
|
+
buildTierSeeds: () => buildTierSeeds,
|
|
34
35
|
canApprove: () => canApprove,
|
|
35
36
|
canBook: () => canBook,
|
|
36
37
|
canChangeRole: () => canChangeRole,
|
|
@@ -55,6 +56,7 @@ __export(index_exports, {
|
|
|
55
56
|
emailGroupFrom: () => emailGroupFrom,
|
|
56
57
|
endForSlot: () => endForSlot,
|
|
57
58
|
findApplicationRef: () => findApplicationRef,
|
|
59
|
+
findTier: () => findTier,
|
|
58
60
|
firstPaymentPatch: () => firstPaymentPatch,
|
|
59
61
|
formatChapterCopy: () => formatChapterCopy,
|
|
60
62
|
formationFields: () => formationFields,
|
|
@@ -67,7 +69,10 @@ __export(index_exports, {
|
|
|
67
69
|
isSlotAvailable: () => isSlotAvailable,
|
|
68
70
|
isValidEmail: () => isValidEmail,
|
|
69
71
|
joinConfig: () => joinConfig,
|
|
72
|
+
joinConfigTiers: () => joinConfigTiers,
|
|
70
73
|
leaderCrmConfig: () => leaderCrmConfig,
|
|
74
|
+
loadOfferableTiers: () => loadOfferableTiers,
|
|
75
|
+
loadTiers: () => loadTiers,
|
|
71
76
|
meetingCreateRow: () => meetingCreateRow,
|
|
72
77
|
meetingRescheduleUpdate: () => meetingRescheduleUpdate,
|
|
73
78
|
memberApplication: () => memberApplication,
|
|
@@ -75,6 +80,7 @@ __export(index_exports, {
|
|
|
75
80
|
networkSourceTag: () => networkSourceTag,
|
|
76
81
|
normalizeSharedRecord: () => normalizeSharedRecord,
|
|
77
82
|
normalizeWebhookEvent: () => normalizeWebhookEvent,
|
|
83
|
+
offerableTiers: () => offerableTiers,
|
|
78
84
|
paymentsReady: () => paymentsReady,
|
|
79
85
|
personInputFromApp: () => personInputFromApp,
|
|
80
86
|
planDelivery: () => planDelivery,
|
|
@@ -92,6 +98,7 @@ __export(index_exports, {
|
|
|
92
98
|
resolveLeaderFormation: () => resolveLeaderFormation,
|
|
93
99
|
resolvePipeline: () => resolvePipeline,
|
|
94
100
|
resolveScheduling: () => resolveScheduling,
|
|
101
|
+
resolveTiers: () => resolveTiers,
|
|
95
102
|
roleFromClaim: () => roleFromClaim,
|
|
96
103
|
sendTemplated: () => sendTemplated,
|
|
97
104
|
sharedPersonInput: () => sharedPersonInput,
|
|
@@ -104,6 +111,8 @@ __export(index_exports, {
|
|
|
104
111
|
submitApplication: () => submitApplication,
|
|
105
112
|
subscriptionIdempotencyKey: () => subscriptionIdempotencyKey,
|
|
106
113
|
syncApplicationToCrm: () => syncApplicationToCrm,
|
|
114
|
+
tierIsFree: () => tierIsFree,
|
|
115
|
+
tierPayable: () => tierPayable,
|
|
107
116
|
updateClerkUserMetadata: () => updateClerkUserMetadata,
|
|
108
117
|
updateClerkUserMetadataByEmail: () => updateClerkUserMetadataByEmail,
|
|
109
118
|
validateScheduling: () => validateScheduling,
|
|
@@ -113,7 +122,7 @@ __export(index_exports, {
|
|
|
113
122
|
module.exports = __toCommonJS(index_exports);
|
|
114
123
|
|
|
115
124
|
// src/config.ts
|
|
116
|
-
var
|
|
125
|
+
var import_crm2 = require("@odla-ai/crm");
|
|
117
126
|
|
|
118
127
|
// src/schema.ts
|
|
119
128
|
function attr(type, flags = {}) {
|
|
@@ -178,6 +187,8 @@ var applications = {
|
|
|
178
187
|
phone: attr("string", { optional: true }),
|
|
179
188
|
state: attr("string", { optional: true }),
|
|
180
189
|
groupId: attr("string", { indexed: true, optional: true }),
|
|
190
|
+
// The tier this application joined on. Absent on rows that predate tiers.
|
|
191
|
+
tierId: attr("string", { indexed: true, optional: true }),
|
|
181
192
|
stripeCustomerId: attr("string", { indexed: true, optional: true }),
|
|
182
193
|
stripeSubscriptionId: attr("string", { indexed: true, optional: true }),
|
|
183
194
|
renewalAt: attr("number", { optional: true }),
|
|
@@ -209,6 +220,19 @@ var groups = {
|
|
|
209
220
|
createdAt: attr("number", { indexed: true })
|
|
210
221
|
}
|
|
211
222
|
};
|
|
223
|
+
var tiers = {
|
|
224
|
+
attrs: {
|
|
225
|
+
id: id(),
|
|
226
|
+
groupId: attr("string", { indexed: true }),
|
|
227
|
+
name: attr("string"),
|
|
228
|
+
priceCents: attr("number"),
|
|
229
|
+
stripePriceId: attr("string", { optional: true }),
|
|
230
|
+
blurb: attr("string", { optional: true }),
|
|
231
|
+
sortOrder: attr("number", { indexed: true }),
|
|
232
|
+
active: attr("boolean", { indexed: true }),
|
|
233
|
+
createdAt: attr("number", { indexed: true })
|
|
234
|
+
}
|
|
235
|
+
};
|
|
212
236
|
var meetings = {
|
|
213
237
|
attrs: {
|
|
214
238
|
id: id(),
|
|
@@ -250,6 +274,7 @@ function chapterDb(mode, auth, includeNetworkNotes = false) {
|
|
|
250
274
|
if (mode === "chapter") {
|
|
251
275
|
entities.applications = applications;
|
|
252
276
|
entities.groups = groups;
|
|
277
|
+
entities.tiers = tiers;
|
|
253
278
|
entities.meetings = meetings;
|
|
254
279
|
entities.emailLog = emailLog;
|
|
255
280
|
}
|
|
@@ -510,6 +535,9 @@ function canApprove(status, p) {
|
|
|
510
535
|
return p.approvableFrom.includes(status);
|
|
511
536
|
}
|
|
512
537
|
|
|
538
|
+
// src/member.ts
|
|
539
|
+
var import_crm = require("@odla-ai/crm");
|
|
540
|
+
|
|
513
541
|
// src/application-id.ts
|
|
514
542
|
var APPLICATION_ID_DOMAIN = "odla-ai/chapter/application/v1:";
|
|
515
543
|
async function applicationIdForSubmission(submissionId) {
|
|
@@ -538,9 +566,21 @@ function resolveApplication(a) {
|
|
|
538
566
|
if (a?.crmFields !== void 0 && (!Array.isArray(a.crmFields) || !a.crmFields.every((f) => typeof f === "string" && f !== ""))) {
|
|
539
567
|
throw new Error("defineChapter.application.crmFields: must be an array of field-name strings");
|
|
540
568
|
}
|
|
569
|
+
const conditions = a?.conditions ?? {};
|
|
570
|
+
for (const [field, declared] of Object.entries(conditions)) {
|
|
571
|
+
for (const key of ["visibleWhen", "requiredWhen"]) {
|
|
572
|
+
const expression = declared?.[key];
|
|
573
|
+
if (expression === void 0) continue;
|
|
574
|
+
if (typeof expression !== "string" || !expression.trim()) {
|
|
575
|
+
throw new Error(`defineChapter.application.conditions.${field}.${key}: must be a non-empty CEL condition`);
|
|
576
|
+
}
|
|
577
|
+
(0, import_crm.assertFieldCondition)(expression, `defineChapter.application.conditions.${field}.${key}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
541
580
|
return {
|
|
542
581
|
required,
|
|
543
582
|
optional,
|
|
583
|
+
conditions,
|
|
544
584
|
maxLen: a?.maxLen ?? {},
|
|
545
585
|
defaultMaxLen: a?.defaultMaxLen ?? 2e3,
|
|
546
586
|
bodyCap: a?.bodyCap ?? 32768,
|
|
@@ -577,7 +617,9 @@ function applicantProfile(chapter, fields) {
|
|
|
577
617
|
}
|
|
578
618
|
async function submitApplication(db, chapter, fields, opts) {
|
|
579
619
|
const app = chapter.application;
|
|
580
|
-
|
|
620
|
+
const fieldStates = (0, import_crm.resolveFieldStates)(app.conditions ?? {}, fields, app.required);
|
|
621
|
+
for (const [f, state] of Object.entries(fieldStates)) {
|
|
622
|
+
if (!state.required) continue;
|
|
581
623
|
const v = fields[f];
|
|
582
624
|
if (typeof v !== "string" || v.trim() === "") return { ok: false, error: `${f} is required` };
|
|
583
625
|
}
|
|
@@ -596,10 +638,17 @@ async function submitApplication(db, chapter, fields, opts) {
|
|
|
596
638
|
const id2 = opts.submissionId ? await applicationIdForSubmission(opts.submissionId) : opts.newId();
|
|
597
639
|
const row = { id: id2, status: chapter.pipeline.initial, createdAt: opts.now };
|
|
598
640
|
for (const f of [...app.required, ...app.optional]) {
|
|
641
|
+
if (fieldStates[f]?.visible === false) continue;
|
|
599
642
|
if (typeof fields[f] === "string") row[f] = fields[f].trim();
|
|
600
643
|
}
|
|
601
644
|
if (fields.focus !== void 0) row.focus = clampArray(fields.focus, app.maxArrayLen);
|
|
602
645
|
if (opts.groupId) row.groupId = opts.groupId;
|
|
646
|
+
if (typeof fields.tierId === "string" && fields.tierId) {
|
|
647
|
+
if (opts.tierIds && !opts.tierIds.includes(fields.tierId)) {
|
|
648
|
+
return { ok: false, error: "tierId is not an offered tier" };
|
|
649
|
+
}
|
|
650
|
+
row.tierId = fields.tierId;
|
|
651
|
+
}
|
|
603
652
|
if (acked) row.disclaimerAckAt = opts.now;
|
|
604
653
|
const { duplicate } = await db.transact(
|
|
605
654
|
[{ t: "update", ns: "applications", id: id2, attrs: row }],
|
|
@@ -607,12 +656,15 @@ async function submitApplication(db, chapter, fields, opts) {
|
|
|
607
656
|
);
|
|
608
657
|
return { ok: true, id: id2, duplicate, status: chapter.pipeline.initial, disclaimerAckAt: acked ? opts.now : null };
|
|
609
658
|
}
|
|
610
|
-
function joinConfig(group, paymentsReady2) {
|
|
659
|
+
function joinConfig(group, paymentsReady2, tiers2 = [], conditions = {}) {
|
|
611
660
|
return {
|
|
612
661
|
id: group.id,
|
|
613
662
|
name: group.name,
|
|
614
663
|
standardPriceCents: group.standardPriceCents ?? 0,
|
|
615
664
|
foundingDiscountCents: group.foundingDiscountCents ?? 0,
|
|
665
|
+
tiers: [...tiers2],
|
|
666
|
+
// The browser evaluates the same conditions the server enforces.
|
|
667
|
+
conditions,
|
|
616
668
|
disclaimerText: group.disclaimerText ?? "",
|
|
617
669
|
refundPolicyText: group.refundPolicyText ?? "",
|
|
618
670
|
trustCopy: group.trustCopy ?? "",
|
|
@@ -1151,7 +1203,7 @@ function defineChapter(config) {
|
|
|
1151
1203
|
if (typeof name !== "string" || name.trim() === "") throw new Error("defineChapter.name: a non-empty name is required");
|
|
1152
1204
|
const mode = config.mode ?? "chapter";
|
|
1153
1205
|
if (mode !== "chapter" && mode !== "hub") throw new Error(`defineChapter.mode: must be "chapter" or "hub" \u2014 got ${JSON.stringify(config.mode)}`);
|
|
1154
|
-
const crm = isResolvedCrm(config.crm) ? config.crm : (0,
|
|
1206
|
+
const crm = isResolvedCrm(config.crm) ? config.crm : (0, import_crm2.defineCrm)(config.crm ?? defaultCrm(mode));
|
|
1155
1207
|
if (mode === "chapter") {
|
|
1156
1208
|
if (!config.emails || typeof config.emails.notificationEmail !== "string" || config.emails.notificationEmail === "") {
|
|
1157
1209
|
throw new Error("defineChapter.emails.notificationEmail: required in chapter mode");
|
|
@@ -1243,7 +1295,7 @@ function defineChapter(config) {
|
|
|
1243
1295
|
}
|
|
1244
1296
|
|
|
1245
1297
|
// src/leader.ts
|
|
1246
|
-
var
|
|
1298
|
+
var import_crm3 = require("@odla-ai/crm");
|
|
1247
1299
|
function leaderCrmConfig(options = {}) {
|
|
1248
1300
|
const base = options.base ?? defaultCrm("hub");
|
|
1249
1301
|
const defaultChapter = {
|
|
@@ -1402,16 +1454,72 @@ function leaderCrmConfig(options = {}) {
|
|
|
1402
1454
|
reverseLabel: "applicant"
|
|
1403
1455
|
};
|
|
1404
1456
|
}
|
|
1405
|
-
return (0,
|
|
1457
|
+
return (0, import_crm3.composeCrmConfigs)(base, portfolio);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// src/descriptor.ts
|
|
1461
|
+
var import_crm4 = require("@odla-ai/crm");
|
|
1462
|
+
|
|
1463
|
+
// src/tiers.ts
|
|
1464
|
+
function buildTierSeeds(groupId, configured = [], now = 0) {
|
|
1465
|
+
return configured.map((tier, index) => ({
|
|
1466
|
+
id: tier.id,
|
|
1467
|
+
groupId,
|
|
1468
|
+
name: tier.name,
|
|
1469
|
+
priceCents: tier.priceCents,
|
|
1470
|
+
...tier.stripePriceId ? { stripePriceId: tier.stripePriceId } : {},
|
|
1471
|
+
...tier.blurb ? { blurb: tier.blurb } : {},
|
|
1472
|
+
sortOrder: tier.sortOrder ?? index,
|
|
1473
|
+
active: tier.active !== false,
|
|
1474
|
+
createdAt: now
|
|
1475
|
+
}));
|
|
1476
|
+
}
|
|
1477
|
+
function tierIsFree(tier) {
|
|
1478
|
+
return !(tier.priceCents > 0) && !tier.stripePriceId;
|
|
1479
|
+
}
|
|
1480
|
+
function tierPayable(tier, group, hasSecretKey) {
|
|
1481
|
+
if (tierIsFree(tier)) return true;
|
|
1482
|
+
return Boolean(group.stripePublishableKey && tier.stripePriceId && hasSecretKey);
|
|
1483
|
+
}
|
|
1484
|
+
function byOrder(a, b) {
|
|
1485
|
+
return a.sortOrder - b.sortOrder || a.id.localeCompare(b.id);
|
|
1486
|
+
}
|
|
1487
|
+
function resolveTiers(group, rows = []) {
|
|
1488
|
+
const stored = rows.filter((tier) => tier.active);
|
|
1489
|
+
if (stored.length) return [...stored].sort(byOrder);
|
|
1490
|
+
return [{
|
|
1491
|
+
id: "standard",
|
|
1492
|
+
groupId: String(group.id ?? ""),
|
|
1493
|
+
name: "Membership",
|
|
1494
|
+
priceCents: group.standardPriceCents ?? 0,
|
|
1495
|
+
...group.stripePriceId ? { stripePriceId: group.stripePriceId } : {},
|
|
1496
|
+
sortOrder: 0,
|
|
1497
|
+
active: true
|
|
1498
|
+
}];
|
|
1499
|
+
}
|
|
1500
|
+
function offerableTiers(tiers2, group, hasSecretKey) {
|
|
1501
|
+
return tiers2.filter((tier) => tier.active && tierPayable(tier, group, hasSecretKey));
|
|
1502
|
+
}
|
|
1503
|
+
function findTier(tiers2, tierId) {
|
|
1504
|
+
if (!tierId) return tiers2.length === 1 ? tiers2[0] : null;
|
|
1505
|
+
return tiers2.find((tier) => tier.id === tierId) ?? null;
|
|
1506
|
+
}
|
|
1507
|
+
function joinConfigTiers(tiers2) {
|
|
1508
|
+
return tiers2.map((tier) => ({
|
|
1509
|
+
id: tier.id,
|
|
1510
|
+
name: tier.name,
|
|
1511
|
+
priceCents: tier.priceCents,
|
|
1512
|
+
blurb: tier.blurb ?? "",
|
|
1513
|
+
free: tierIsFree(tier)
|
|
1514
|
+
}));
|
|
1406
1515
|
}
|
|
1407
1516
|
|
|
1408
1517
|
// src/descriptor.ts
|
|
1409
|
-
var import_crm3 = require("@odla-ai/crm");
|
|
1410
1518
|
function createChapterIntegration(chapter, options = {}) {
|
|
1411
1519
|
const basePath = options.basePath ?? "/api/crm";
|
|
1412
1520
|
const now = options.now ?? Date.now();
|
|
1413
1521
|
const emails = chapter.config.emails;
|
|
1414
|
-
const crmDesc = (0,
|
|
1522
|
+
const crmDesc = (0, import_crm4.createCrmIntegration)(chapter.crm, {
|
|
1415
1523
|
basePath,
|
|
1416
1524
|
now,
|
|
1417
1525
|
...emails?.notificationEmail ? { notificationEmail: emails.notificationEmail } : {},
|
|
@@ -1422,6 +1530,14 @@ function createChapterIntegration(chapter, options = {}) {
|
|
|
1422
1530
|
const group = chapter.groupSeed();
|
|
1423
1531
|
if (group) {
|
|
1424
1532
|
seeds.push({ id: "group", ns: "groups", key: { attr: "id", value: chapter.id }, attrs: { ...group, createdAt: now } });
|
|
1533
|
+
for (const tier of buildTierSeeds(chapter.id, chapter.config.tiers, now)) {
|
|
1534
|
+
seeds.push({
|
|
1535
|
+
id: `tier:${String(tier.id)}`,
|
|
1536
|
+
ns: "tiers",
|
|
1537
|
+
key: { attr: "id", value: String(tier.id) },
|
|
1538
|
+
attrs: tier
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1425
1541
|
}
|
|
1426
1542
|
const isLeader = (chapter.config.network?.targets?.length ?? 0) > 0;
|
|
1427
1543
|
const runbooks = isLeader ? {
|
|
@@ -1660,7 +1776,7 @@ function canceledPatch() {
|
|
|
1660
1776
|
}
|
|
1661
1777
|
|
|
1662
1778
|
// src/network.ts
|
|
1663
|
-
var
|
|
1779
|
+
var import_crm5 = require("@odla-ai/crm");
|
|
1664
1780
|
|
|
1665
1781
|
// src/network-contract.ts
|
|
1666
1782
|
var DEFAULT_SHARE_FIELDS = {
|
|
@@ -1762,14 +1878,14 @@ async function upsertPerson(deps, opts) {
|
|
|
1762
1878
|
const { crm_record } = await deps.db.query({ crm_record: { $: { where: { type: "person", primaryEmail: email }, limit: 1 } } });
|
|
1763
1879
|
const existing = crm_record?.[0];
|
|
1764
1880
|
if (existing && typeof existing.id === "string") {
|
|
1765
|
-
await (0,
|
|
1881
|
+
await (0, import_crm5.updateRecord)(crmDeps, { id: existing.id, input: opts.input });
|
|
1766
1882
|
return { recordId: existing.id };
|
|
1767
1883
|
}
|
|
1768
|
-
const created = await (0,
|
|
1884
|
+
const created = await (0, import_crm5.createRecord)(crmDeps, { type: "person", input: opts.input, mutationId: opts.mutationId });
|
|
1769
1885
|
return { recordId: created.id };
|
|
1770
1886
|
}
|
|
1771
1887
|
async function findSharedRecord(deps, record, tag) {
|
|
1772
|
-
const structured = await (0,
|
|
1888
|
+
const structured = await (0, import_crm5.getRecordByOrigin)(
|
|
1773
1889
|
{ crm: deps.crm, db: deps.db, now: deps.now, newId: deps.newId },
|
|
1774
1890
|
record.sourceId,
|
|
1775
1891
|
record.sourceRecordId
|
|
@@ -1821,11 +1937,11 @@ async function projectSharedRecord(deps, shared, options = {}) {
|
|
|
1821
1937
|
const existing = await findSharedRecord(deps, record, tag);
|
|
1822
1938
|
let recordId;
|
|
1823
1939
|
if (existing && typeof existing.id === "string") {
|
|
1824
|
-
await (0,
|
|
1940
|
+
await (0, import_crm5.updateRecord)(crmDeps, { id: existing.id, input: record.input });
|
|
1825
1941
|
recordId = existing.id;
|
|
1826
1942
|
} else {
|
|
1827
1943
|
recordId = `network_${shortHash(`${record.sourceId}\0${record.type}\0${record.sourceRecordId}`)}`;
|
|
1828
|
-
await (0,
|
|
1944
|
+
await (0, import_crm5.createRecord)({ ...crmDeps, newId: () => recordId }, {
|
|
1829
1945
|
type: record.type,
|
|
1830
1946
|
input: record.input,
|
|
1831
1947
|
mutationId: `share-create:${tag}`
|
|
@@ -1835,7 +1951,7 @@ async function projectSharedRecord(deps, shared, options = {}) {
|
|
|
1835
1951
|
[{ t: "update", ns: "crm_tag", id: tag, attrs: { key: `${recordId}:${tag}`, recordId, tag, createdAt: deps.now() } }],
|
|
1836
1952
|
{ mutationId: `share-map:${tag}:${recordId}` }
|
|
1837
1953
|
);
|
|
1838
|
-
await (0,
|
|
1954
|
+
await (0, import_crm5.upsertRecordOrigin)(crmDeps, {
|
|
1839
1955
|
recordId,
|
|
1840
1956
|
sourceId: record.sourceId,
|
|
1841
1957
|
sourceRecordId: record.sourceRecordId,
|
|
@@ -1864,7 +1980,7 @@ async function projectApplicant(deps, applicant) {
|
|
|
1864
1980
|
}
|
|
1865
1981
|
|
|
1866
1982
|
// src/crm-sync.ts
|
|
1867
|
-
var
|
|
1983
|
+
var import_crm6 = require("@odla-ai/crm");
|
|
1868
1984
|
var str = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
|
|
1869
1985
|
function personInputFromApp(chapter, app) {
|
|
1870
1986
|
const input = sharedPersonInput({
|
|
@@ -1904,16 +2020,16 @@ async function syncApplicationToCrm(deps, opts) {
|
|
|
1904
2020
|
let recordId;
|
|
1905
2021
|
if (existing && typeof existing.id === "string") {
|
|
1906
2022
|
recordId = existing.id;
|
|
1907
|
-
await (0,
|
|
2023
|
+
await (0, import_crm6.updateRecord)(recordDeps, { id: recordId, input });
|
|
1908
2024
|
} else {
|
|
1909
|
-
const created = await (0,
|
|
2025
|
+
const created = await (0, import_crm6.createRecord)(recordDeps, { type: "person", input, ...stage ? { stage } : {} });
|
|
1910
2026
|
recordId = created.id;
|
|
1911
2027
|
}
|
|
1912
2028
|
if (existing && stage && existing.stage !== stage) {
|
|
1913
|
-
await (0,
|
|
2029
|
+
await (0, import_crm6.setStage)(recordDeps, { id: recordId, to: stage, authorId: "system", mutationId: `crm:stage:${recordId}:${stage}` }).catch(() => void 0);
|
|
1914
2030
|
}
|
|
1915
2031
|
await deps.db.transact([{ t: "update", ns: "crm_record", id: recordId, attrs: billingColumns(opts.app) }]);
|
|
1916
|
-
await (0,
|
|
2032
|
+
await (0, import_crm6.linkIdentity)(recordDeps, { recordId, email: emailKey, mutationId: `crm:link:${recordId}:${emailKey}` }).catch(() => void 0);
|
|
1917
2033
|
return recordId;
|
|
1918
2034
|
}
|
|
1919
2035
|
async function backfillCrm(deps) {
|
|
@@ -2160,18 +2276,22 @@ var clerkIntegration = {
|
|
|
2160
2276
|
],
|
|
2161
2277
|
secrets: [
|
|
2162
2278
|
{
|
|
2163
|
-
|
|
2279
|
+
// odla-db reads this exact name (CLERK_WEBHOOK_SECRET_NAME in the
|
|
2280
|
+
// platform's clerk-webhook.ts); it is also what `odla-ai secrets set`
|
|
2281
|
+
// writes. It read "CLERK_WEBHOOK_SECRET" until 0.30.0, which sent
|
|
2282
|
+
// adopters to store the value under a name nothing looks up.
|
|
2283
|
+
name: "clerk_webhook_secret",
|
|
2164
2284
|
description: "Svix signing secret for Clerk user webhooks \u2014 verifies $users sync events.",
|
|
2165
2285
|
pattern: "whsec_",
|
|
2166
2286
|
mode: "full",
|
|
2167
|
-
|
|
2287
|
+
required: false
|
|
2168
2288
|
},
|
|
2169
2289
|
{
|
|
2170
|
-
|
|
2290
|
+
name: "clerk_secret_key",
|
|
2171
2291
|
description: "Clerk backend key (sk_*) \u2014 powers account creation, the public role claim, and backend-only private profile signals; it also lets odla-db resolve user email/name via the Clerk API.",
|
|
2172
2292
|
pattern: "sk_",
|
|
2173
2293
|
mode: "full",
|
|
2174
|
-
|
|
2294
|
+
required: false
|
|
2175
2295
|
}
|
|
2176
2296
|
],
|
|
2177
2297
|
syncs: [
|
|
@@ -2489,4 +2609,36 @@ function applicationBookingUpdate(currentStatus, startAt, htmlLink) {
|
|
|
2489
2609
|
...currentStatus !== "call_scheduled" ? { status: "call_scheduled" } : {}
|
|
2490
2610
|
};
|
|
2491
2611
|
}
|
|
2612
|
+
|
|
2613
|
+
// src/tiers-store.ts
|
|
2614
|
+
function rowToTier(row, groupId) {
|
|
2615
|
+
return {
|
|
2616
|
+
id: String(row.id ?? ""),
|
|
2617
|
+
groupId: String(row.groupId ?? groupId),
|
|
2618
|
+
name: String(row.name ?? ""),
|
|
2619
|
+
priceCents: typeof row.priceCents === "number" ? row.priceCents : 0,
|
|
2620
|
+
...typeof row.stripePriceId === "string" && row.stripePriceId ? { stripePriceId: row.stripePriceId } : {},
|
|
2621
|
+
...typeof row.blurb === "string" ? { blurb: row.blurb } : {},
|
|
2622
|
+
sortOrder: typeof row.sortOrder === "number" ? row.sortOrder : 0,
|
|
2623
|
+
// A row that never set `active` is offered; only an explicit false retires
|
|
2624
|
+
// a tier, so a half-seeded row cannot silently hide a membership.
|
|
2625
|
+
active: row.active !== false
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
async function loadTiers(db, group) {
|
|
2629
|
+
const groupId = String(group.id ?? "");
|
|
2630
|
+
let rows;
|
|
2631
|
+
try {
|
|
2632
|
+
rows = (await db.query({
|
|
2633
|
+
tiers: { $: { where: { groupId }, order: { sortOrder: "asc" }, limit: 100 } }
|
|
2634
|
+
})).tiers;
|
|
2635
|
+
} catch {
|
|
2636
|
+
rows = void 0;
|
|
2637
|
+
}
|
|
2638
|
+
const list = Array.isArray(rows) ? rows.map((row) => rowToTier(row, groupId)) : [];
|
|
2639
|
+
return resolveTiers(group, list);
|
|
2640
|
+
}
|
|
2641
|
+
async function loadOfferableTiers(db, group, hasSecretKey) {
|
|
2642
|
+
return offerableTiers(await loadTiers(db, group), group, hasSecretKey);
|
|
2643
|
+
}
|
|
2492
2644
|
//# sourceMappingURL=index.cjs.map
|