@ecency/render-helper 2.5.25 → 2.5.26

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 CHANGED
@@ -19,7 +19,7 @@ Built and maintained by [Ecency](https://ecency.com), an open-source social plat
19
19
  - `pnpm publish:render-helper` - Manually publish to npm
20
20
 
21
21
  ### CI/CD
22
- - Part of vision-next monorepo with centralized GitHub Actions (`.github/workflows/packages.yml`)
22
+ - Part of vision-web monorepo with centralized GitHub Actions (`.github/workflows/packages.yml`)
23
23
  - Automatically detects changes to `packages/render-helper/**` using git diff
24
24
  - On push to `develop` branch, builds and publishes to npm
25
25
  - Uses OIDC (OpenID Connect) for secure npm authentication
@@ -128,7 +128,7 @@ src/
128
128
 
129
129
  ## Build System
130
130
 
131
- - Uses **tsup** for bundling (shared with other vision-next packages)
131
+ - Uses **tsup** for bundling (shared with other vision-web packages)
132
132
  - TypeScript 5.6+ with strict mode enabled
133
133
  - Outputs three bundles:
134
134
  - `dist/browser/index.js` - Browser ESM with TypeScript declarations
@@ -173,6 +173,6 @@ const summary = postBodySummary(entry, 200, 'react-native')
173
173
  - When adding embed support for new platforms, implement server-side rendering only
174
174
  - All external content must go through sanitization
175
175
  - Version bumps required for auto-publish via CI
176
- - Part of vision-next monorepo - changes require updating across dependent packages
176
+ - Part of vision-web monorepo - changes require updating across dependent packages
177
177
  - The `@ecency/renderer` package uses this as a workspace dependency
178
178
  - Exports are optimized for tree-shaking with proper ESM structure
@@ -22,6 +22,22 @@ interface RenderOptions {
22
22
  * existing chip styling still applies.
23
23
  */
24
24
  inertAuthorAndTagChips?: boolean;
25
+ /**
26
+ * Absolute origin for links a consumer has no route to serve itself, e.g.
27
+ * `https://ecency.com`. Only affects the non-app render path.
28
+ *
29
+ * This is for PROFILE SECTION links, `/@user/wallet`, `/@user/followers` and
30
+ * the rest of SECTION_LIST. They are emitted as ordinary links, so a
31
+ * consumer whose only matching route is `/:author/:permlink` routes them as a
32
+ * post and tries to load one whose permlink is `wallet`. That is not a dead
33
+ * route, so no route guard catches it, and it is not a chip, so
34
+ * `inertAuthorAndTagChips` does not either.
35
+ *
36
+ * Post links themselves are deliberately NOT rewritten: `/@author/permlink`
37
+ * is real content the consumer can resolve from the chain, and keeping it
38
+ * internal is the point of a self-hosted blog.
39
+ */
40
+ externalProfileBase?: string;
25
41
  }
26
42
 
27
43
  /**
@@ -1069,9 +1069,9 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
1069
1069
  if (el.textContent === href) {
1070
1070
  el.textContent = `@${author}/${section}`;
1071
1071
  }
1072
- if (forApp) {
1073
- const ha = `https://ecency.com/@${author}/${section}`;
1074
- el.setAttribute("href", ha);
1072
+ const external1 = renderOptions?.externalProfileBase;
1073
+ if (forApp || external1) {
1074
+ el.setAttribute("href", `${external1 ?? "https://ecency.com"}/@${author}/${section}`);
1075
1075
  } else {
1076
1076
  const h = `/@${author}/${section}`;
1077
1077
  el.setAttribute("href", h);
@@ -1139,9 +1139,9 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
1139
1139
  if (el.textContent === href) {
1140
1140
  el.textContent = `@${author}/${section}`;
1141
1141
  }
1142
- if (forApp) {
1143
- const ha = `https://ecency.com/@${author}/${section}`;
1144
- el.setAttribute("href", ha);
1142
+ const external2 = renderOptions?.externalProfileBase;
1143
+ if (forApp || external2) {
1144
+ el.setAttribute("href", `${external2 ?? "https://ecency.com"}/@${author}/${section}`);
1145
1145
  } else {
1146
1146
  const h = `/@${author}/${section}`;
1147
1147
  el.setAttribute("href", h);
@@ -1799,7 +1799,8 @@ function linkify(content, forApp, renderOptions) {
1799
1799
  const permlink = sanitizePermlink(p3);
1800
1800
  if (!isValidPermlink(permlink)) return match;
1801
1801
  if (SECTION_LIST.includes(permlink)) {
1802
- const attrs = forApp ? `href="https://ecency.com/@${authorLower}/${permlink}"` : `href="/@${authorLower}/${permlink}"`;
1802
+ const external = renderOptions?.externalProfileBase;
1803
+ const attrs = forApp || external ? `href="${external ?? "https://ecency.com"}/@${authorLower}/${permlink}"` : `href="/@${authorLower}/${permlink}"`;
1803
1804
  return `${preceding}<a class="markdown-profile-link" ${attrs}>@${authorLower}/${permlink}</a>`;
1804
1805
  } else {
1805
1806
  const attrs = forApp ? `data-author="${authorLower}" data-tag="${tag}" data-permlink="${permlink}"` : `href="/@${authorLower}/${permlink}"`;
@@ -1814,7 +1815,8 @@ function linkify(content, forApp, renderOptions) {
1814
1815
  const permlink = sanitizePermlink(p3);
1815
1816
  if (!isValidPermlink(permlink)) return match;
1816
1817
  if (SECTION_LIST.includes(permlink)) {
1817
- const attrs = forApp ? `href="https://ecency.com/@${uu}/${permlink}"` : `href="/@${uu}/${permlink}"`;
1818
+ const external = renderOptions?.externalProfileBase;
1819
+ const attrs = forApp || external ? `href="${external ?? "https://ecency.com"}/@${uu}/${permlink}"` : `href="/@${uu}/${permlink}"`;
1818
1820
  return ` <a class="markdown-profile-link" ${attrs}>@${uu}/${permlink}</a>`;
1819
1821
  } else {
1820
1822
  const attrs = forApp ? `data-author="${uu}" data-tag="post" data-permlink="${permlink}"` : `href="/@${uu}/${permlink}"`;
@@ -2161,7 +2163,7 @@ function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency
2161
2163
  logIfSlow(performance.now() - t02, `body_len=${obj.length}`);
2162
2164
  return res2;
2163
2165
  }
2164
- const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}${renderOptions?.embedVideosDirectly ? "-embed" : ""}${renderOptions?.inertAuthorAndTagChips ? "-inert" : ""}`;
2166
+ const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}${renderOptions?.embedVideosDirectly ? "-embed" : ""}${renderOptions?.inertAuthorAndTagChips ? "-inert" : ""}${renderOptions?.externalProfileBase ? "-ext" + renderOptions.externalProfileBase : ""}`;
2165
2167
  const item = cacheGet(key);
2166
2168
  if (item) {
2167
2169
  return item;