@odla-ai/chapter 0.39.1 → 0.40.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.
@@ -1,8 +1,8 @@
1
1
  import * as preact from 'preact';
2
2
  import { ComponentChildren, JSX } from 'preact';
3
3
  import { CrmClient, Crm, CrmRecord } from '@odla-ai/crm';
4
- import { a as Chapter, b as ChapterCopy, M as MemberContentDocument, E as EffectiveMemberContent, c as MemberContentTierAuthority } from '../../copy-context-Buz_27Ed.js';
5
- export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-Buz_27Ed.js';
4
+ import { a as Chapter, b as ChapterCopy, M as MemberContentDocument, E as EffectiveMemberContent, c as MemberContentTierAuthority } from '../../copy-context-Dbu9Bbq1.js';
5
+ export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-Dbu9Bbq1.js';
6
6
  import { CrmLifecycleAdapter, CrmWorkspaceMasterContext, CrmWorkspaceRecordContext, RecordPanelTab } from '@odla-ai/crm/ui';
7
7
 
8
8
  /** Normalized public auth configuration consumed by the Clerk gate. */
@@ -1,5 +1,5 @@
1
1
  export { AdminAccountMenuProps, AdminBuilderLibraryData, AdminChrome, AdminMemberContentData, AdminNote, AdminPage, AdminResource, AdminRouteState, AdminRouteTarget, AdminRouting, AdminSection, AdminSectionContext, AdminWorkspace, AdminWorkspaceNavProps, AdmissionGrantsWorkspace, AvailabilitySectionOptions, BillingSectionOptions, BuilderLibraryAdmin, ChapterAdmin, ChapterAdminAuthAdapter, ChapterAdminAuthConfig, ChapterAdminAuthLoadContext, ChapterAdminProps, ChapterAdminUser, CollectionSectionOptions, DashboardSectionOptions, EmailSectionOptions, LeaderNetworkOverview, MeetingsSectionOptions, MemberContentAdmin, NetworkRecordsWorkspace, NetworkShareActions, NetworkShareActionsProps, Panel, PeopleSectionOptions, RecordActions, RecordActionsProps, ThemeWarning, adminFetch, adminRouteFromUrl, adminRouteHref, adminSectionFromUrl, adminSectionHref, admissionGrantsWorkspace, applicationLifecycleAdapter, availabilitySection, billingSection, builderLibrarySection, collectionSection, dashboardSection, defaultAdminSections, defaultAdminWorkspaces, emailSection, loadChapterAdminConfig, loadChapterAdminUser, meetingsSection, memberContentSection, networkRecordsWorkspace, peopleSection, useAdminResource } from './admin/index.js';
2
- export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-Buz_27Ed.js';
2
+ export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-Dbu9Bbq1.js';
3
3
  export { ApiFn, BrandStyle, BrandStyleProps, GiftOfferCards, GiftOfferCardsMode, GiftOfferCardsProps, JoinBookingSimulation, JoinConfig, JoinFlowState, JoinIsland, JoinIslandProps, JoinSimulation, JoinStepRenderContext, JoinSubmitRenderContext, JoinTier, JoinTierRenderContext, MEMBERSHIP_CARD_TOKEN_DEFAULTS, MemberContentArea, MemberContentAreaProps, MemberGifts, MemberProvisionalRenderContext, MembersArea, MembersAreaProps, MembershipCardTokenName, MembershipCards, MembershipCardsMode, MembershipCardsProps, PaymentPriceLines, PaymentStep, PaymentStepProps, PaymentStepSimulation, RescheduleProps, Rescheduler, Slot, SlotPicker, SlotPickerClasses, SlotPickerProps, SubResponse, TierPlaceholder, dayKey, dayLabel, fmtDate, fmtMoney, fullLabel, groupSlotsByDay, loadJoinResume, membershipApplicationHref, tierNeedsPayment, timeLabel, tzShort, useTierSelection } from './member/index.js';
4
4
  import 'preact';
