@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.
package/dist/index.d.cts CHANGED
@@ -207,16 +207,14 @@ interface ResolvedMemberContent {
207
207
  interface ChapterBuilderLibrary {
208
208
  /** Keep local admin authoring enabled. Defaults to true. */
209
209
  localAuthoring?: boolean;
210
- /** Signed sources this app accepts. Hub sources are Chapters; Chapter sources are Built Not Found. */
210
+ /** Existing Chapter-network peers this app accepts. Hub sources must be
211
+ * configured network targets; a Chapter source must be a configured reader. */
211
212
  sources?: readonly {
212
213
  sourceId: string;
213
- /** Vault key holding the dedicated Builder Library edge secret. */
214
- secretName?: string;
215
214
  }[];
216
215
  }
217
216
  interface ResolvedBuilderLibrarySource {
218
217
  sourceId: string;
219
- secretName: string;
220
218
  }
221
219
  /** Validated Builder Library settings carried on a resolved Chapter. */
222
220
  interface ResolvedBuilderLibrary {
@@ -796,8 +794,9 @@ declare function resolveSignupControl(input: ChapterSignupControl | undefined):
796
794
  /** Validate the deliberately separate member-content authoring boundary. */
797
795
  declare function resolveMemberContent(input: ChapterMemberContent | undefined): ResolvedMemberContent | null;
798
796
 
799
- /** Validate the private Builder Library federation boundary. */
800
- declare function resolveBuilderLibrary(mode: ChapterMode, input: ChapterBuilderLibrary | undefined): ResolvedBuilderLibrary | null;
797
+ /** Validate the private Builder Library boundary against Chapter's existing
798
+ * network peers. Builder Library never introduces a second edge credential. */
799
+ declare function resolveBuilderLibrary(mode: ChapterMode, input: ChapterBuilderLibrary | undefined, network?: ResolvedNetwork): ResolvedBuilderLibrary | null;
801
800
 
802
801
  /** Options for {@link leaderCrmConfig}. */
803
802
  interface LeaderCrmOptions {
@@ -1545,6 +1544,8 @@ type WebhookEvent = ({
1545
1544
  }) & {
1546
1545
  appId?: string;
1547
1546
  environment?: string;
1547
+ dataEnvironment?: string;
1548
+ runtime?: string;
1548
1549
  };
1549
1550
  /** Resolve the application reference on a Stripe object: `applicationId` from
1550
1551
  * metadata (direct, then subscription_details, then nested
@@ -1554,6 +1555,8 @@ declare function findApplicationRef(obj: Record<string, unknown>): {
1554
1555
  customerId?: string;
1555
1556
  appId?: string;
1556
1557
  environment?: string;
1558
+ dataEnvironment?: string;
1559
+ runtime?: string;
1557
1560
  };
1558
1561
  /** Normalize a verified Stripe event into a {@link WebhookEvent}. `invoice.paid`
1559
1562
  * splits into first_payment vs renewal by `billing_reason`; refunds and
package/dist/index.d.ts CHANGED
@@ -207,16 +207,14 @@ interface ResolvedMemberContent {
207
207
  interface ChapterBuilderLibrary {
208
208
  /** Keep local admin authoring enabled. Defaults to true. */
209
209
  localAuthoring?: boolean;
210
- /** Signed sources this app accepts. Hub sources are Chapters; Chapter sources are Built Not Found. */
210
+ /** Existing Chapter-network peers this app accepts. Hub sources must be
211
+ * configured network targets; a Chapter source must be a configured reader. */
211
212
  sources?: readonly {
212
213
  sourceId: string;
213
- /** Vault key holding the dedicated Builder Library edge secret. */
214
- secretName?: string;
215
214
  }[];
216
215
  }
217
216
  interface ResolvedBuilderLibrarySource {
218
217
  sourceId: string;
219
- secretName: string;
220
218
  }
221
219
  /** Validated Builder Library settings carried on a resolved Chapter. */
222
220
  interface ResolvedBuilderLibrary {
@@ -796,8 +794,9 @@ declare function resolveSignupControl(input: ChapterSignupControl | undefined):
796
794
  /** Validate the deliberately separate member-content authoring boundary. */
797
795
  declare function resolveMemberContent(input: ChapterMemberContent | undefined): ResolvedMemberContent | null;
798
796
 
799
- /** Validate the private Builder Library federation boundary. */
800
- declare function resolveBuilderLibrary(mode: ChapterMode, input: ChapterBuilderLibrary | undefined): ResolvedBuilderLibrary | null;
797
+ /** Validate the private Builder Library boundary against Chapter's existing
798
+ * network peers. Builder Library never introduces a second edge credential. */
799
+ declare function resolveBuilderLibrary(mode: ChapterMode, input: ChapterBuilderLibrary | undefined, network?: ResolvedNetwork): ResolvedBuilderLibrary | null;
801
800
 
802
801
  /** Options for {@link leaderCrmConfig}. */
803
802
  interface LeaderCrmOptions {
@@ -1545,6 +1544,8 @@ type WebhookEvent = ({
1545
1544
  }) & {
1546
1545
  appId?: string;
1547
1546
  environment?: string;
1547
+ dataEnvironment?: string;
1548
+ runtime?: string;
1548
1549
  };
1549
1550
  /** Resolve the application reference on a Stripe object: `applicationId` from
1550
1551
  * metadata (direct, then subscription_details, then nested
@@ -1554,6 +1555,8 @@ declare function findApplicationRef(obj: Record<string, unknown>): {
1554
1555
  customerId?: string;
1555
1556
  appId?: string;
1556
1557
  environment?: string;
1558
+ dataEnvironment?: string;
1559
+ runtime?: string;
1557
1560
  };
1558
1561
  /** Normalize a verified Stripe event into a {@link WebhookEvent}. `invoice.paid`
1559
1562
  * splits into first_payment vs renewal by `billing_reason`; refunds and
package/dist/index.js CHANGED
@@ -215,6 +215,7 @@ var applicationAttrs = {
215
215
  tierId: attr("string", { indexed: true, optional: true }),
216
216
  stripeCustomerId: attr("string", { indexed: true, optional: true }),
217
217
  stripeSubscriptionId: attr("string", { indexed: true, optional: true }),
218
+ stripeRuntime: attr("string", { indexed: true, optional: true }),
218
219
  renewalAt: attr("number", { optional: true }),
219
220
  disclaimerAckAt: attr("number", { optional: true }),
220
221
  refundPolicyAckAt: attr("number", { optional: true }),
@@ -310,6 +311,7 @@ var giftPurchaseIntentsEntity = { attrs: {
310
311
  id: id2(),
311
312
  chapterId: attr3("string", { indexed: true }),
312
313
  environment: attr3("string", { indexed: true }),
314
+ runtime: attr3("string", { optional: true, indexed: true }),
313
315
  purchaserType: attr3("string", { indexed: true }),
314
316
  purchaserApplicationId: attr3("string", { optional: true, indexed: true }),
315
317
  purchaserUserId: attr3("string", { optional: true, indexed: true }),
@@ -1523,8 +1525,7 @@ function resolveMemberContent(input) {
1523
1525
 
1524
1526
  // src/config-builder-library.ts
1525
1527
  var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
1526
- var SECRET_NAME3 = /^[a-z][a-z0-9_]{1,127}$/;
1527
- function resolveBuilderLibrary(mode, input) {
1528
+ function resolveBuilderLibrary(mode, input, network = { targets: [], readers: [] }) {
1528
1529
  if (!input) return null;
1529
1530
  if (input.sources !== void 0 && !Array.isArray(input.sources)) {
1530
1531
  throw new Error("defineChapter.builderLibrary.sources: must be an array");
@@ -1544,11 +1545,19 @@ function resolveBuilderLibrary(mode, input) {
1544
1545
  throw new Error(`defineChapter.builderLibrary.sources: duplicate source "${source.sourceId}"`);
1545
1546
  }
1546
1547
  seen.add(source.sourceId);
1547
- const secretName = source.secretName?.trim() || "builder_library_control_secret";
1548
- if (!SECRET_NAME3.test(secretName)) {
1549
- throw new Error(`defineChapter.builderLibrary.sources[${index}].secretName: must be a vault-key identifier`);
1548
+ if ("secretName" in source) {
1549
+ throw new Error(
1550
+ `defineChapter.builderLibrary.sources[${index}].secretName: remove this field; Builder Library reuses the Chapter network edge`
1551
+ );
1552
+ }
1553
+ const networkPeer = mode === "hub" ? network.targets.some((target) => target.id === source.sourceId) : network.readers.some((reader) => reader.id === source.sourceId);
1554
+ if (!networkPeer) {
1555
+ const side = mode === "hub" ? "targets" : "readers";
1556
+ throw new Error(
1557
+ `defineChapter.builderLibrary.sources[${index}]: "${source.sourceId}" must already be configured in network.${side}`
1558
+ );
1550
1559
  }
1551
- return { sourceId: source.sourceId, secretName };
1560
+ return { sourceId: source.sourceId };
1552
1561
  });
1553
1562
  return { enabled: true, localAuthoring: input.localAuthoring !== false, sources };
1554
1563
  }
@@ -1587,7 +1596,7 @@ function defineChapter(config) {
1587
1596
  const network = resolveNetwork(config, crm);
1588
1597
  const signupControl = resolveSignupControl(config.signupControl);
1589
1598
  const memberContent = resolveMemberContent(config.memberContent);
1590
- const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary);
1599
+ const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary, network);
1591
1600
  const formation = resolveLeaderFormation(config.formation, crm);
1592
1601
  if (signupControl && mode !== "chapter") {
1593
1602
  throw new Error("defineChapter.signupControl: only chapter-mode followers can receive signup control");
@@ -1911,13 +1920,6 @@ function createChapterIntegration(chapter, options = {}) {
1911
1920
  ...emails?.debugEmail ? { debugEmail: emails.debugEmail } : {}
1912
1921
  });
1913
1922
  const seeds = [...crmDesc.seeds ?? []];
1914
- const builderLibrarySecrets = [...new Set(
1915
- chapter.builderLibrary?.sources.map((source) => source.secretName) ?? []
1916
- )].map((name) => ({
1917
- name,
1918
- description: "Signing secret for private Builder Library delivery from a configured source.",
1919
- required: true
1920
- }));
1921
1923
  const group = chapter.groupSeed();
1922
1924
  if (group) {
1923
1925
  seeds.push({ id: "group", ns: "groups", key: { attr: "id", value: chapter.id }, attrs: { ...group, createdAt: now } });
@@ -1950,7 +1952,7 @@ function createChapterIntegration(chapter, options = {}) {
1950
1952
  },
1951
1953
  rules: { ...crmDesc.rules, ...chapter.rules },
1952
1954
  seeds,
1953
- secrets: builderLibrarySecrets,
1955
+ secrets: [],
1954
1956
  probes: [
1955
1957
  ...crmDesc.probes ?? [],
1956
1958
  // The snapshot route is deliberately private. A credential-free smoke
@@ -2655,12 +2657,16 @@ function findApplicationRef(obj) {
2655
2657
  const applicationId = candidates.map(pick).find(Boolean);
2656
2658
  const appId = candidates.map((metadata) => typeof metadata.odlaAppId === "string" ? metadata.odlaAppId : void 0).find(Boolean);
2657
2659
  const environment = candidates.map((metadata) => typeof metadata.odlaEnvironment === "string" ? metadata.odlaEnvironment : void 0).find(Boolean);
2660
+ const dataEnvironment = candidates.map((metadata) => typeof metadata.odlaDataEnvironment === "string" ? metadata.odlaDataEnvironment : void 0).find(Boolean);
2661
+ const runtime = candidates.map((metadata) => typeof metadata.odlaRuntime === "string" ? metadata.odlaRuntime : void 0).find(Boolean);
2658
2662
  const customerId = typeof obj.customer === "string" ? obj.customer : void 0;
2659
2663
  return {
2660
2664
  ...applicationId ? { applicationId } : {},
2661
2665
  ...customerId ? { customerId } : {},
2662
2666
  ...appId ? { appId } : {},
2663
- ...environment ? { environment } : {}
2667
+ ...environment ? { environment } : {},
2668
+ ...dataEnvironment ? { dataEnvironment } : {},
2669
+ ...runtime ? { runtime } : {}
2664
2670
  };
2665
2671
  }
2666
2672
  function findGiftRef(obj) {
@@ -2670,17 +2676,31 @@ function findGiftRef(obj) {
2670
2676
  const subscription = obj.subscription_details ?? parent?.subscription_details;
2671
2677
  const giftIntentId = pick(metaOf(obj)) ?? pick(metaOf(subscription));
2672
2678
  const subscriptionId = typeof obj.subscription === "string" ? obj.subscription : typeof subscription?.subscription === "string" ? String(subscription.subscription) : void 0;
2673
- return { ...giftIntentId ? { giftIntentId } : {}, providerObjectId: subscriptionId ?? String(obj.id ?? "") };
2679
+ const candidates = [metaOf(obj), metaOf(subscription)];
2680
+ const pickIdentity = (name) => candidates.map((metadata) => typeof metadata[name] === "string" ? String(metadata[name]) : void 0).find(Boolean);
2681
+ const appId = pickIdentity("odlaAppId");
2682
+ const environment = pickIdentity("odlaEnvironment");
2683
+ const dataEnvironment = pickIdentity("odlaDataEnvironment");
2684
+ const runtime = pickIdentity("odlaRuntime");
2685
+ return {
2686
+ ...giftIntentId ? { giftIntentId } : {},
2687
+ providerObjectId: subscriptionId ?? String(obj.id ?? ""),
2688
+ ...appId ? { appId } : {},
2689
+ ...environment ? { environment } : {},
2690
+ ...dataEnvironment ? { dataEnvironment } : {},
2691
+ ...runtime ? { runtime } : {}
2692
+ };
2674
2693
  }
2675
2694
  function normalizeWebhookEvent(event) {
2676
2695
  const obj = event.data?.object ?? {};
2677
2696
  const gift = findGiftRef(obj);
2678
2697
  if (gift.giftIntentId) {
2698
+ const { giftIntentId, providerObjectId, ...identity } = gift;
2679
2699
  if (event.type === "payment_intent.succeeded" || event.type === "invoice.paid" && obj.billing_reason === "subscription_create") {
2680
- return { kind: "gift_paid", giftIntentId: gift.giftIntentId, providerObjectId: gift.providerObjectId };
2700
+ return { kind: "gift_paid", giftIntentId, providerObjectId, ...identity };
2681
2701
  }
2682
- if (event.type === "charge.refunded") return { kind: "gift_revoked", giftIntentId: gift.giftIntentId, reason: "refunded" };
2683
- if (event.type === "customer.subscription.deleted") return { kind: "gift_revoked", giftIntentId: gift.giftIntentId, reason: "canceled" };
2702
+ if (event.type === "charge.refunded") return { kind: "gift_revoked", giftIntentId, ...identity, reason: "refunded" };
2703
+ if (event.type === "customer.subscription.deleted") return { kind: "gift_revoked", giftIntentId, ...identity, reason: "canceled" };
2684
2704
  return { kind: "ignored", type: event.type };
2685
2705
  }
2686
2706
  const ref = findApplicationRef(obj);