@odla-ai/chapter 0.40.1 → 0.40.3
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 +9 -4
- package/dist/{chunk-4K2Q5JBC.js → chunk-O22RRPBT.js} +232 -84
- package/dist/chunk-O22RRPBT.js.map +1 -0
- package/dist/index.cjs +24 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -6
- package/dist/index.d.ts +14 -6
- package/dist/index.js +24 -9
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +6 -2
- package/dist/ui/admin/index.js +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/worker/index.cjs +73 -19
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +73 -19
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4K2Q5JBC.js.map +0 -1
package/dist/ui/admin/index.d.ts
CHANGED
|
@@ -407,16 +407,20 @@ interface AdminBuilderLibraryData {
|
|
|
407
407
|
receivedAt: number;
|
|
408
408
|
}[];
|
|
409
409
|
localAuthoring: boolean;
|
|
410
|
+
/** Closed set of Chapter destinations the server will accept. */
|
|
411
|
+
chapterTargets: {
|
|
412
|
+
chapterId: string;
|
|
413
|
+
}[];
|
|
410
414
|
visibility: {
|
|
411
415
|
builtNotFoundSeesChapterSources: boolean;
|
|
412
416
|
chaptersSeeSiblingSources: false;
|
|
413
417
|
};
|
|
414
418
|
}
|
|
415
|
-
/**
|
|
419
|
+
/** CMS-style private source-material list and editor. */
|
|
416
420
|
declare function BuilderLibraryAdmin({ ctx, initialData }: {
|
|
417
421
|
ctx: AdminSectionContext;
|
|
418
422
|
initialData?: AdminBuilderLibraryData;
|
|
419
|
-
}):
|
|
423
|
+
}): JSX.Element;
|
|
420
424
|
/** Reusable private Builder Library section for the standard Settings workspace. */
|
|
421
425
|
declare function builderLibrarySection(): AdminSection;
|
|
422
426
|
|
package/dist/ui/admin/index.js
CHANGED
package/dist/ui/index.js
CHANGED
package/dist/worker/index.cjs
CHANGED
|
@@ -1676,7 +1676,14 @@ async function createClerkUser(secretKey, input, fetchImpl = fetch) {
|
|
|
1676
1676
|
headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
|
|
1677
1677
|
body: JSON.stringify(body)
|
|
1678
1678
|
});
|
|
1679
|
-
if (res.ok)
|
|
1679
|
+
if (res.ok) {
|
|
1680
|
+
const body2 = typeof res.json === "function" ? await res.json().catch(() => null) : null;
|
|
1681
|
+
return {
|
|
1682
|
+
ok: true,
|
|
1683
|
+
status: res.status,
|
|
1684
|
+
...typeof body2?.id === "string" && body2.id ? { userId: body2.id } : {}
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1680
1687
|
const healed = heal(res.status);
|
|
1681
1688
|
const repair = input.repairMetadata ?? {
|
|
1682
1689
|
...input.publicMetadata !== void 0 ? { publicMetadata: input.publicMetadata } : {},
|
|
@@ -1726,6 +1733,34 @@ async function syncApplicantPrivateMetadata(db, chapter, applicationId, fields,
|
|
|
1726
1733
|
return false;
|
|
1727
1734
|
}
|
|
1728
1735
|
}
|
|
1736
|
+
var AccountProvisioningError = class extends Error {
|
|
1737
|
+
constructor(code, status) {
|
|
1738
|
+
super(code);
|
|
1739
|
+
this.code = code;
|
|
1740
|
+
this.status = status;
|
|
1741
|
+
}
|
|
1742
|
+
code;
|
|
1743
|
+
status;
|
|
1744
|
+
name = "AccountProvisioningError";
|
|
1745
|
+
};
|
|
1746
|
+
async function provisionApplicantForRoute(db, chapter, applicationId, fields) {
|
|
1747
|
+
try {
|
|
1748
|
+
await provisionApplicant(db, chapter, applicationId, fields);
|
|
1749
|
+
return null;
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
if (!(error instanceof AccountProvisioningError)) throw error;
|
|
1752
|
+
return {
|
|
1753
|
+
status: error.status,
|
|
1754
|
+
body: {
|
|
1755
|
+
error: {
|
|
1756
|
+
code: error.code,
|
|
1757
|
+
message: error.code === "clerk_secret_unavailable" ? "account provisioning credential is unavailable" : "account provisioning failed"
|
|
1758
|
+
},
|
|
1759
|
+
applicationId
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1729
1764
|
async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
1730
1765
|
const email = typeof fields.email === "string" ? fields.email : "";
|
|
1731
1766
|
if (!email) return;
|
|
@@ -1750,9 +1785,9 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
|
1750
1785
|
} catch {
|
|
1751
1786
|
}
|
|
1752
1787
|
if (chapter.account === "none") return;
|
|
1788
|
+
const secret = await getVaultSecret(db, "clerk_secret_key");
|
|
1789
|
+
if (!secret) throw new AccountProvisioningError("clerk_secret_unavailable", 503);
|
|
1753
1790
|
try {
|
|
1754
|
-
const secret = await getVaultSecret(db, "clerk_secret_key");
|
|
1755
|
-
if (!secret) return;
|
|
1756
1791
|
const privateMetadata = applicantPrivateMetadata(chapter, applicationId, fields);
|
|
1757
1792
|
if (chapter.account === "create") {
|
|
1758
1793
|
const result = await createClerkUser(secret, {
|
|
@@ -1765,11 +1800,13 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
|
1765
1800
|
privateMetadata: applicantPrivateMetadata(chapter, applicationId, fields, true)
|
|
1766
1801
|
}
|
|
1767
1802
|
});
|
|
1768
|
-
if (result.ok
|
|
1769
|
-
|
|
1803
|
+
if (!result.ok || result.existed && !result.refreshed) {
|
|
1804
|
+
throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
|
|
1770
1805
|
}
|
|
1806
|
+
await markPrivateMetadataSynced(db, applicationId, result.userId);
|
|
1771
1807
|
} else {
|
|
1772
1808
|
const result = await createClerkInvitation(secret, { email });
|
|
1809
|
+
if (!result.ok) throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
|
|
1773
1810
|
if (result.existed) {
|
|
1774
1811
|
const refreshed = await updateClerkUserMetadataByEmail(secret, email, {
|
|
1775
1812
|
publicMetadata: publicProfileRemoval,
|
|
@@ -1778,7 +1815,9 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
|
1778
1815
|
if (refreshed) await markPrivateMetadataSynced(db, applicationId);
|
|
1779
1816
|
}
|
|
1780
1817
|
}
|
|
1781
|
-
} catch {
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
if (error instanceof AccountProvisioningError) throw error;
|
|
1820
|
+
throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
|
|
1782
1821
|
}
|
|
1783
1822
|
}
|
|
1784
1823
|
async function notifyAdminOfApplication(db, env, chapterId, applicationId, fields) {
|
|
@@ -1816,12 +1855,16 @@ async function notifyAdminOfApplication(db, env, chapterId, applicationId, field
|
|
|
1816
1855
|
}
|
|
1817
1856
|
|
|
1818
1857
|
// src/worker-routes.ts
|
|
1819
|
-
async function memberSessionApplication(db, chapterId,
|
|
1858
|
+
async function memberSessionApplication(db, chapterId, identity) {
|
|
1820
1859
|
let apps = (await db.query({ applications: { $: {
|
|
1821
|
-
where: {
|
|
1860
|
+
where: { clerkUserId: identity.userId },
|
|
1822
1861
|
limit: 20
|
|
1823
1862
|
} } })).applications;
|
|
1824
|
-
if (!apps?.length) apps = (await db.query({ applications: { $: {
|
|
1863
|
+
if (!apps?.length && identity.email) apps = (await db.query({ applications: { $: {
|
|
1864
|
+
where: { emailNormalized: normalizeChapterEmail(identity.email) },
|
|
1865
|
+
limit: 20
|
|
1866
|
+
} } })).applications;
|
|
1867
|
+
if (!apps?.length && identity.email) apps = (await db.query({ applications: { $: { where: { email: identity.email }, limit: 20 } } })).applications;
|
|
1825
1868
|
const app = Array.isArray(apps) ? [...apps].sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0] : void 0;
|
|
1826
1869
|
if (!app) return null;
|
|
1827
1870
|
const meetings = (await db.query({ meetings: { $: { where: { applicationId: app.id, status: "scheduled" }, order: { createdAt: "desc" }, limit: 1 } } })).meetings;
|
|
@@ -1852,8 +1895,8 @@ var handleMe = async (req, url, env, ctx) => {
|
|
|
1852
1895
|
const role = await ctx.roleFor(db, u);
|
|
1853
1896
|
const superAdmin = await ctx.isSuperAdminEmail(db, u.email);
|
|
1854
1897
|
const base = { authorized: isAdminRole(role, ctx.auth), role, superAdmin, email: u.email ?? null };
|
|
1855
|
-
if (ctx.chapter.mode !== "chapter"
|
|
1856
|
-
const member = await memberSessionApplication(db, ctx.chapter.id, u.email);
|
|
1898
|
+
if (ctx.chapter.mode !== "chapter") return json(base);
|
|
1899
|
+
const member = await memberSessionApplication(db, ctx.chapter.id, { userId: u.userId, email: u.email });
|
|
1857
1900
|
let privateMetadataSynced = false;
|
|
1858
1901
|
if (member && ctx.chapter.account !== "none" && typeof member.source.clerkPrivateMetadataSyncedAt !== "number") {
|
|
1859
1902
|
privateMetadataSynced = await syncApplicantPrivateMetadata(
|
|
@@ -2002,8 +2045,9 @@ var handleMember = async (req, url, env, ctx) => {
|
|
|
2002
2045
|
if (effectiveChapter.sends.adminNotification === "submit") {
|
|
2003
2046
|
await notifyAdminOfApplication(db, env, chapter.id, result.id, parsed);
|
|
2004
2047
|
}
|
|
2005
|
-
await provisionApplicant(db, effectiveChapter, result.id, parsed);
|
|
2006
2048
|
}
|
|
2049
|
+
const provisioningFailure = await provisionApplicantForRoute(db, effectiveChapter, result.id, parsed);
|
|
2050
|
+
if (provisioningFailure) return json(provisioningFailure.body, provisioningFailure.status);
|
|
2007
2051
|
const apps = (await db.query({ applications: { $: { where: { id: result.id }, limit: 1 } } })).applications;
|
|
2008
2052
|
const canonical2 = Array.isArray(apps) ? apps[0] : void 0;
|
|
2009
2053
|
const groups = (await db.query({ groups: { $: { where: { id: chapter.id }, limit: 1 } } })).groups;
|
|
@@ -6039,8 +6083,8 @@ function validateBuilderLibraryDocument(value, options = {}) {
|
|
|
6039
6083
|
if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
|
|
6040
6084
|
errors.push(`document.resources.${resource.id}: Chapter sources may author only their own chapter scope`);
|
|
6041
6085
|
}
|
|
6042
|
-
if (sourceType === "built_not_found" && resource.scope.kind === "chapter") {
|
|
6043
|
-
errors.push(`document.resources.${resource.id}: Built Not Found
|
|
6086
|
+
if (sourceType === "built_not_found" && resource.scope.kind === "chapter" && !(options.allowedChapterIds ?? []).includes(resource.scope.chapterId)) {
|
|
6087
|
+
errors.push(`document.resources.${resource.id}: Built Not Found may target only configured Chapters`);
|
|
6044
6088
|
}
|
|
6045
6089
|
}
|
|
6046
6090
|
if (errors.length) return { ok: false, errors };
|
|
@@ -6200,12 +6244,13 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
|
|
|
6200
6244
|
const checked = validateBuilderLibraryDocument(input.document, {
|
|
6201
6245
|
sourceType,
|
|
6202
6246
|
sourceAppId: verified.source.sourceId,
|
|
6203
|
-
targetAppId: ctx.chapter.id
|
|
6247
|
+
targetAppId: ctx.chapter.id,
|
|
6248
|
+
...ctx.chapter.mode === "chapter" ? { allowedChapterIds: [ctx.chapter.id] } : {}
|
|
6204
6249
|
});
|
|
6205
6250
|
if (!checked.ok) return json({ ok: false, reason: "invalid_document", errors: checked.errors }, 400);
|
|
6206
6251
|
const document = checked.value;
|
|
6207
|
-
if (ctx.chapter.mode === "chapter" && document.resources.some((resource) => resource.scope.kind !== "global")) {
|
|
6208
|
-
return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global
|
|
6252
|
+
if (ctx.chapter.mode === "chapter" && document.resources.some((resource) => resource.scope.kind !== "global" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== ctx.chapter.id))) {
|
|
6253
|
+
return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global or this Chapter's resources"] }, 400);
|
|
6209
6254
|
}
|
|
6210
6255
|
if (!document.target || document.target.environment !== env.ODLA_ENV) return json({ ok: false, reason: "wrong_environment" }, 409);
|
|
6211
6256
|
if (!digest || await builderLibraryDigest(document) !== digest) return json({ ok: false, reason: "invalid_digest" }, 400);
|
|
@@ -6311,7 +6356,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6311
6356
|
source: { type: "built_not_found", appId: ctx.chapter.id },
|
|
6312
6357
|
resources: [...local3?.document.resources ?? [], promoted]
|
|
6313
6358
|
};
|
|
6314
|
-
const checked2 = validateBuilderLibraryDocument(document, {
|
|
6359
|
+
const checked2 = validateBuilderLibraryDocument(document, {
|
|
6360
|
+
sourceType: "built_not_found",
|
|
6361
|
+
sourceAppId: ctx.chapter.id,
|
|
6362
|
+
allowedChapterIds: ctx.chapter.builderLibrary.sources.map((source2) => source2.sourceId)
|
|
6363
|
+
});
|
|
6315
6364
|
if (!checked2.ok) return json({ error: "promoted resource is invalid", errors: checked2.errors }, 400);
|
|
6316
6365
|
try {
|
|
6317
6366
|
const saved = await saveLocalBuilderLibrary(db, checked2.value, env.ODLA_ENV, expectedRevision2, now2, actorFor(user));
|
|
@@ -6326,6 +6375,7 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6326
6375
|
local: local3 ? { revision: local3.revision, document: local3.document } : null,
|
|
6327
6376
|
sources: controls.map(({ sourceId, revision, digest, document, receivedAt }) => ({ sourceId, revision, digest, document, receivedAt })),
|
|
6328
6377
|
localAuthoring: ctx.chapter.builderLibrary.localAuthoring,
|
|
6378
|
+
chapterTargets: ctx.chapter.mode === "hub" ? ctx.chapter.builderLibrary.sources.map((source) => ({ chapterId: source.sourceId })) : [{ chapterId: ctx.chapter.id }],
|
|
6329
6379
|
visibility: { builtNotFoundSeesChapterSources: ctx.chapter.mode === "hub", chaptersSeeSiblingSources: false }
|
|
6330
6380
|
});
|
|
6331
6381
|
}
|
|
@@ -6344,7 +6394,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6344
6394
|
const now = Date.now();
|
|
6345
6395
|
const actor = actorFor(user);
|
|
6346
6396
|
const authoritative = withLocalAuthority2(input.document, ctx.chapter.mode, ctx.chapter.id, expectedRevision + 1, now, actor, local3?.document);
|
|
6347
|
-
const checked = validateBuilderLibraryDocument(authoritative, {
|
|
6397
|
+
const checked = validateBuilderLibraryDocument(authoritative, {
|
|
6398
|
+
sourceType: ctx.chapter.mode === "hub" ? "built_not_found" : "chapter",
|
|
6399
|
+
sourceAppId: ctx.chapter.id,
|
|
6400
|
+
...ctx.chapter.mode === "hub" ? { allowedChapterIds: ctx.chapter.builderLibrary.sources.map((source) => source.sourceId) } : {}
|
|
6401
|
+
});
|
|
6348
6402
|
if (!checked.ok) return json({ error: "invalid Builder Library", errors: checked.errors }, 400);
|
|
6349
6403
|
try {
|
|
6350
6404
|
const saved = await saveLocalBuilderLibrary(db, checked.value, env.ODLA_ENV, expectedRevision, now, actor);
|