5
5
  import '@odla-ai/crm';
@@ -1,7 +1,7 @@
1
1
  import * as preact from 'preact';
2
2
  import { ComponentChildren, JSX } from 'preact';
3
- import { d as MemberContentProjection, e as MemberContentPageProjection, b as ChapterCopy, f as ChapterBrand } from '../../copy-context-Buz_27Ed.js';
4
- export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-Buz_27Ed.js';
3
+ import { d as MemberContentProjection, e as MemberContentPageProjection, b as ChapterCopy, f as ChapterBrand } from '../../copy-context-Dbu9Bbq1.js';
4
+ export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-Dbu9Bbq1.js';
5
5
  import { FieldType, FieldConditions } from '@odla-ai/crm';
6
6
 
7
7
  /** A bookable slot: a start instant in epoch milliseconds. */
@@ -3382,12 +3382,16 @@ function findApplicationRef(obj) {
3382
3382
  const applicationId = candidates.map(pick).find(Boolean);
3383
3383
  const appId = candidates.map((metadata) => typeof metadata.odlaAppId === "string" ? metadata.odlaAppId : void 0).find(Boolean);
3384
3384
  const environment = candidates.map((metadata) => typeof metadata.odlaEnvironment === "string" ? metadata.odlaEnvironment : void 0).find(Boolean);
3385
+ const dataEnvironment = candidates.map((metadata) => typeof metadata.odlaDataEnvironment === "string" ? metadata.odlaDataEnvironment : void 0).find(Boolean);
3386
+ const runtime = candidates.map((metadata) => typeof metadata.odlaRuntime === "string" ? metadata.odlaRuntime : void 0).find(Boolean);
3385
3387
  const customerId = typeof obj.customer === "string" ? obj.customer : void 0;
3386
3388
  return {
3387
3389
  ...applicationId ? { applicationId } : {},
3388
3390
  ...customerId ? { customerId } : {},
3389
3391
  ...appId ? { appId } : {},
3390
- ...environment ? { environment } : {}
3392
+ ...environment ? { environment } : {},
3393
+ ...dataEnvironment ? { dataEnvironment } : {},
3394
+ ...runtime ? { runtime } : {}
3391
3395
  };
3392
3396
  }
