@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/worker/index.js
CHANGED
|
@@ -1648,7 +1648,14 @@ async function createClerkUser(secretKey, input, fetchImpl = fetch) {
|
|
|
1648
1648
|
headers: { authorization: `Bearer ${secretKey}`, "content-type": "application/json" },
|
|
1649
1649
|
body: JSON.stringify(body)
|
|
1650
1650
|
});
|
|
1651
|
-
if (res.ok)
|
|
1651
|
+
if (res.ok) {
|
|
1652
|
+
const body2 = typeof res.json === "function" ? await res.json().catch(() => null) : null;
|
|
1653
|
+
return {
|
|
1654
|
+
ok: true,
|
|
1655
|
+
status: res.status,
|
|
1656
|
+
...typeof body2?.id === "string" && body2.id ? { userId: body2.id } : {}
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1652
1659
|
const healed = heal(res.status);
|
|
1653
1660
|
const repair = input.repairMetadata ?? {
|
|
1654
1661
|
...input.publicMetadata !== void 0 ? { publicMetadata: input.publicMetadata } : {},
|
|
@@ -1698,6 +1705,34 @@ async function syncApplicantPrivateMetadata(db, chapter, applicationId, fields,
|
|
|
1698
1705
|
return false;
|
|
1699
1706
|
}
|
|
1700
1707
|
}
|
|
1708
|
+
var AccountProvisioningError = class extends Error {
|
|
1709
|
+
constructor(code, status) {
|
|
1710
|
+
super(code);
|
|
1711
|
+
this.code = code;
|
|
1712
|
+
this.status = status;
|
|
1713
|
+
}
|
|
1714
|
+
code;
|
|
1715
|
+
status;
|
|
1716
|
+
name = "AccountProvisioningError";
|
|
1717
|
+
};
|
|
1718
|
+
async function provisionApplicantForRoute(db, chapter, applicationId, fields) {
|
|
1719
|
+
try {
|
|
1720
|
+
await provisionApplicant(db, chapter, applicationId, fields);
|
|
1721
|
+
return null;
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
if (!(error instanceof AccountProvisioningError)) throw error;
|
|
1724
|
+
return {
|
|
1725
|
+
status: error.status,
|
|
1726
|
+
body: {
|
|
1727
|
+
error: {
|
|
1728
|
+
code: error.code,
|
|
1729
|
+
message: error.code === "clerk_secret_unavailable" ? "account provisioning credential is unavailable" : "account provisioning failed"
|
|
1730
|
+
},
|
|
1731
|
+
applicationId
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1701
1736
|
async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
1702
1737
|
const email = typeof fields.email === "string" ? fields.email : "";
|
|
1703
1738
|
if (!email) return;
|
|
@@ -1722,9 +1757,9 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
|
1722
1757
|
} catch {
|
|
1723
1758
|
}
|
|
1724
1759
|
if (chapter.account === "none") return;
|
|
1760
|
+
const secret = await getVaultSecret(db, "clerk_secret_key");
|
|
1761
|
+
if (!secret) throw new AccountProvisioningError("clerk_secret_unavailable", 503);
|
|
1725
1762
|
try {
|
|
1726
|
-
const secret = await getVaultSecret(db, "clerk_secret_key");
|
|
1727
|
-
if (!secret) return;
|
|
1728
1763
|
const privateMetadata = applicantPrivateMetadata(chapter, applicationId, fields);
|
|
1729
1764
|
if (chapter.account === "create") {
|
|
1730
1765
|
const result = await createClerkUser(secret, {
|
|
@@ -1737,11 +1772,13 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
|
1737
1772
|
privateMetadata: applicantPrivateMetadata(chapter, applicationId, fields, true)
|
|
1738
1773
|
}
|
|
1739
1774
|
});
|
|
1740
|
-
if (result.ok
|
|
1741
|
-
|
|
1775
|
+
if (!result.ok || result.existed && !result.refreshed) {
|
|
1776
|
+
throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
|
|
1742
1777
|
}
|
|
1778
|
+
await markPrivateMetadataSynced(db, applicationId, result.userId);
|
|
1743
1779
|
} else {
|
|
1744
1780
|
const result = await createClerkInvitation(secret, { email });
|
|
1781
|
+
if (!result.ok) throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
|
|
1745
1782
|
if (result.existed) {
|
|
1746
1783
|
const refreshed = await updateClerkUserMetadataByEmail(secret, email, {
|
|
1747
1784
|
publicMetadata: publicProfileRemoval,
|
|
@@ -1750,7 +1787,9 @@ async function provisionApplicant(db, chapter, applicationId, fields) {
|
|
|
1750
1787
|
if (refreshed) await markPrivateMetadataSynced(db, applicationId);
|
|
1751
1788
|
}
|
|
1752
1789
|
}
|
|
1753
|
-
} catch {
|
|
1790
|
+
} catch (error) {
|
|
1791
|
+
if (error instanceof AccountProvisioningError) throw error;
|
|
1792
|
+
throw new AccountProvisioningError("clerk_account_provisioning_failed", 502);
|
|
1754
1793
|
}
|
|
1755
1794
|
}
|
|
1756
1795
|
async function notifyAdminOfApplication(db, env, chapterId, applicationId, fields) {
|
|
@@ -1788,12 +1827,16 @@ async function notifyAdminOfApplication(db, env, chapterId, applicationId, field
|
|
|
1788
1827
|
}
|
|
1789
1828
|
|
|
1790
1829
|
// src/worker-routes.ts
|
|
1791
|
-
async function memberSessionApplication(db, chapterId,
|
|
1830
|
+
async function memberSessionApplication(db, chapterId, identity) {
|
|
1792
1831
|
let apps = (await db.query({ applications: { $: {
|
|
1793
|
-
where: {
|
|
1832
|
+
where: { clerkUserId: identity.userId },
|
|
1794
1833
|
limit: 20
|
|
1795
1834
|
} } })).applications;
|
|
1796
|
-
if (!apps?.length) apps = (await db.query({ applications: { $: {
|
|
1835
|
+
if (!apps?.length && identity.email) apps = (await db.query({ applications: { $: {
|
|
1836
|
+
where: { emailNormalized: normalizeChapterEmail(identity.email) },
|
|
1837
|
+
limit: 20
|
|
1838
|
+
} } })).applications;
|
|
1839
|
+
if (!apps?.length && identity.email) apps = (await db.query({ applications: { $: { where: { email: identity.email }, limit: 20 } } })).applications;
|
|
1797
1840
|
const app = Array.isArray(apps) ? [...apps].sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))[0] : void 0;
|
|
1798
1841
|
if (!app) return null;
|
|
1799
1842
|
const meetings = (await db.query({ meetings: { $: { where: { applicationId: app.id, status: "scheduled" }, order: { createdAt: "desc" }, limit: 1 } } })).meetings;
|
|
@@ -1824,8 +1867,8 @@ var handleMe = async (req, url, env, ctx) => {
|
|
|
1824
1867
|
const role = await ctx.roleFor(db, u);
|
|
1825
1868
|
const superAdmin = await ctx.isSuperAdminEmail(db, u.email);
|
|
1826
1869
|
const base = { authorized: isAdminRole(role, ctx.auth), role, superAdmin, email: u.email ?? null };
|
|
1827
|
-
if (ctx.chapter.mode !== "chapter"
|
|
1828
|
-
const member = await memberSessionApplication(db, ctx.chapter.id, u.email);
|
|
1870
|
+
if (ctx.chapter.mode !== "chapter") return json(base);
|
|
1871
|
+
const member = await memberSessionApplication(db, ctx.chapter.id, { userId: u.userId, email: u.email });
|
|
1829
1872
|
let privateMetadataSynced = false;
|
|
1830
1873
|
if (member && ctx.chapter.account !== "none" && typeof member.source.clerkPrivateMetadataSyncedAt !== "number") {
|
|
1831
1874
|
privateMetadataSynced = await syncApplicantPrivateMetadata(
|
|
@@ -1974,8 +2017,9 @@ var handleMember = async (req, url, env, ctx) => {
|
|
|
1974
2017
|
if (effectiveChapter.sends.adminNotification === "submit") {
|
|
1975
2018
|
await notifyAdminOfApplication(db, env, chapter.id, result.id, parsed);
|
|
1976
2019
|
}
|
|
1977
|
-
await provisionApplicant(db, effectiveChapter, result.id, parsed);
|
|
1978
2020
|
}
|
|
2021
|
+
const provisioningFailure = await provisionApplicantForRoute(db, effectiveChapter, result.id, parsed);
|
|
2022
|
+
if (provisioningFailure) return json(provisioningFailure.body, provisioningFailure.status);
|
|
1979
2023
|
const apps = (await db.query({ applications: { $: { where: { id: result.id }, limit: 1 } } })).applications;
|
|
1980
2024
|
const canonical2 = Array.isArray(apps) ? apps[0] : void 0;
|
|
1981
2025
|
const groups = (await db.query({ groups: { $: { where: { id: chapter.id }, limit: 1 } } })).groups;
|
|
@@ -6015,8 +6059,8 @@ function validateBuilderLibraryDocument(value, options = {}) {
|
|
|
6015
6059
|
if (sourceType === "chapter" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== sourceAppId)) {
|
|
6016
6060
|
errors.push(`document.resources.${resource.id}: Chapter sources may author only their own chapter scope`);
|
|
6017
6061
|
}
|
|
6018
|
-
if (sourceType === "built_not_found" && resource.scope.kind === "chapter") {
|
|
6019
|
-
errors.push(`document.resources.${resource.id}: Built Not Found
|
|
6062
|
+
if (sourceType === "built_not_found" && resource.scope.kind === "chapter" && !(options.allowedChapterIds ?? []).includes(resource.scope.chapterId)) {
|
|
6063
|
+
errors.push(`document.resources.${resource.id}: Built Not Found may target only configured Chapters`);
|
|
6020
6064
|
}
|
|
6021
6065
|
}
|
|
6022
6066
|
if (errors.length) return { ok: false, errors };
|
|
@@ -6176,12 +6220,13 @@ var handleBuilderLibraryControl = async (req, url, env, ctx) => {
|
|
|
6176
6220
|
const checked = validateBuilderLibraryDocument(input.document, {
|
|
6177
6221
|
sourceType,
|
|
6178
6222
|
sourceAppId: verified.source.sourceId,
|
|
6179
|
-
targetAppId: ctx.chapter.id
|
|
6223
|
+
targetAppId: ctx.chapter.id,
|
|
6224
|
+
...ctx.chapter.mode === "chapter" ? { allowedChapterIds: [ctx.chapter.id] } : {}
|
|
6180
6225
|
});
|
|
6181
6226
|
if (!checked.ok) return json({ ok: false, reason: "invalid_document", errors: checked.errors }, 400);
|
|
6182
6227
|
const document = checked.value;
|
|
6183
|
-
if (ctx.chapter.mode === "chapter" && document.resources.some((resource) => resource.scope.kind !== "global")) {
|
|
6184
|
-
return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global
|
|
6228
|
+
if (ctx.chapter.mode === "chapter" && document.resources.some((resource) => resource.scope.kind !== "global" && (resource.scope.kind !== "chapter" || resource.scope.chapterId !== ctx.chapter.id))) {
|
|
6229
|
+
return json({ ok: false, reason: "invalid_document", errors: ["Built Not Found may deliver only global or this Chapter's resources"] }, 400);
|
|
6185
6230
|
}
|
|
6186
6231
|
if (!document.target || document.target.environment !== env.ODLA_ENV) return json({ ok: false, reason: "wrong_environment" }, 409);
|
|
6187
6232
|
if (!digest || await builderLibraryDigest(document) !== digest) return json({ ok: false, reason: "invalid_digest" }, 400);
|
|
@@ -6287,7 +6332,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6287
6332
|
source: { type: "built_not_found", appId: ctx.chapter.id },
|
|
6288
6333
|
resources: [...local3?.document.resources ?? [], promoted]
|
|
6289
6334
|
};
|
|
6290
|
-
const checked2 = validateBuilderLibraryDocument(document, {
|
|
6335
|
+
const checked2 = validateBuilderLibraryDocument(document, {
|
|
6336
|
+
sourceType: "built_not_found",
|
|
6337
|
+
sourceAppId: ctx.chapter.id,
|
|
6338
|
+
allowedChapterIds: ctx.chapter.builderLibrary.sources.map((source2) => source2.sourceId)
|
|
6339
|
+
});
|
|
6291
6340
|
if (!checked2.ok) return json({ error: "promoted resource is invalid", errors: checked2.errors }, 400);
|
|
6292
6341
|
try {
|
|
6293
6342
|
const saved = await saveLocalBuilderLibrary(db, checked2.value, env.ODLA_ENV, expectedRevision2, now2, actorFor(user));
|
|
@@ -6302,6 +6351,7 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6302
6351
|
local: local3 ? { revision: local3.revision, document: local3.document } : null,
|
|
6303
6352
|
sources: controls.map(({ sourceId, revision, digest, document, receivedAt }) => ({ sourceId, revision, digest, document, receivedAt })),
|
|
6304
6353
|
localAuthoring: ctx.chapter.builderLibrary.localAuthoring,
|
|
6354
|
+
chapterTargets: ctx.chapter.mode === "hub" ? ctx.chapter.builderLibrary.sources.map((source) => ({ chapterId: source.sourceId })) : [{ chapterId: ctx.chapter.id }],
|
|
6305
6355
|
visibility: { builtNotFoundSeesChapterSources: ctx.chapter.mode === "hub", chaptersSeeSiblingSources: false }
|
|
6306
6356
|
});
|
|
6307
6357
|
}
|
|
@@ -6320,7 +6370,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6320
6370
|
const now = Date.now();
|
|
6321
6371
|
const actor = actorFor(user);
|
|
6322
6372
|
const authoritative = withLocalAuthority2(input.document, ctx.chapter.mode, ctx.chapter.id, expectedRevision + 1, now, actor, local3?.document);
|
|
6323
|
-
const checked = validateBuilderLibraryDocument(authoritative, {
|
|
6373
|
+
const checked = validateBuilderLibraryDocument(authoritative, {
|
|
6374
|
+
sourceType: ctx.chapter.mode === "hub" ? "built_not_found" : "chapter",
|
|
6375
|
+
sourceAppId: ctx.chapter.id,
|
|
6376
|
+
...ctx.chapter.mode === "hub" ? { allowedChapterIds: ctx.chapter.builderLibrary.sources.map((source) => source.sourceId) } : {}
|
|
6377
|
+
});
|
|
6324
6378
|
if (!checked.ok) return json({ error: "invalid Builder Library", errors: checked.errors }, 400);
|
|
6325
6379
|
try {
|
|
6326
6380
|
const saved = await saveLocalBuilderLibrary(db, checked.value, env.ODLA_ENV, expectedRevision, now, actor);
|