@jant/core 0.3.50 → 0.4.1
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/{app-C7CtIQM-.js → app-DQgkp6yV.js} +155 -130
- package/dist/app-DYJLFZaM.js +6 -0
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-BbJ0FhON.css +2 -0
- package/dist/client/_assets/client-DqsPJKiP.js +272 -0
- package/dist/client/_assets/{client-auth-Ce5WEAVS.js → client-auth-N6fiJcOg.js} +82 -82
- package/dist/{export-ZBlfKSKm.js → export-DwH3ga3Y.js} +2 -2
- package/dist/{github-sync-C593r22F.js → github-sync-D2FO19Re.js} +2 -2
- package/dist/{github-sync-bL1hnx3Q.js → github-sync-eHOTYZGO.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/node.js +4 -4
- package/package.json +1 -1
- package/src/client/__tests__/compose-shortcuts.test.ts +1 -4
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1 -1
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +89 -0
- package/src/client/components/compose-types.ts +6 -1
- package/src/client/components/jant-compose-dialog.ts +2 -0
- package/src/client/components/jant-compose-editor.ts +2 -1
- package/src/client/components/jant-media-lightbox.ts +33 -10
- package/src/client/compose-bridge.ts +88 -25
- package/src/client/compose-launch.ts +0 -13
- package/src/client/palette-search-trigger.ts +35 -0
- package/src/client/thread-context.ts +1 -140
- package/src/client/upload-session.ts +77 -31
- package/src/client-auth.ts +1 -0
- package/src/i18n/locales/public/en.po +0 -4
- package/src/i18n/locales/public/zh-Hans.po +0 -4
- package/src/i18n/locales/public/zh-Hant.po +0 -4
- package/src/lib/__tests__/hosted-domain.test.ts +1 -1
- package/src/lib/hosted-domain-check.ts +21 -80
- package/src/lib/hosted-domain.ts +1 -1
- package/src/routes/api/internal/__tests__/sites.test.ts +168 -0
- package/src/routes/api/internal/sites.ts +63 -10
- package/src/routes/hosted/__tests__/domain-check.test.ts +30 -19
- package/src/routes/hosted/domain-check.ts +9 -14
- package/src/services/export-theme/assets/client-site.js +1 -1
- package/src/services/site-admin.ts +62 -1
- package/src/styles/tokens.css +0 -1
- package/src/styles/ui.css +0 -71
- package/src/ui/feed/ThreadPreview.tsx +34 -65
- package/src/ui/feed/__tests__/thread-preview.test.ts +64 -58
- package/src/ui/feed/thread-preview-state.ts +0 -48
- package/dist/app-CIx9SSOi.js +0 -6
- package/dist/client/_assets/client-BoUn7xBo.css +0 -2
- package/dist/client/_assets/client-dSfWfMe9.js +0 -272
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as getSitePathPrefix, c as normalizePath, d as sanitizeUrl, f as slugify, g as toPublicPath, h as toPublicHref, i as getSiteOrigin, m as toAbsoluteSiteUrl, n as extractDisplayDomain, o as isFullUrl, p as stripSitePathPrefix, r as extractDomain, s as isSafeInternalRedirect, t as buildSiteUrl, u as normalizeSiteUrl, v as __exportAll } from "./url-umUptr5z.js";
|
|
2
|
-
import { A as JANT_POSITIVE_LOGO_PNG_FILENAME, B as getJantLogoHref, C as formatYearMonth, D as HOME_BRANDING_LINK_LABEL, E as toISOString, F as getJantBundledAsset, G as base64ToUint8Array, H as JANT_LOGO_PATH_DATA, I as getJantIconFilename, L as getJantIconHref, M as getDefaultJantAppleTouchIconBytes, N as getDefaultJantFaviconIcoBytes, O as HOME_BRANDING_PREFIX, P as getJantBrandPackHref, R as getJantLogoFilename, S as formatTime, U as JANT_LOGO_VIEW_BOX, V as getJantPositiveLogoPngHref, W as arrayBufferToBase64, _ as getMediaUrl, b as formatRelativeAge, d as extractSummaryHtml, f as renderTiptapDocument, g as getImageUrl, h as escapeHtml, i as tiptapJsonToMarkdown, j as JANT_REPO_URL, k as JANT_BRAND_PACK_FILENAME, l as extractBodyText, m as trimTiptapBody, o as render, p as renderTiptapJson, s as toPlainText, t as createExportService, u as extractSummary, v as getPublicUrlForProvider, w as now, x as formatRelativeTime, y as formatDate, z as getJantLogoFills } from "./export-
|
|
2
|
+
import { A as JANT_POSITIVE_LOGO_PNG_FILENAME, B as getJantLogoHref, C as formatYearMonth, D as HOME_BRANDING_LINK_LABEL, E as toISOString, F as getJantBundledAsset, G as base64ToUint8Array, H as JANT_LOGO_PATH_DATA, I as getJantIconFilename, L as getJantIconHref, M as getDefaultJantAppleTouchIconBytes, N as getDefaultJantFaviconIcoBytes, O as HOME_BRANDING_PREFIX, P as getJantBrandPackHref, R as getJantLogoFilename, S as formatTime, U as JANT_LOGO_VIEW_BOX, V as getJantPositiveLogoPngHref, W as arrayBufferToBase64, _ as getMediaUrl, b as formatRelativeAge, d as extractSummaryHtml, f as renderTiptapDocument, g as getImageUrl, h as escapeHtml, i as tiptapJsonToMarkdown, j as JANT_REPO_URL, k as JANT_BRAND_PACK_FILENAME, l as extractBodyText, m as trimTiptapBody, o as render, p as renderTiptapJson, s as toPlainText, t as createExportService, u as extractSummary, v as getPublicUrlForProvider, w as now, x as formatRelativeTime, y as formatDate, z as getJantLogoFills } from "./export-DwH3ga3Y.js";
|
|
3
3
|
import { C as coalesceDisplayText, S as shouldUseSecureCookies, _ as getInternalAdminToken, a as getConfiguredSingleSiteUrl, b as getSiteResolutionMode, c as getDevApiToken, d as getHostedControlPlaneBaseUrl, f as getHostedControlPlaneDomainCheckSecret, g as getHostedControlPlaneSsoSecret, h as getHostedControlPlaneProviderLabel$1, i as getConfiguredSingleSitePathPrefix, l as getEnvString, m as getHostedControlPlaneInternalToken, n as getAuthSecret, o as getConfiguredStorageDriver, p as getHostedControlPlaneInternalBaseUrl, r as getConfiguredSingleSiteOrigin, s as getCorsOrigins, u as getGitHubAppConfig, v as getLocalStoragePath } from "./env-CgaH9Mut.js";
|
|
4
|
-
import { l as markdownToTiptapJson, o as createGitHubSyncService } from "./github-sync-
|
|
4
|
+
import { l as markdownToTiptapJson, o as createGitHubSyncService } from "./github-sync-eHOTYZGO.js";
|
|
5
5
|
import { a as listInstallationReposPage, n as getInstallation, o as searchInstallationRepos, t as buildInstallUrl } from "./github-app-D0GvNnqp.js";
|
|
6
6
|
import { r as parseRepoSlug, t as createGitHubClient } from "./github-api-Bh0PH3zr.js";
|
|
7
7
|
import { I18n } from "@lingui/core";
|
|
@@ -3418,10 +3418,10 @@ function normalizeThemeColorForMeta(color) {
|
|
|
3418
3418
|
* internal paths (e.g. `/_assets/client-HASH.js`) embedded by the Worker build
|
|
3419
3419
|
* from the Vite client manifest. Used only in production (IS_VITE_DEV=false).
|
|
3420
3420
|
*/ var IS_VITE_DEV = typeof __JANT_DEV__ !== "undefined" && __JANT_DEV__ === true;
|
|
3421
|
-
var CORE_VERSION = "0.
|
|
3422
|
-
var CLIENT_JS_FILE = "/_assets/client-
|
|
3423
|
-
var CLIENT_AUTH_JS_FILE = "/_assets/client-auth-
|
|
3424
|
-
var CLIENT_CSS_FILE = "/_assets/client-
|
|
3421
|
+
var CORE_VERSION = "0.4.1-ec7ca04d562a5649";
|
|
3422
|
+
var CLIENT_JS_FILE = "/_assets/client-DqsPJKiP.js";
|
|
3423
|
+
var CLIENT_AUTH_JS_FILE = "/_assets/client-auth-N6fiJcOg.js";
|
|
3424
|
+
var CLIENT_CSS_FILE = "/_assets/client-BbJ0FhON.css";
|
|
3425
3425
|
var CLIENT_CJK_CSS_FILE = "/_assets/client-cjk-B7Z0snDu.css";
|
|
3426
3426
|
var CLIENT_CJK_TC_CSS_FILE = "/_assets/client-cjk-tc-BesJYrb2.css";
|
|
3427
3427
|
var CLIENT_CJK_JP_CSS_FILE = "/_assets/client-cjk-jp-DZwrTzQC.css";
|
|
@@ -3739,7 +3739,7 @@ var IconSprite = () => {
|
|
|
3739
3739
|
const cjkSerifFont = appConfig?.cjkSerifFont ?? "off";
|
|
3740
3740
|
const cjkStylesheetPath = cjkSerifFont === "zh-Hans" ? IS_VITE_DEV ? assetPath("/src/style-cjk.css") : toPublicAssetPath(CLIENT_CJK_CSS_FILE, assetBasePath) : cjkSerifFont === "zh-Hant" ? IS_VITE_DEV ? assetPath("/src/style-cjk-tc.css") : toPublicAssetPath(CLIENT_CJK_TC_CSS_FILE, assetBasePath) : cjkSerifFont === "ja" ? IS_VITE_DEV ? assetPath("/src/style-cjk-jp.css") : toPublicAssetPath(CLIENT_CJK_JP_CSS_FILE, assetBasePath) : cjkSerifFont === "ko" ? IS_VITE_DEV ? assetPath("/src/style-cjk-kr.css") : toPublicAssetPath(CLIENT_CJK_KR_CSS_FILE, assetBasePath) : null;
|
|
3741
3741
|
const clientScriptPath = IS_VITE_DEV ? resolvedClientBundle === "full" ? assetPath("/src/client-auth.ts") : assetPath("/src/client.ts") : toPublicAssetPath(resolvedClientBundle === "full" ? CLIENT_AUTH_JS_FILE : CLIENT_JS_FILE, assetBasePath);
|
|
3742
|
-
const faviconAssetVersion = resolvedFaviconVersion || "0.
|
|
3742
|
+
const faviconAssetVersion = resolvedFaviconVersion || "0.4.1-ec7ca04d562a5649";
|
|
3743
3743
|
const resolvedFaviconHref = faviconHref ?? (faviconAssetVersion ? toPublicPath(`/favicon.ico?v=${faviconAssetVersion}`, sitePathPrefix) : toPublicPath("/favicon.ico", sitePathPrefix));
|
|
3744
3744
|
const resolvedAppleTouchHref = appleTouchHref ?? (faviconAssetVersion ? toPublicPath(`/apple-touch-icon.png?v=${faviconAssetVersion}`, sitePathPrefix) : toPublicPath("/apple-touch-icon.png", sitePathPrefix));
|
|
3745
3745
|
const socialImageHref = resolvedSocialImagePath && (isFullUrl(resolvedSocialImagePath) || resolvedSocialImagePath.startsWith("//") ? resolvedSocialImagePath : toAbsoluteSiteUrl(resolvedSocialImagePath, appConfig?.siteUrl || "", sitePathPrefix));
|
|
@@ -6397,83 +6397,44 @@ hostedSsoRoutes.get("/__sso", async (c) => {
|
|
|
6397
6397
|
}
|
|
6398
6398
|
});
|
|
6399
6399
|
//#endregion
|
|
6400
|
-
//#region src/lib/crypto.ts
|
|
6401
|
-
/**
|
|
6402
|
-
* Compare two byte arrays using a constant-time loop.
|
|
6403
|
-
*
|
|
6404
|
-
* This avoids relying on runtime-specific crypto helpers so the same behavior
|
|
6405
|
-
* works in Node.js and Workers.
|
|
6406
|
-
*
|
|
6407
|
-
* @param a - First byte array
|
|
6408
|
-
* @param b - Second byte array
|
|
6409
|
-
* @returns `true` when the inputs are byte-for-byte identical
|
|
6410
|
-
*
|
|
6411
|
-
* @example
|
|
6412
|
-
* ```ts
|
|
6413
|
-
* const isEqual = timingSafeEqualBytes(
|
|
6414
|
-
* new TextEncoder().encode("abc"),
|
|
6415
|
-
* new TextEncoder().encode("abc"),
|
|
6416
|
-
* );
|
|
6417
|
-
* ```
|
|
6418
|
-
*/ function timingSafeEqualBytes(a, b) {
|
|
6419
|
-
if (a.byteLength !== b.byteLength) return false;
|
|
6420
|
-
let mismatch = 0;
|
|
6421
|
-
for (let index = 0; index < a.byteLength; index += 1) mismatch |= (a[index] ?? 0) ^ (b[index] ?? 0);
|
|
6422
|
-
return mismatch === 0;
|
|
6423
|
-
}
|
|
6424
|
-
/**
|
|
6425
|
-
* Compare two strings using a constant-time byte comparison.
|
|
6426
|
-
*
|
|
6427
|
-
* @param a - First string
|
|
6428
|
-
* @param b - Second string
|
|
6429
|
-
* @returns `true` when the UTF-8 byte sequences are identical
|
|
6430
|
-
*
|
|
6431
|
-
* @example
|
|
6432
|
-
* ```ts
|
|
6433
|
-
* const isEqual = timingSafeEqualText("token-a", "token-b");
|
|
6434
|
-
* ```
|
|
6435
|
-
*/ function timingSafeEqualText(a, b) {
|
|
6436
|
-
const encoder = new TextEncoder();
|
|
6437
|
-
return timingSafeEqualBytes(encoder.encode(a), encoder.encode(b));
|
|
6438
|
-
}
|
|
6439
|
-
//#endregion
|
|
6440
6400
|
//#region src/lib/hosted-domain-check.ts
|
|
6401
|
+
var VERIFICATION_HMAC_VERSION = "v1";
|
|
6441
6402
|
var textEncoder$3 = new TextEncoder();
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
6403
|
+
function bytesToHex$1(bytes) {
|
|
6404
|
+
let out = "";
|
|
6405
|
+
for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
|
|
6406
|
+
return out;
|
|
6447
6407
|
}
|
|
6448
|
-
|
|
6408
|
+
/**
|
|
6409
|
+
* Compute the plaintext HMAC token returned from
|
|
6410
|
+
* `/.well-known/jant-verification`.
|
|
6411
|
+
*
|
|
6412
|
+
* The control plane sends a nonce in the query string; the site replies with
|
|
6413
|
+
* `jant-verification=<hex>` where `<hex>` is `HMAC-SHA256(secret, payload)`
|
|
6414
|
+
* over `payload = "v1:" + host + ":" + nonce`. The shared secret is
|
|
6415
|
+
* `HOSTED_CONTROL_PLANE_DOMAIN_CHECK_SECRET`.
|
|
6416
|
+
*/ async function computeHostedVerificationToken(secret, host, nonce) {
|
|
6449
6417
|
const key = await crypto.subtle.importKey("raw", textEncoder$3.encode(secret), {
|
|
6450
6418
|
name: "HMAC",
|
|
6451
6419
|
hash: "SHA-256"
|
|
6452
6420
|
}, false, ["sign"]);
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
async function signHostedDomainCheckToken(secret, claims) {
|
|
6456
|
-
const payload = toBase64Url$1(textEncoder$3.encode(JSON.stringify(claims)));
|
|
6457
|
-
return `${payload}.${toBase64Url$1(await createHmacSignature$1(secret, payload))}`;
|
|
6421
|
+
const payload = `${VERIFICATION_HMAC_VERSION}:${host.trim().toLowerCase()}:${nonce}`;
|
|
6422
|
+
return bytesToHex$1(new Uint8Array(await crypto.subtle.sign("HMAC", key, textEncoder$3.encode(payload))));
|
|
6458
6423
|
}
|
|
6459
6424
|
//#endregion
|
|
6460
6425
|
//#region src/routes/hosted/domain-check.ts
|
|
6461
6426
|
var hostedDomainCheckRoutes = new Hono();
|
|
6462
|
-
hostedDomainCheckRoutes.get("/.well-known/jant-
|
|
6427
|
+
hostedDomainCheckRoutes.get("/.well-known/jant-verification", async (c) => {
|
|
6463
6428
|
const secret = getHostedControlPlaneDomainCheckSecret(c.env);
|
|
6464
|
-
if (!secret) throw new NotFoundError("Hosted domain
|
|
6465
|
-
if (!c.var.currentSiteDomain) throw new NotFoundError("Hosted domain
|
|
6429
|
+
if (!secret) throw new NotFoundError("Hosted domain verification endpoint");
|
|
6430
|
+
if (!c.var.currentSiteDomain) throw new NotFoundError("Hosted domain verification endpoint");
|
|
6466
6431
|
const nonce = c.req.query("nonce")?.trim();
|
|
6467
|
-
if (!nonce) return c.
|
|
6468
|
-
const token = await
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
iss: "jant-core",
|
|
6474
|
-
nonce
|
|
6475
|
-
});
|
|
6476
|
-
return c.json({ token }, 200, { "Cache-Control": "no-store" });
|
|
6432
|
+
if (!nonce) return c.text("Missing nonce.", 400);
|
|
6433
|
+
const token = await computeHostedVerificationToken(secret, c.var.currentSiteDomain.host.trim().toLowerCase(), nonce);
|
|
6434
|
+
return c.text(`jant-verification=${token}\n`, 200, {
|
|
6435
|
+
"Cache-Control": "no-store",
|
|
6436
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
6437
|
+
});
|
|
6477
6438
|
});
|
|
6478
6439
|
//#endregion
|
|
6479
6440
|
//#region src/lib/collection-paths.ts
|
|
@@ -9935,7 +9896,7 @@ function getThreadPreviewState({ secondReply, penultimateReply, latestReply, tot
|
|
|
9935
9896
|
/**
|
|
9936
9897
|
* Thread Preview
|
|
9937
9898
|
*
|
|
9938
|
-
* Shows latest reply as the hero post with
|
|
9899
|
+
* Shows latest reply as the hero post with ancestor context above.
|
|
9939
9900
|
* Thread line connects all posts via `.thread-group` / `.thread-item`.
|
|
9940
9901
|
*/ var ROOT_CONTEXT_DISPLAY = { footer: { hideReply: true } };
|
|
9941
9902
|
var CONTEXT_DISPLAY = {
|
|
@@ -9945,8 +9906,6 @@ var CONTEXT_DISPLAY = {
|
|
|
9945
9906
|
var HERO_DISPLAY = {};
|
|
9946
9907
|
var ThreadPreview = ({ rootPost, secondReply, penultimateReply, latestReply, totalReplyCount }) => {
|
|
9947
9908
|
const { i18n } = useLingui();
|
|
9948
|
-
const showMoreLabel = i18n._({ id: "fMPkxb" });
|
|
9949
|
-
const showLessLabel = i18n._({ id: "6lGV3K" });
|
|
9950
9909
|
const { hiddenCount } = getThreadPreviewState({
|
|
9951
9910
|
secondReply,
|
|
9952
9911
|
penultimateReply,
|
|
@@ -9956,56 +9915,41 @@ var ThreadPreview = ({ rootPost, secondReply, penultimateReply, latestReply, tot
|
|
|
9956
9915
|
const hiddenPostsLabel = i18n._({ id: "oO0hKx" }, { count: hiddenCount });
|
|
9957
9916
|
const renderedSecondReply = secondReply && secondReply.id !== latestReply.id ? secondReply : void 0;
|
|
9958
9917
|
const renderedPenultimateReply = penultimateReply && penultimateReply.id !== latestReply.id && penultimateReply.id !== secondReply?.id ? penultimateReply : void 0;
|
|
9918
|
+
const gapHref = renderedSecondReply?.permalink ?? latestReply.permalink;
|
|
9959
9919
|
return /* @__PURE__ */ jsxDEV$1("div", {
|
|
9960
9920
|
class: "thread-group thread-group-preview",
|
|
9961
9921
|
children: [
|
|
9962
9922
|
/* @__PURE__ */ jsxDEV$1("div", {
|
|
9963
|
-
class: "thread-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
post: rootPost,
|
|
9970
|
-
mode: "feed",
|
|
9971
|
-
display: ROOT_CONTEXT_DISPLAY
|
|
9972
|
-
})
|
|
9973
|
-
}),
|
|
9974
|
-
renderedSecondReply && /* @__PURE__ */ jsxDEV$1("div", {
|
|
9975
|
-
class: "thread-item thread-item-context",
|
|
9976
|
-
children: /* @__PURE__ */ jsxDEV$1(TimelineItemFromPost, {
|
|
9977
|
-
post: renderedSecondReply,
|
|
9978
|
-
mode: "feed",
|
|
9979
|
-
display: CONTEXT_DISPLAY
|
|
9980
|
-
})
|
|
9981
|
-
}),
|
|
9982
|
-
hiddenCount > 0 && /* @__PURE__ */ jsxDEV$1("div", {
|
|
9983
|
-
class: "thread-item thread-item-gap",
|
|
9984
|
-
children: /* @__PURE__ */ jsxDEV$1("a", {
|
|
9985
|
-
href: latestReply.permalink,
|
|
9986
|
-
class: "thread-gap-link",
|
|
9987
|
-
children: hiddenPostsLabel
|
|
9988
|
-
})
|
|
9989
|
-
}),
|
|
9990
|
-
renderedPenultimateReply && /* @__PURE__ */ jsxDEV$1("div", {
|
|
9991
|
-
class: "thread-item thread-item-context",
|
|
9992
|
-
children: /* @__PURE__ */ jsxDEV$1(TimelineItemFromPost, {
|
|
9993
|
-
post: renderedPenultimateReply,
|
|
9994
|
-
mode: "feed",
|
|
9995
|
-
display: CONTEXT_DISPLAY
|
|
9996
|
-
})
|
|
9997
|
-
}),
|
|
9998
|
-
/* @__PURE__ */ jsxDEV$1("div", { class: "thread-context-fade" })
|
|
9999
|
-
]
|
|
9923
|
+
class: "thread-item thread-item-context",
|
|
9924
|
+
children: /* @__PURE__ */ jsxDEV$1(TimelineItemFromPost, {
|
|
9925
|
+
post: rootPost,
|
|
9926
|
+
mode: "feed",
|
|
9927
|
+
display: ROOT_CONTEXT_DISPLAY
|
|
9928
|
+
})
|
|
10000
9929
|
}),
|
|
10001
|
-
/* @__PURE__ */ jsxDEV$1("
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
9930
|
+
renderedSecondReply && /* @__PURE__ */ jsxDEV$1("div", {
|
|
9931
|
+
class: "thread-item thread-item-context",
|
|
9932
|
+
children: /* @__PURE__ */ jsxDEV$1(TimelineItemFromPost, {
|
|
9933
|
+
post: renderedSecondReply,
|
|
9934
|
+
mode: "feed",
|
|
9935
|
+
display: CONTEXT_DISPLAY
|
|
9936
|
+
})
|
|
9937
|
+
}),
|
|
9938
|
+
hiddenCount > 0 && /* @__PURE__ */ jsxDEV$1("div", {
|
|
9939
|
+
class: "thread-item thread-item-gap",
|
|
9940
|
+
children: /* @__PURE__ */ jsxDEV$1("a", {
|
|
9941
|
+
href: gapHref,
|
|
9942
|
+
class: "thread-gap-link",
|
|
9943
|
+
children: hiddenPostsLabel
|
|
9944
|
+
})
|
|
9945
|
+
}),
|
|
9946
|
+
renderedPenultimateReply && /* @__PURE__ */ jsxDEV$1("div", {
|
|
9947
|
+
class: "thread-item thread-item-context",
|
|
9948
|
+
children: /* @__PURE__ */ jsxDEV$1(TimelineItemFromPost, {
|
|
9949
|
+
post: renderedPenultimateReply,
|
|
9950
|
+
mode: "feed",
|
|
9951
|
+
display: CONTEXT_DISPLAY
|
|
9952
|
+
})
|
|
10009
9953
|
}),
|
|
10010
9954
|
/* @__PURE__ */ jsxDEV$1("div", {
|
|
10011
9955
|
class: "thread-item thread-item-hero",
|
|
@@ -20233,7 +20177,7 @@ async function syncHostedControlPlaneSiteAvatar(input) {
|
|
|
20233
20177
|
return;
|
|
20234
20178
|
}
|
|
20235
20179
|
await markSyncPending(settings);
|
|
20236
|
-
const { createGitHubSyncService } = await import("./github-sync-
|
|
20180
|
+
const { createGitHubSyncService } = await import("./github-sync-D2FO19Re.js");
|
|
20237
20181
|
const { getGitHubAppConfig } = await import("./env-CgaH9Mut.js").then((n) => n.t);
|
|
20238
20182
|
const run = runBackgroundSync(settings, createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
|
|
20239
20183
|
storage: c.var.storage,
|
|
@@ -21020,7 +20964,7 @@ settingsRoutes.post("/github-sync/connect", async (c) => {
|
|
|
21020
20964
|
await c.var.services.settings.set("GITHUB_SYNC_AUTH_MODE", "pat");
|
|
21021
20965
|
await c.var.services.settings.set("GITHUB_SYNC_APP_INSTALLATION_ID", "");
|
|
21022
20966
|
await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
|
|
21023
|
-
const { createGitHubSyncService } = await import("./github-sync-
|
|
20967
|
+
const { createGitHubSyncService } = await import("./github-sync-D2FO19Re.js");
|
|
21024
20968
|
const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
|
|
21025
20969
|
storage: c.var.storage,
|
|
21026
20970
|
githubApp: getGitHubAppConfig(c.env)
|
|
@@ -21039,7 +20983,7 @@ settingsRoutes.post("/github-sync/connect", async (c) => {
|
|
|
21039
20983
|
return dsRedirect(publicPath(c, "/settings/github-sync"));
|
|
21040
20984
|
});
|
|
21041
20985
|
settingsRoutes.post("/github-sync/push", async (c) => {
|
|
21042
|
-
const { createGitHubSyncService } = await import("./github-sync-
|
|
20986
|
+
const { createGitHubSyncService } = await import("./github-sync-D2FO19Re.js");
|
|
21043
20987
|
const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
|
|
21044
20988
|
storage: c.var.storage,
|
|
21045
20989
|
githubApp: getGitHubAppConfig(c.env)
|
|
@@ -21059,7 +21003,7 @@ settingsRoutes.post("/github-sync/push", async (c) => {
|
|
|
21059
21003
|
});
|
|
21060
21004
|
});
|
|
21061
21005
|
settingsRoutes.post("/github-sync/disconnect", async (c) => {
|
|
21062
|
-
const { createGitHubSyncService } = await import("./github-sync-
|
|
21006
|
+
const { createGitHubSyncService } = await import("./github-sync-D2FO19Re.js");
|
|
21063
21007
|
await createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), { githubApp: getGitHubAppConfig(c.env) }).teardownWebhook();
|
|
21064
21008
|
return dsRedirect(publicPath(c, "/settings/github-sync"));
|
|
21065
21009
|
});
|
|
@@ -21250,7 +21194,7 @@ function buildRepoPickerLabels(c) {
|
|
|
21250
21194
|
const { parseRepoSlug, createGitHubClient } = await import("./github-api-Bh0PH3zr.js").then((n) => n.n);
|
|
21251
21195
|
const parsed = parseRepoSlug(repo);
|
|
21252
21196
|
if (!parsed) return wantsJson ? c.json({ error: "Invalid repository format." }, 400) : c.text("Invalid repository format.", 400);
|
|
21253
|
-
const { classifyRepoForSync } = await import("./github-sync-
|
|
21197
|
+
const { classifyRepoForSync } = await import("./github-sync-D2FO19Re.js");
|
|
21254
21198
|
const ghClient = createGitHubClient(() => getInstallationTokenFromApp(app, installationId));
|
|
21255
21199
|
let classification;
|
|
21256
21200
|
try {
|
|
@@ -21280,7 +21224,7 @@ function buildRepoPickerLabels(c) {
|
|
|
21280
21224
|
await c.var.services.settings.set("GITHUB_SYNC_REPO", repo);
|
|
21281
21225
|
await c.var.services.settings.set("GITHUB_SYNC_TOKEN", "");
|
|
21282
21226
|
await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
|
|
21283
|
-
const { createGitHubSyncService } = await import("./github-sync-
|
|
21227
|
+
const { createGitHubSyncService } = await import("./github-sync-D2FO19Re.js");
|
|
21284
21228
|
const syncService = createGitHubSyncService(c.var.services, c.var.currentSite.id, await buildSyncSiteConfig(c), {
|
|
21285
21229
|
storage: c.var.storage,
|
|
21286
21230
|
githubApp: app
|
|
@@ -21396,7 +21340,7 @@ function requireGitHubApp(c) {
|
|
|
21396
21340
|
const { parseRepoSlug, createGitHubClient } = await import("./github-api-Bh0PH3zr.js").then((n) => n.n);
|
|
21397
21341
|
const parsed = parseRepoSlug(repo);
|
|
21398
21342
|
if (!parsed) return c.json({ error: "Invalid repository format." }, 400);
|
|
21399
|
-
const { classifyRepoForSync } = await import("./github-sync-
|
|
21343
|
+
const { classifyRepoForSync } = await import("./github-sync-D2FO19Re.js");
|
|
21400
21344
|
const client = createGitHubClient(() => getInstallationTokenFromApp(app, installationId));
|
|
21401
21345
|
try {
|
|
21402
21346
|
const classification = await classifyRepoForSync(client, parsed.owner, parsed.repo, c.var.currentSite.id);
|
|
@@ -24752,8 +24696,10 @@ internalApiTokensRoutes.post("/purge", requireInternalAdminApi(), async (c) => {
|
|
|
24752
24696
|
});
|
|
24753
24697
|
//#endregion
|
|
24754
24698
|
//#region src/routes/api/internal/sites.ts
|
|
24699
|
+
var ManagedSiteKeySchema = z.string().trim().toLowerCase().min(3).max(40).regex(/^[a-z0-9](?:[a-z0-9-]{1,38}[a-z0-9])?$/, "Site key must use lowercase letters, numbers, or hyphens.");
|
|
24700
|
+
var SiteKeyAvailabilityQuerySchema = z.object({ key: ManagedSiteKeySchema });
|
|
24755
24701
|
var CreateManagedSiteSchema = z.object({
|
|
24756
|
-
key:
|
|
24702
|
+
key: ManagedSiteKeySchema,
|
|
24757
24703
|
primaryHost: z.string().trim().toLowerCase().min(1).max(255).regex(/^(?=.{1,255}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/, "Primary host must be a valid hostname."),
|
|
24758
24704
|
siteName: z.string().trim().min(1).max(120),
|
|
24759
24705
|
siteLanguage: z.string().trim().max(35).optional(),
|
|
@@ -24778,6 +24724,12 @@ internalSitesRoutes.post("/", requireInternalAdminApi(), async (c) => {
|
|
|
24778
24724
|
status: result.site.status
|
|
24779
24725
|
}, 201);
|
|
24780
24726
|
});
|
|
24727
|
+
internalSitesRoutes.get("/availability", requireInternalAdminApi(), async (c) => {
|
|
24728
|
+
assertHostBasedMode(c.env);
|
|
24729
|
+
const query = parseValidated(SiteKeyAvailabilityQuerySchema, { key: c.req.query("key") ?? "" });
|
|
24730
|
+
const result = await c.var.services.siteAdmin.isManagedSiteKeyAvailable(query.key);
|
|
24731
|
+
return c.json(result);
|
|
24732
|
+
});
|
|
24781
24733
|
internalSitesRoutes.delete("/:siteId", requireInternalAdminApi(), async (c) => {
|
|
24782
24734
|
assertHostBasedMode(c.env);
|
|
24783
24735
|
await c.var.services.siteAdmin.deleteManagedSite(c.req.param("siteId"), { storage: c.var.storage });
|
|
@@ -24847,6 +24799,18 @@ internalSitesRoutes.post("/:siteId/domains/:domainId/primary", requireInternalAd
|
|
|
24847
24799
|
redirectToPrimary: domain.redirectToPrimary
|
|
24848
24800
|
})) });
|
|
24849
24801
|
});
|
|
24802
|
+
var ManagedSiteDomainRedirectSchema = z.object({ redirectToPrimary: z.boolean() });
|
|
24803
|
+
internalSitesRoutes.post("/:siteId/domains/:domainId/redirect", requireInternalAdminApi(), async (c) => {
|
|
24804
|
+
assertHostBasedMode(c.env);
|
|
24805
|
+
const body = parseValidated(ManagedSiteDomainRedirectSchema, await c.req.json());
|
|
24806
|
+
const domains = await c.var.services.siteAdmin.setManagedSiteDomainRedirect(c.req.param("siteId"), c.req.param("domainId"), body.redirectToPrimary);
|
|
24807
|
+
return c.json({ domains: domains.map((domain) => ({
|
|
24808
|
+
host: domain.host,
|
|
24809
|
+
id: domain.id,
|
|
24810
|
+
kind: domain.kind,
|
|
24811
|
+
redirectToPrimary: domain.redirectToPrimary
|
|
24812
|
+
})) });
|
|
24813
|
+
});
|
|
24850
24814
|
internalSitesRoutes.delete("/:siteId/domains/:domainId", requireInternalAdminApi(), async (c) => {
|
|
24851
24815
|
assertHostBasedMode(c.env);
|
|
24852
24816
|
const domains = await c.var.services.siteAdmin.deleteManagedSiteDomain(c.req.param("siteId"), c.req.param("domainId"));
|
|
@@ -26616,7 +26580,7 @@ var cors = (options) => {
|
|
|
26616
26580
|
//#endregion
|
|
26617
26581
|
//#region src/lib/hosted-domain.ts
|
|
26618
26582
|
var CANONICAL_REDIRECT_BYPASS_PREFIXES = [
|
|
26619
|
-
"/.well-known/jant-
|
|
26583
|
+
"/.well-known/jant-verification",
|
|
26620
26584
|
"/__dev",
|
|
26621
26585
|
"/__sso",
|
|
26622
26586
|
"/api/",
|
|
@@ -26963,6 +26927,46 @@ function createD1RateLimiter(db, schema, now = () => Math.floor(Date.now() / 1e3
|
|
|
26963
26927
|
} };
|
|
26964
26928
|
}
|
|
26965
26929
|
//#endregion
|
|
26930
|
+
//#region src/lib/crypto.ts
|
|
26931
|
+
/**
|
|
26932
|
+
* Compare two byte arrays using a constant-time loop.
|
|
26933
|
+
*
|
|
26934
|
+
* This avoids relying on runtime-specific crypto helpers so the same behavior
|
|
26935
|
+
* works in Node.js and Workers.
|
|
26936
|
+
*
|
|
26937
|
+
* @param a - First byte array
|
|
26938
|
+
* @param b - Second byte array
|
|
26939
|
+
* @returns `true` when the inputs are byte-for-byte identical
|
|
26940
|
+
*
|
|
26941
|
+
* @example
|
|
26942
|
+
* ```ts
|
|
26943
|
+
* const isEqual = timingSafeEqualBytes(
|
|
26944
|
+
* new TextEncoder().encode("abc"),
|
|
26945
|
+
* new TextEncoder().encode("abc"),
|
|
26946
|
+
* );
|
|
26947
|
+
* ```
|
|
26948
|
+
*/ function timingSafeEqualBytes(a, b) {
|
|
26949
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
26950
|
+
let mismatch = 0;
|
|
26951
|
+
for (let index = 0; index < a.byteLength; index += 1) mismatch |= (a[index] ?? 0) ^ (b[index] ?? 0);
|
|
26952
|
+
return mismatch === 0;
|
|
26953
|
+
}
|
|
26954
|
+
/**
|
|
26955
|
+
* Compare two strings using a constant-time byte comparison.
|
|
26956
|
+
*
|
|
26957
|
+
* @param a - First string
|
|
26958
|
+
* @param b - Second string
|
|
26959
|
+
* @returns `true` when the UTF-8 byte sequences are identical
|
|
26960
|
+
*
|
|
26961
|
+
* @example
|
|
26962
|
+
* ```ts
|
|
26963
|
+
* const isEqual = timingSafeEqualText("token-a", "token-b");
|
|
26964
|
+
* ```
|
|
26965
|
+
*/ function timingSafeEqualText(a, b) {
|
|
26966
|
+
const encoder = new TextEncoder();
|
|
26967
|
+
return timingSafeEqualBytes(encoder.encode(a), encoder.encode(b));
|
|
26968
|
+
}
|
|
26969
|
+
//#endregion
|
|
26966
26970
|
//#region src/lib/hosted-sso.ts
|
|
26967
26971
|
var textEncoder = new TextEncoder();
|
|
26968
26972
|
var textDecoder = new TextDecoder();
|
|
@@ -30755,6 +30759,14 @@ function createSiteAdminService(db, databaseSchema = sqliteSchemaBundle, databas
|
|
|
30755
30759
|
if (supportsDrizzleTransaction(db, databaseDialect)) return db.transaction(async (tx) => createWithDatabase(tx, input));
|
|
30756
30760
|
return createWithDatabase(db, input);
|
|
30757
30761
|
},
|
|
30762
|
+
async isManagedSiteKeyAvailable(key) {
|
|
30763
|
+
assertManagedSiteOperationsEnabled();
|
|
30764
|
+
const normalizedKey = key.trim();
|
|
30765
|
+
return {
|
|
30766
|
+
available: !(await db.select({ id: sites.id }).from(sites).where(eq(sites.key, normalizedKey)).limit(1))[0],
|
|
30767
|
+
key: normalizedKey
|
|
30768
|
+
};
|
|
30769
|
+
},
|
|
30758
30770
|
async getManagedSiteMediaUsage(siteId) {
|
|
30759
30771
|
assertManagedSiteOperationsEnabled();
|
|
30760
30772
|
return getManagedSiteMediaUsage(siteId);
|
|
@@ -30785,7 +30797,7 @@ function createSiteAdminService(db, databaseSchema = sqliteSchemaBundle, databas
|
|
|
30785
30797
|
const themeCss = buildThemeStyle(activeTheme, appConfig.themeMode, fontOverrides);
|
|
30786
30798
|
const navItemList = await navItems.list();
|
|
30787
30799
|
const appleTouchKey = allSettings[SETTINGS_KEYS.SITE_FAVICON_APPLE_TOUCH];
|
|
30788
|
-
const { createExportService } = await import("./export-
|
|
30800
|
+
const { createExportService } = await import("./export-DwH3ga3Y.js").then((n) => n.n);
|
|
30789
30801
|
const exportService = createExportService({
|
|
30790
30802
|
collections,
|
|
30791
30803
|
media: mediaService,
|
|
@@ -30877,7 +30889,7 @@ function createSiteAdminService(db, databaseSchema = sqliteSchemaBundle, databas
|
|
|
30877
30889
|
const timestamp = now();
|
|
30878
30890
|
if (input.makePrimary) await targetDb.update(siteDomains).set({
|
|
30879
30891
|
kind: "alias",
|
|
30880
|
-
redirectToPrimary:
|
|
30892
|
+
redirectToPrimary: false,
|
|
30881
30893
|
updatedAt: timestamp
|
|
30882
30894
|
}).where(eq(siteDomains.siteId, normalizedSiteId));
|
|
30883
30895
|
await targetDb.insert(siteDomains).values({
|
|
@@ -30892,6 +30904,19 @@ function createSiteAdminService(db, databaseSchema = sqliteSchemaBundle, databas
|
|
|
30892
30904
|
});
|
|
30893
30905
|
});
|
|
30894
30906
|
},
|
|
30907
|
+
async setManagedSiteDomainRedirect(siteId, domainId, redirectToPrimary) {
|
|
30908
|
+
assertManagedSiteOperationsEnabled();
|
|
30909
|
+
return mutateSiteDomains(siteId, async (targetDb, normalizedSiteId) => {
|
|
30910
|
+
await requireSite(targetDb, normalizedSiteId);
|
|
30911
|
+
const normalizedDomainId = domainId.trim();
|
|
30912
|
+
if (!(await targetDb.select({ id: siteDomains.id }).from(siteDomains).where(sql`${siteDomains.id} = ${normalizedDomainId} AND ${siteDomains.siteId} = ${normalizedSiteId}`).limit(1))[0]) throw new NotFoundError("Site domain");
|
|
30913
|
+
const timestamp = now();
|
|
30914
|
+
await targetDb.update(siteDomains).set({
|
|
30915
|
+
redirectToPrimary,
|
|
30916
|
+
updatedAt: timestamp
|
|
30917
|
+
}).where(eq(siteDomains.id, normalizedDomainId));
|
|
30918
|
+
});
|
|
30919
|
+
},
|
|
30895
30920
|
async setManagedSitePrimaryDomain(siteId, domainId) {
|
|
30896
30921
|
assertManagedSiteOperationsEnabled();
|
|
30897
30922
|
return mutateSiteDomains(siteId, async (targetDb, normalizedSiteId) => {
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"name": "url"
|
|
147
147
|
},
|
|
148
148
|
"src/client-auth.ts": {
|
|
149
|
-
"file": "_assets/client-auth-
|
|
149
|
+
"file": "_assets/client-auth-N6fiJcOg.js",
|
|
150
150
|
"name": "client-auth",
|
|
151
151
|
"src": "src/client-auth.ts",
|
|
152
152
|
"isEntry": true,
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
]
|
|
164
164
|
},
|
|
165
165
|
"src/client.ts": {
|
|
166
|
-
"file": "_assets/client-
|
|
166
|
+
"file": "_assets/client-DqsPJKiP.js",
|
|
167
167
|
"name": "client",
|
|
168
168
|
"src": "src/client.ts",
|
|
169
169
|
"isEntry": true,
|
|
@@ -858,7 +858,7 @@
|
|
|
858
858
|
]
|
|
859
859
|
},
|
|
860
860
|
"src/style.css": {
|
|
861
|
-
"file": "_assets/client-
|
|
861
|
+
"file": "_assets/client-BbJ0FhON.css",
|
|
862
862
|
"name": "style",
|
|
863
863
|
"names": [
|
|
864
864
|
"style.css"
|