3393
3397
  function findGiftRef(obj) {
@@ -3397,17 +3401,31 @@ function findGiftRef(obj) {
3397
3401
  const subscription = obj.subscription_details ?? parent?.subscription_details;
3398
3402
  const giftIntentId = pick(metaOf(obj)) ?? pick(metaOf(subscription));
3399
3403
  const subscriptionId = typeof obj.subscription === "string" ? obj.subscription : typeof subscription?.subscription === "string" ? String(subscription.subscription) : void 0;
3400
- return { ...giftIntentId ? { giftIntentId } : {}, providerObjectId: subscriptionId ?? String(obj.id ?? "") };
3404
+ const candidates = [metaOf(obj), metaOf(subscription)];
3405
+ const pickIdentity = (name) => candidates.map((metadata) => typeof metadata[name] === "string" ? String(metadata[name]) : void 0).find(Boolean);
3406
+ const appId = pickIdentity("odlaAppId");
3407
+ const environment = pickIdentity("odlaEnvironment");
3408
+ const dataEnvironment = pickIdentity("odlaDataEnvironment");
3409
+ const runtime = pickIdentity("odlaRuntime");
3410
+ return {
3411
+ ...giftIntentId ? { giftIntentId } : {},
3412
+ providerObjectId: subscriptionId ?? String(obj.id ?? ""),
3413
+ ...appId ? { appId } : {},
3414
+ ...environment ? { environment } : {},
3415
+ ...dataEnvironment ? { dataEnvironment } : {},
3416
+ ...runtime ? { runtime } : {}
3417
+ };
3401
3418
  }
3402
3419
  function normalizeWebhookEvent(event) {
3403
3420
  const obj = event.data?.object ?? {};
3404
3421
  const gift = findGiftRef(obj);
3405
3422
  if (gift.giftIntentId) {
3423
+ const { giftIntentId, providerObjectId, ...identity } = gift;
3406
3424
  if (event.type === "payment_intent.succeeded" || event.type === "invoice.paid" && obj.billing_reason === "subscription_create") {
3407
- return { kind: "gift_paid", giftIntentId: gift.giftIntentId, providerObjectId: gift.providerObjectId };
3425
+ return { kind: "gift_paid", giftIntentId, providerObjectId, ...identity };
3408
3426
  }
3409
- if (event.type === "charge.refunded") return { kind: "gift_revoked", giftIntentId: gift.giftIntentId, reason: "refunded" };
3410
- if (event.type === "customer.subscription.deleted") return { kind: "gift_revoked", giftIntentId: gift.giftIntentId, reason: "canceled" };
3427
+ if (event.type === "charge.refunded") return { kind: "gift_revoked", giftIntentId, ...identity, reason: "refunded" };
3428
+ if (event.type === "customer.subscription.deleted") return { kind: "gift_revoked", giftIntentId, ...identity, reason: "canceled" };
3411
3429
  return { kind: "ignored", type: event.type };
3412
3430
  }
3413
3431
  const ref = findApplicationRef(obj);
@@ -3484,7 +3502,9 @@ function createStripeProvider(config) {
3484
3502
  groupId: input.groupId,
3485
3503
  email: input.email,
3486
3504
  odlaAppId: input.appId,
3487
- odlaEnvironment: input.environment
3505
+ odlaEnvironment: input.environment,
3506
+ odlaDataEnvironment: input.environment,
3507
+ ...input.runtime ? { odlaRuntime: input.runtime } : {}
3488
3508
  };
3489
3509
  let customerId = input.existingCustomerId;
3490
3510
  if (!customerId) {
@@ -3772,12 +3792,16 @@ var clientSecret = (value) => {
3772
3792
  const nested = confirmation?.client_secret ?? intent?.client_secret;
3773
3793
  return typeof nested === "string" && nested ? nested : null;
3774
3794
  };
3775
- async function startGiftProviderCheckout(secretKey, intent, email, existingCustomerId) {
3795
+ async function startGiftProviderCheckout(secretKey, intent, email, existingCustomerId, runtime) {
3776
3796
  const offer = intent.offerSnapshot;
3777
3797
  const metadata = {
3778
3798
  giftIntentId: intent.id,
3779
3799
  chapterId: intent.chapterId,
3780
3800
  environment: intent.environment,
3801
+ odlaAppId: intent.chapterId,
3802
+ odlaEnvironment: intent.environment,
3803
+ odlaDataEnvironment: intent.environment,
3804
+ ...runtime ? { odlaRuntime: runtime } : {},
3781
3805
  offerId: intent.offerId
3782
3806
  };
3783
3807
  let customerId = existingCustomerId;
@@ -3827,6 +3851,7 @@ async function startGiftIntent(db, input) {
3827
3851
  id: input.id,
3828
3852
  chapterId: input.chapterId,
3829
3853
  environment: input.environment,
3854
+ ...input.runtime ? { runtime: input.runtime } : {},
3830
3855
  purchaserType: input.purchaserType,
3831
3856
  purchaserEmailNormalized: input.purchaserEmailNormalized,
3832
3857
  ...input.purchaserApplicationId ? { purchaserApplicationId: input.purchaserApplicationId } : {},
@@ -3846,7 +3871,7 @@ async function startGiftIntent(db, input) {
3846
3871
  stored = write.duplicate ? first2((await db.query({ giftPurchaseIntents: { $: { where: { id: input.id }, limit: 1 } } })).giftPurchaseIntents) : attrs;
3847
3872
  if (!write.duplicate) giftCode = prepared.code;
3848
3873
  }
3849
- if (!stored || stored.chapterId !== input.chapterId || stored.environment !== input.environment || stored.offerId !== input.offer.id || stored.purchaserEmailNormalized !== input.purchaserEmailNormalized) {
3874
+ if (!stored || stored.chapterId !== input.chapterId || stored.environment !== input.environment || stored.runtime !== input.runtime || stored.offerId !== input.offer.id || stored.purchaserEmailNormalized !== input.purchaserEmailNormalized) {
3850
3875
  throw new Error("gift purchase intent does not match this checkout");
3851
3876
  }
3852
3877
  if (stored.status === "paid") throw new Error("gift purchase is already paid");
@@ -3854,7 +3879,8 @@ async function startGiftIntent(db, input) {
3854
3879
  input.stripeSecret,
3855
3880
  stored,
3856
3881
  input.purchaserEmail,
3857
- typeof stored.providerCustomerId === "string" ? stored.providerCustomerId : input.existingCustomerId
3882
+ typeof stored.providerCustomerId === "string" ? stored.providerCustomerId : input.existingCustomerId,
3883
+ input.runtime
3858
3884
  );
3859
3885
  await db.transact([{ t: "update", ns: "giftPurchaseIntents", id: input.id, attrs: {
3860
3886
  status: "checkout_ready",
@@ -3873,18 +3899,29 @@ async function startGiftIntent(db, input) {
3873
3899
  };
3874
3900
  }
3875
3901
 
3902
+ // src/stripe-runtime.ts
3903
+ function stripeWebhookSecretName(runtime) {
3904
+ if (!runtime) return "stripe_webhook_secret";
3905
+ if (!/^[a-z0-9]{2,12}$/.test(runtime) || runtime === "prod" || runtime === "production") {
3906
+ throw new Error("ODLA_RUNTIME is invalid");
3907
+ }
3908
+ return `stripe_webhook_secret__${runtime}`;
3909
+ }
3910
+
3911
+ // src/payments-route-support.ts
3912
+ var paymentErrorCode = (error) => error && typeof error === "object" && typeof error.code === "string" ? error.code : "unknown";
3913
+ function paymentLineItems(group) {
3914
+ const standard = Number(group.standardPriceCents ?? 0);
3915
+ const discount = Number(group.foundingDiscountCents ?? 0);
3916
+ return { standardCents: standard, discountCents: discount, dueTodayCents: standard - discount };
3917
+ }
3918
+
3876
3919
  // src/worker-routes-payments.ts
3877
- var codeOf = (err) => err && typeof err === "object" && typeof err.code === "string" ? err.code : "unknown";
3878
3920
  async function firstRow2(db, ns, q) {
3879
3921
  const res = await db.query({ [ns]: { $: q } });
3880
3922
  const rows4 = res[ns];
3881
3923
  return Array.isArray(rows4) ? rows4[0] : void 0;
3882
3924
  }
3883
- function lineItems(group) {
3884
- const standard = Number(group.standardPriceCents ?? 0);
3885
- const discount = Number(group.foundingDiscountCents ?? 0);
3886
- return { standardCents: standard, discountCents: discount, dueTodayCents: standard - discount };
3887
- }
3888
3925
  async function findApplication(db, event) {
3889
3926
  if ("applicationId" in event && event.applicationId) {
3890
3927
  return firstRow2(db, "applications", { where: { id: event.applicationId }, limit: 1 });
@@ -3975,6 +4012,7 @@ async function startSubscription(req, env, ctx) {
3975
4012
  chapterId: ctx.chapter.id,
3976
4013
  environment: env.ODLA_ENV,
3977
4014
  offer,
4015
+ ...env.ODLA_RUNTIME ? { runtime: env.ODLA_RUNTIME } : {},
3978
4016
  purchaserType: "application",
3979
4017
  purchaserEmail: String(app.email ?? ""),
3980
4018
  purchaserEmailNormalized: normalizeChapterEmail(String(app.email ?? "")),
@@ -4007,6 +4045,7 @@ async function startSubscription(req, env, ctx) {
4007
4045
  result = await provider.createSubscription({
4008
4046
  appId: env.ODLA_APP_ID,
4009
4047
  environment: env.ODLA_ENV,
4048
+ ...env.ODLA_RUNTIME ? { runtime: env.ODLA_RUNTIME } : {},
4010
4049
  applicationId,
4011
4050
  groupId: String(group.id),
4012
4051
  email: String(app.email ?? ""),
@@ -4015,29 +4054,35 @@ async function startSubscription(req, env, ctx) {
4015
4054
  existingCustomerId: typeof app.stripeCustomerId === "string" ? app.stripeCustomerId : void 0
4016
4055
  });
4017
4056
  } catch (err) {
4018
- return json({ error: "payment setup failed", code: codeOf(err) }, 502);
4057
+ return json({ error: "payment setup failed", code: paymentErrorCode(err) }, 502);
4019
4058
  }
4020
4059
  await db.transact([
4021
4060
  {
4022
4061
  t: "update",
4023
4062
  ns: "applications",
4024
4063
  id: applicationId,
4025
- attrs: { stripeCustomerId: result.customerId, stripeSubscriptionId: result.subscriptionId, refundPolicyAckAt: Date.now() }
4064
+ attrs: {
4065
+ stripeCustomerId: result.customerId,
4066
+ stripeSubscriptionId: result.subscriptionId,
4067
+ ...env.ODLA_RUNTIME ? { stripeRuntime: env.ODLA_RUNTIME } : {},
4068
+ refundPolicyAckAt: Date.now()
4069
+ }
4026
4070
  }
4027
4071
  ]);
4028
- return json({ clientSecret: result.clientSecret, publishableKey: group.stripePublishableKey ?? null, lineItems: lineItems(group) });
4072
+ return json({ clientSecret: result.clientSecret, publishableKey: group.stripePublishableKey ?? null, lineItems: paymentLineItems(group) });
4029
4073
  }
4030
4074
  async function ingestWebhook(req, env, ctx) {
4031
4075
  const db = ctx.makeDb(env);
4032
- const webhookSecret = await getVaultSecret(db, "stripe_webhook_secret");
4076
+ const webhookSecret = await getVaultSecret(db, stripeWebhookSecretName(env.ODLA_RUNTIME));
4033
4077
  if (!webhookSecret) return json({ error: "webhook not configured" }, 503);
4034
4078
  const rawBody = await req.text();
4035
4079
  const ingest = await createStripeProvider({ webhookSecret }).ingestWebhook(rawBody, req.headers.get("stripe-signature") ?? "");
4036
4080
  if (!ingest.ok) return json({ error: "invalid signature" }, 400);
4037
4081
  const { eventId, event } = ingest;
4038
4082
  if (event.kind === "ignored") return json({ ok: true, ignored: event.type });
4039
- const identifiedEnvironment = event.appId !== void 0 || event.environment !== void 0;
4040
- if (env.ODLA_APP_ID && identifiedEnvironment && (event.appId !== env.ODLA_APP_ID || event.environment !== env.ODLA_ENV)) {
4083
+ const identifiedEnvironment = event.appId !== void 0 || event.environment !== void 0 || event.dataEnvironment !== void 0 || event.runtime !== void 0;
4084
+ const eventDataEnvironment = event.dataEnvironment ?? event.environment;
4085
+ if (env.ODLA_APP_ID && identifiedEnvironment && (event.appId !== env.ODLA_APP_ID || eventDataEnvironment !== env.ODLA_ENV || event.runtime !== void 0 && event.runtime !== env.ODLA_RUNTIME)) {
4041
4086
  return json({ ok: true, matched: false });
4042
4087
  }
4043
4088
  if (event.kind === "gift_paid" || event.kind === "gift_revoked") {
@@ -4045,6 +4090,9 @@ async function ingestWebhook(req, env, ctx) {
4045
4090
  if (!intent || intent.chapterId !== ctx.chapter.id || intent.environment !== env.ODLA_ENV) {
4046
4091
  return json({ ok: true, matched: false });
4047
4092
  }
4093
+ if (event.runtime === void 0 && typeof intent.runtime === "string" && intent.runtime !== env.ODLA_RUNTIME) {
4094
+ return json({ ok: true, matched: false });
4095
+ }
4048
4096
  if (event.kind === "gift_paid") {
4049
4097
  if (intent.providerObjectId !== event.providerObjectId) return json({ ok: true, matched: false });
4050
4098
  if (intent.status === "refunded" || intent.status === "canceled") return json({ ok: true, matched: false });
@@ -4068,6 +4116,9 @@ async function ingestWebhook(req, env, ctx) {
4068
4116
  }
4069
4117
  const app = await findApplication(db, event);
4070
4118
  if (!app) return json({ ok: true, matched: false });
4119
+ if (event.runtime === void 0 && typeof app.stripeRuntime === "string" && app.stripeRuntime !== env.ODLA_RUNTIME) {
4120
+ return json({ ok: true, matched: false });
4121
+ }
4071
4122
  const patch = webhookPatch(event, String(app.status ?? ""));
4072
4123
  let next = app;
4073
4124
  if (Object.keys(patch).length) {
@@ -4170,6 +4221,7 @@ var handleGifts = async (req, url, env, ctx) => {
4170
4221
  offer,
4171
4222
  purchaserType: "member",
4172
4223
  purchaserEmail: user.email,
4224
+ ...env.ODLA_RUNTIME ? { runtime: env.ODLA_RUNTIME } : {},
4173
4225
  purchaserEmailNormalized: emailNormalized,
4174
4226
  purchaserUserId: user.userId,
4175
4227
  ...typeof app?.stripeCustomerId === "string" ? { existingCustomerId: app.stripeCustomerId } : {},
@@ -6098,9 +6150,23 @@ async function saveLocalBuilderLibrary(db, document, environment, expectedRevisi
6098
6150
 
6099
6151
  // src/worker-routes-builder-library.ts
6100
6152
  var BODY_CAP2 = 1e6;
6101
- async function verifySource(req, db, sources) {
6153
+ async function verifySource(req, db, ctx) {
6154
+ const sources = ctx.chapter.builderLibrary?.sources ?? [];
6155
+ if (ctx.chapter.mode === "chapter") {
6156
+ const secret = await getVaultSecret(db, "network_share_secret");
6157
+ if (!secret) return null;
6158
+ const verified = await (0, import_db10.verifyFederatedRequest)(req, {
6159
+ secret,
6160
+ senders: sources.map((source2) => source2.sourceId)
6161
+ });
6162
+ if (!verified.ok) return null;
6163
+ const source = sources.find((candidate) => candidate.sourceId === verified.sender);
6164
+ return source ? { source, body: verified.body } : null;
6165
+ }
6102
6166
  for (const source of sources) {
6103
- const secret = await getVaultSecret(db, source.secretName);
6167
+ const target = ctx.chapter.network.targets.find((candidate) => candidate.id === source.sourceId);
6168
+ if (!target) continue;
6169
+ const secret = await getVaultSecret(db, target.secretName);
6104
6170
  if (!secret) continue;
6105
6171
  const verified = await (0, import_db10.verifyFederatedRequest)(req.clone(), { secret, senders: [source.sourceId] });
6106
6172
  if (verified.ok) return { source, body: verified.body };
@@ -6112,7 +6178,7 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
6112
6178
  const settings = ctx.chapter.builderLibrary;
6113
6179
  if (!settings) return json({ error: "not found" }, 404);
6114
6180
  const db = ctx.makeDb(env);
6115
- const verified = await verifySource(req, db, settings.sources);
6181
+ const verified = await verifySource(req, db, ctx);
6116
6182
  if (!verified) return json({ error: "unauthorized" }, 401);
6117
6183
  let input;
6118
6184
  try {