@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/README.md +18 -5
- package/dist/{copy-context-Buz_27Ed.d.ts → copy-context-Dbu9Bbq1.d.ts} +2 -4
- package/dist/index.cjs +40 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.js +40 -20
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +2 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/member/index.d.ts +2 -2
- package/dist/worker/index.cjs +90 -24
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +8 -5
- package/dist/worker/index.d.ts +8 -5
- package/dist/worker/index.js +90 -24
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/runbooks/greenfield.md +21 -4
package/README.md
CHANGED
|
@@ -517,11 +517,20 @@ it on both the Built Not Found hub and each participating Chapter:
|
|
|
517
517
|
|
|
518
518
|
```ts
|
|
519
519
|
// Built Not Found
|
|
520
|
+
network: {
|
|
521
|
+
targets: [
|
|
522
|
+
{ id: "oakland", url: "https://oakland.example", binding: "OAKLAND" },
|
|
523
|
+
{ id: "denver", url: "https://denver.example", binding: "DENVER" },
|
|
524
|
+
],
|
|
525
|
+
},
|
|
520
526
|
builderLibrary: {
|
|
521
527
|
sources: [{ sourceId: "oakland" }, { sourceId: "denver" }],
|
|
522
528
|
},
|
|
523
529
|
|
|
524
530
|
// Oakland
|
|
531
|
+
network: {
|
|
532
|
+
readers: [{ id: "built-not-found", fields: { person: ["name"] } }],
|
|
533
|
+
},
|
|
525
534
|
builderLibrary: {
|
|
526
535
|
sources: [{ sourceId: "built-not-found" }],
|
|
527
536
|
},
|
|
@@ -544,18 +553,22 @@ Saving a library item never creates or changes a member page.
|
|
|
544
553
|
An authenticated admin or site-building agent reads the bounded, published-only
|
|
545
554
|
manifest from `/api/admin/builder-library/manifest`. On Built Not Found, pass a
|
|
546
555
|
configured `chapterId` query parameter; on a Chapter, the server fixes the
|
|
547
|
-
viewer to that Chapter.
|
|
548
|
-
`
|
|
549
|
-
|
|
556
|
+
viewer to that Chapter. Every Builder source must already be the corresponding
|
|
557
|
+
`network.target` (on Built Not Found) or `network.reader` (on a Chapter). Signed
|
|
558
|
+
delivery reuses that Chapter network edge: the Chapter's existing
|
|
559
|
+
`network_share_secret` is the same value Built Not Found already stores as
|
|
560
|
+
`network_share_<chapter_id>`. Builder Library declares and provisions no secret
|
|
561
|
+
of its own.
|
|
550
562
|
|
|
551
563
|
```bash
|
|
552
564
|
odla-ai chapter builder-library validate --file oakland-library.json --source-type chapter --source oakland --json
|
|
553
565
|
odla-ai chapter builder-library manifest --file oakland-library.json --chapter oakland --json
|
|
554
566
|
odla-ai chapter builder-library deliver --file oakland-library.json \
|
|
555
|
-
--url https://builtnotfoundcapital.com --from-env
|
|
567
|
+
--url https://builtnotfoundcapital.com --from-env NETWORK_SHARE_SECRET --json
|
|
556
568
|
```
|
|
557
569
|
|
|
558
|
-
The
|
|
570
|
+
The existing network share secret is accepted only from a named environment
|
|
571
|
+
variable or stdin; it is never copied into Builder Library config or argv.
|
|
559
572
|
For a Built Not Found local document, add `--target oakland --environment dev`;
|
|
560
573
|
the CLI signs only its global resources for that Chapter, never BNF-only items.
|
|
561
574
|
|
|
@@ -118,16 +118,14 @@ interface ResolvedMemberContent {
|
|
|
118
118
|
interface ChapterBuilderLibrary {
|
|
119
119
|
/** Keep local admin authoring enabled. Defaults to true. */
|
|
120
120
|
localAuthoring?: boolean;
|
|
121
|
-
/**
|
|
121
|
+
/** Existing Chapter-network peers this app accepts. Hub sources must be
|
|
122
|
+
* configured network targets; a Chapter source must be a configured reader. */
|
|
122
123
|
sources?: readonly {
|
|
123
124
|
sourceId: string;
|
|
124
|
-
/** Vault key holding the dedicated Builder Library edge secret. */
|
|
125
|
-
secretName?: string;
|
|
126
125
|
}[];
|
|
127
126
|
}
|
|
128
127
|
interface ResolvedBuilderLibrarySource {
|
|
129
128
|
sourceId: string;
|
|
130
|
-
secretName: string;
|
|
131
129
|
}
|
|
132
130
|
/** Validated Builder Library settings carried on a resolved Chapter. */
|
|
133
131
|
interface ResolvedBuilderLibrary {
|
package/dist/index.cjs
CHANGED
|
@@ -402,6 +402,7 @@ var applicationAttrs = {
|
|
|
402
402
|
tierId: attr("string", { indexed: true, optional: true }),
|
|
403
403
|
stripeCustomerId: attr("string", { indexed: true, optional: true }),
|
|
404
404
|
stripeSubscriptionId: attr("string", { indexed: true, optional: true }),
|
|
405
|
+
stripeRuntime: attr("string", { indexed: true, optional: true }),
|
|
405
406
|
renewalAt: attr("number", { optional: true }),
|
|
406
407
|
disclaimerAckAt: attr("number", { optional: true }),
|
|
407
408
|
refundPolicyAckAt: attr("number", { optional: true }),
|
|
@@ -497,6 +498,7 @@ var giftPurchaseIntentsEntity = { attrs: {
|
|
|
497
498
|
id: id2(),
|
|
498
499
|
chapterId: attr3("string", { indexed: true }),
|
|
499
500
|
environment: attr3("string", { indexed: true }),
|
|
501
|
+
runtime: attr3("string", { optional: true, indexed: true }),
|
|
500
502
|
purchaserType: attr3("string", { indexed: true }),
|
|
501
503
|
purchaserApplicationId: attr3("string", { optional: true, indexed: true }),
|
|
502
504
|
purchaserUserId: attr3("string", { optional: true, indexed: true }),
|
|
@@ -1710,8 +1712,7 @@ function resolveMemberContent(input) {
|
|
|
1710
1712
|
|
|
1711
1713
|
// src/config-builder-library.ts
|
|
1712
1714
|
var SLUG4 = /^[a-z0-9][a-z0-9-]{1,62}$/;
|
|
1713
|
-
|
|
1714
|
-
function resolveBuilderLibrary(mode, input) {
|
|
1715
|
+
function resolveBuilderLibrary(mode, input, network = { targets: [], readers: [] }) {
|
|
1715
1716
|
if (!input) return null;
|
|
1716
1717
|
if (input.sources !== void 0 && !Array.isArray(input.sources)) {
|
|
1717
1718
|
throw new Error("defineChapter.builderLibrary.sources: must be an array");
|
|
@@ -1731,11 +1732,19 @@ function resolveBuilderLibrary(mode, input) {
|
|
|
1731
1732
|
throw new Error(`defineChapter.builderLibrary.sources: duplicate source "${source.sourceId}"`);
|
|
1732
1733
|
}
|
|
1733
1734
|
seen.add(source.sourceId);
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1735
|
+
if ("secretName" in source) {
|
|
1736
|
+
throw new Error(
|
|
1737
|
+
`defineChapter.builderLibrary.sources[${index}].secretName: remove this field; Builder Library reuses the Chapter network edge`
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
const networkPeer = mode === "hub" ? network.targets.some((target) => target.id === source.sourceId) : network.readers.some((reader) => reader.id === source.sourceId);
|
|
1741
|
+
if (!networkPeer) {
|
|
1742
|
+
const side = mode === "hub" ? "targets" : "readers";
|
|
1743
|
+
throw new Error(
|
|
1744
|
+
`defineChapter.builderLibrary.sources[${index}]: "${source.sourceId}" must already be configured in network.${side}`
|
|
1745
|
+
);
|
|
1737
1746
|
}
|
|
1738
|
-
return { sourceId: source.sourceId
|
|
1747
|
+
return { sourceId: source.sourceId };
|
|
1739
1748
|
});
|
|
1740
1749
|
return { enabled: true, localAuthoring: input.localAuthoring !== false, sources };
|
|
1741
1750
|
}
|
|
@@ -1774,7 +1783,7 @@ function defineChapter(config) {
|
|
|
1774
1783
|
const network = resolveNetwork(config, crm);
|
|
1775
1784
|
const signupControl = resolveSignupControl(config.signupControl);
|
|
1776
1785
|
const memberContent = resolveMemberContent(config.memberContent);
|
|
1777
|
-
const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary);
|
|
1786
|
+
const builderLibrary = resolveBuilderLibrary(mode, config.builderLibrary, network);
|
|
1778
1787
|
const formation = resolveLeaderFormation(config.formation, crm);
|
|
1779
1788
|
if (signupControl && mode !== "chapter") {
|
|
1780
1789
|
throw new Error("defineChapter.signupControl: only chapter-mode followers can receive signup control");
|
|
@@ -2098,13 +2107,6 @@ function createChapterIntegration(chapter, options = {}) {
|
|
|
2098
2107
|
...emails?.debugEmail ? { debugEmail: emails.debugEmail } : {}
|
|
2099
2108
|
});
|
|
2100
2109
|
const seeds = [...crmDesc.seeds ?? []];
|
|
2101
|
-
const builderLibrarySecrets = [...new Set(
|
|
2102
|
-
chapter.builderLibrary?.sources.map((source) => source.secretName) ?? []
|
|
2103
|
-
)].map((name) => ({
|
|
2104
|
-
name,
|
|
2105
|
-
description: "Signing secret for private Builder Library delivery from a configured source.",
|
|
2106
|
-
required: true
|
|
2107
|
-
}));
|
|
2108
2110
|
const group = chapter.groupSeed();
|
|
2109
2111
|
if (group) {
|
|
2110
2112
|
seeds.push({ id: "group", ns: "groups", key: { attr: "id", value: chapter.id }, attrs: { ...group, createdAt: now } });
|
|
@@ -2137,7 +2139,7 @@ function createChapterIntegration(chapter, options = {}) {
|
|
|
2137
2139
|
},
|
|
2138
2140
|
rules: { ...crmDesc.rules, ...chapter.rules },
|
|
2139
2141
|
seeds,
|
|
2140
|
-
secrets:
|
|
2142
|
+
secrets: [],
|
|
2141
2143
|
probes: [
|
|
2142
2144
|
...crmDesc.probes ?? [],
|
|
2143
2145
|
// The snapshot route is deliberately private. A credential-free smoke
|
|
@@ -2842,12 +2844,16 @@ function findApplicationRef(obj) {
|
|
|
2842
2844
|
const applicationId = candidates.map(pick).find(Boolean);
|
|
2843
2845
|
const appId = candidates.map((metadata) => typeof metadata.odlaAppId === "string" ? metadata.odlaAppId : void 0).find(Boolean);
|
|
2844
2846
|
const environment = candidates.map((metadata) => typeof metadata.odlaEnvironment === "string" ? metadata.odlaEnvironment : void 0).find(Boolean);
|
|
2847
|
+
const dataEnvironment = candidates.map((metadata) => typeof metadata.odlaDataEnvironment === "string" ? metadata.odlaDataEnvironment : void 0).find(Boolean);
|
|
2848
|
+
const runtime = candidates.map((metadata) => typeof metadata.odlaRuntime === "string" ? metadata.odlaRuntime : void 0).find(Boolean);
|
|
2845
2849
|
const customerId = typeof obj.customer === "string" ? obj.customer : void 0;
|
|
2846
2850
|
return {
|
|
2847
2851
|
...applicationId ? { applicationId } : {},
|
|
2848
2852
|
...customerId ? { customerId } : {},
|
|
2849
2853
|
...appId ? { appId } : {},
|
|
2850
|
-
...environment ? { environment } : {}
|
|
2854
|
+
...environment ? { environment } : {},
|
|
2855
|
+
...dataEnvironment ? { dataEnvironment } : {},
|
|
2856
|
+
...runtime ? { runtime } : {}
|
|
2851
2857
|
};
|
|
2852
2858
|
}
|
|
2853
2859
|
function findGiftRef(obj) {
|
|
@@ -2857,17 +2863,31 @@ function findGiftRef(obj) {
|
|
|
2857
2863
|
const subscription = obj.subscription_details ?? parent?.subscription_details;
|
|
2858
2864
|
const giftIntentId = pick(metaOf(obj)) ?? pick(metaOf(subscription));
|
|
2859
2865
|
const subscriptionId = typeof obj.subscription === "string" ? obj.subscription : typeof subscription?.subscription === "string" ? String(subscription.subscription) : void 0;
|
|
2860
|
-
|
|
2866
|
+
const candidates = [metaOf(obj), metaOf(subscription)];
|
|
2867
|
+
const pickIdentity = (name) => candidates.map((metadata) => typeof metadata[name] === "string" ? String(metadata[name]) : void 0).find(Boolean);
|
|
2868
|
+
const appId = pickIdentity("odlaAppId");
|
|
2869
|
+
const environment = pickIdentity("odlaEnvironment");
|
|
2870
|
+
const dataEnvironment = pickIdentity("odlaDataEnvironment");
|
|
2871
|
+
const runtime = pickIdentity("odlaRuntime");
|
|
2872
|
+
return {
|
|
2873
|
+
...giftIntentId ? { giftIntentId } : {},
|
|
2874
|
+
providerObjectId: subscriptionId ?? String(obj.id ?? ""),
|
|
2875
|
+
...appId ? { appId } : {},
|
|
2876
|
+
...environment ? { environment } : {},
|
|
2877
|
+
...dataEnvironment ? { dataEnvironment } : {},
|
|
2878
|
+
...runtime ? { runtime } : {}
|
|
2879
|
+
};
|
|
2861
2880
|
}
|
|
2862
2881
|
function normalizeWebhookEvent(event) {
|
|
2863
2882
|
const obj = event.data?.object ?? {};
|
|
2864
2883
|
const gift = findGiftRef(obj);
|
|
2865
2884
|
if (gift.giftIntentId) {
|
|
2885
|
+
const { giftIntentId, providerObjectId, ...identity } = gift;
|
|
2866
2886
|
if (event.type === "payment_intent.succeeded" || event.type === "invoice.paid" && obj.billing_reason === "subscription_create") {
|
|
2867
|
-
return { kind: "gift_paid", giftIntentId
|
|
2887
|
+
return { kind: "gift_paid", giftIntentId, providerObjectId, ...identity };
|
|
2868
2888
|
}
|
|
2869
|
-
if (event.type === "charge.refunded") return { kind: "gift_revoked", giftIntentId
|
|
2870
|
-
if (event.type === "customer.subscription.deleted") return { kind: "gift_revoked", giftIntentId
|
|
2889
|
+
if (event.type === "charge.refunded") return { kind: "gift_revoked", giftIntentId, ...identity, reason: "refunded" };
|
|
2890
|
+
if (event.type === "customer.subscription.deleted") return { kind: "gift_revoked", giftIntentId, ...identity, reason: "canceled" };
|
|
2871
2891
|
return { kind: "ignored", type: event.type };
|
|
2872
2892
|
}
|
|
2873
2893
|
const ref = findApplicationRef(obj);
|