@ecency/render-helper 2.3.16 → 2.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.
Files changed (155) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +171 -17
  3. package/dist/browser/index.d.ts +32 -0
  4. package/dist/browser/index.js +1556 -0
  5. package/dist/browser/index.js.map +1 -0
  6. package/dist/node/index.cjs +1575 -0
  7. package/dist/node/index.cjs.map +1 -0
  8. package/dist/node/index.mjs +1559 -0
  9. package/dist/node/index.mjs.map +1 -0
  10. package/package.json +49 -47
  11. package/lib/cache.d.ts +0 -3
  12. package/lib/cache.js +0 -21
  13. package/lib/cache.js.map +0 -1
  14. package/lib/catch-post-image.d.ts +0 -2
  15. package/lib/catch-post-image.js +0 -76
  16. package/lib/catch-post-image.js.map +0 -1
  17. package/lib/consts/allowed-attributes.const.d.ts +0 -2
  18. package/lib/consts/allowed-attributes.const.js +0 -71
  19. package/lib/consts/allowed-attributes.const.js.map +0 -1
  20. package/lib/consts/dom-parser.const.d.ts +0 -2
  21. package/lib/consts/dom-parser.const.js +0 -12
  22. package/lib/consts/dom-parser.const.js.map +0 -1
  23. package/lib/consts/index.d.ts +0 -5
  24. package/lib/consts/index.js +0 -22
  25. package/lib/consts/index.js.map +0 -1
  26. package/lib/consts/regexes.const.d.ts +0 -44
  27. package/lib/consts/regexes.const.js +0 -49
  28. package/lib/consts/regexes.const.js.map +0 -1
  29. package/lib/consts/section-list.const.d.ts +0 -1
  30. package/lib/consts/section-list.const.js +0 -24
  31. package/lib/consts/section-list.const.js.map +0 -1
  32. package/lib/consts/white-list.const.d.ts +0 -2
  33. package/lib/consts/white-list.const.js +0 -37
  34. package/lib/consts/white-list.const.js.map +0 -1
  35. package/lib/helper.d.ts +0 -5
  36. package/lib/helper.js +0 -60
  37. package/lib/helper.js.map +0 -1
  38. package/lib/index.d.ts +0 -8
  39. package/lib/index.js +0 -19
  40. package/lib/index.js.map +0 -1
  41. package/lib/markdown-2-html.d.ts +0 -2
  42. package/lib/markdown-2-html.js +0 -25
  43. package/lib/markdown-2-html.js.map +0 -1
  44. package/lib/methods/a.method.d.ts +0 -1
  45. package/lib/methods/a.method.js +0 -647
  46. package/lib/methods/a.method.js.map +0 -1
  47. package/lib/methods/clean-reply.method.d.ts +0 -1
  48. package/lib/methods/clean-reply.method.js +0 -37
  49. package/lib/methods/clean-reply.method.js.map +0 -1
  50. package/lib/methods/get-inner-html.method.d.ts +0 -1
  51. package/lib/methods/get-inner-html.method.js +0 -16
  52. package/lib/methods/get-inner-html.method.js.map +0 -1
  53. package/lib/methods/iframe.method.d.ts +0 -1
  54. package/lib/methods/iframe.method.js +0 -159
  55. package/lib/methods/iframe.method.js.map +0 -1
  56. package/lib/methods/img.method.d.ts +0 -4
  57. package/lib/methods/img.method.js +0 -50
  58. package/lib/methods/img.method.js.map +0 -1
  59. package/lib/methods/index.d.ts +0 -7
  60. package/lib/methods/index.js +0 -24
  61. package/lib/methods/index.js.map +0 -1
  62. package/lib/methods/linkify.method.d.ts +0 -1
  63. package/lib/methods/linkify.method.js +0 -56
  64. package/lib/methods/linkify.method.js.map +0 -1
  65. package/lib/methods/markdown-to-html.method.d.ts +0 -1
  66. package/lib/methods/markdown-to-html.method.js +0 -98
  67. package/lib/methods/markdown-to-html.method.js.map +0 -1
  68. package/lib/methods/noop.method.d.ts +0 -1
  69. package/lib/methods/noop.method.js +0 -6
  70. package/lib/methods/noop.method.js.map +0 -1
  71. package/lib/methods/p.method.d.ts +0 -1
  72. package/lib/methods/p.method.js +0 -11
  73. package/lib/methods/p.method.js.map +0 -1
  74. package/lib/methods/remove-child-nodes.method.d.ts +0 -1
  75. package/lib/methods/remove-child-nodes.method.js +0 -10
  76. package/lib/methods/remove-child-nodes.method.js.map +0 -1
  77. package/lib/methods/sanitize-html.method.d.ts +0 -1
  78. package/lib/methods/sanitize-html.method.js +0 -42
  79. package/lib/methods/sanitize-html.method.js.map +0 -1
  80. package/lib/methods/text.method.d.ts +0 -1
  81. package/lib/methods/text.method.js +0 -69
  82. package/lib/methods/text.method.js.map +0 -1
  83. package/lib/methods/traverse.method.d.ts +0 -3
  84. package/lib/methods/traverse.method.js +0 -38
  85. package/lib/methods/traverse.method.js.map +0 -1
  86. package/lib/post-body-summary.d.ts +0 -2
  87. package/lib/post-body-summary.js +0 -123
  88. package/lib/post-body-summary.js.map +0 -1
  89. package/lib/proxify-image-src.d.ts +0 -5
  90. package/lib/proxify-image-src.js +0 -86
  91. package/lib/proxify-image-src.js.map +0 -1
  92. package/lib/render-helper.js +0 -1
  93. package/lib/types/entry.interface.d.ts +0 -7
  94. package/lib/types/entry.interface.js +0 -3
  95. package/lib/types/entry.interface.js.map +0 -1
  96. package/lib/types/index.d.ts +0 -2
  97. package/lib/types/index.js +0 -19
  98. package/lib/types/index.js.map +0 -1
  99. package/lib/types/xss-white-list.interface.d.ts +0 -6
  100. package/lib/types/xss-white-list.interface.js +0 -3
  101. package/lib/types/xss-white-list.interface.js.map +0 -1
  102. package/src/cache.ts +0 -15
  103. package/src/catch-post-image.spec.ts +0 -144
  104. package/src/catch-post-image.ts +0 -78
  105. package/src/consts/allowed-attributes.const.ts +0 -69
  106. package/src/consts/dom-parser.const.ts +0 -6
  107. package/src/consts/index.ts +0 -5
  108. package/src/consts/regexes.const.ts +0 -46
  109. package/src/consts/section-list.const.ts +0 -20
  110. package/src/consts/white-list.const.ts +0 -33
  111. package/src/external-types/multihashes.d.ts +0 -3
  112. package/src/helper.spec.ts +0 -16
  113. package/src/helper.ts +0 -61
  114. package/src/index.ts +0 -18
  115. package/src/markdown-2-html.spec.ts +0 -1239
  116. package/src/markdown-2-html.ts +0 -25
  117. package/src/methods/a.method.ts +0 -791
  118. package/src/methods/clean-reply.method.ts +0 -32
  119. package/src/methods/get-inner-html.method.ts +0 -11
  120. package/src/methods/iframe.method.ts +0 -176
  121. package/src/methods/img.method.ts +0 -62
  122. package/src/methods/index.ts +0 -7
  123. package/src/methods/linkify.method.ts +0 -61
  124. package/src/methods/markdown-to-html.method.ts +0 -104
  125. package/src/methods/noop.method.ts +0 -1
  126. package/src/methods/p.method.ts +0 -6
  127. package/src/methods/remove-child-nodes.method.ts +0 -5
  128. package/src/methods/sanitize-html.method.ts +0 -32
  129. package/src/methods/text.method.ts +0 -77
  130. package/src/methods/traverse.method.ts +0 -33
  131. package/src/post-body-summary.spec.ts +0 -150
  132. package/src/post-body-summary.ts +0 -130
  133. package/src/proxify-image-src.spec.ts +0 -76
  134. package/src/proxify-image-src.ts +0 -77
  135. package/src/sanitize-html.spec.ts +0 -15
  136. package/src/test/data/index.ts +0 -12
  137. package/src/test/data/json/muratkbesiroglu____sci-fi-novel-underground-city-part-13.json +0 -2120
  138. package/src/test/data/json/steemitboard____steemitboard-notify-dunsky-20181210t153450000z.json +0 -47
  139. package/src/test/data/legacy/10.json +0 -5
  140. package/src/test/data/legacy/20.json +0 -5
  141. package/src/test/data/legacy/21.json +0 -5
  142. package/src/test/data/legacy/2112524.json +0 -5
  143. package/src/test/data/legacy/22.json +0 -5
  144. package/src/test/data/legacy/23.json +0 -5
  145. package/src/test/data/legacy/24.json +0 -5
  146. package/src/test/data/legacy/25.json +0 -5
  147. package/src/test/data/legacy/26.json +0 -5
  148. package/src/test/data/legacy/27.JSON +0 -5
  149. package/src/test/data/legacy/28.json +0 -5
  150. package/src/test/data/legacy/29.json +0 -5
  151. package/src/test/data/legacy/31.json +0 -5
  152. package/src/test/snaps.json +0 -8
  153. package/src/types/entry.interface.ts +0 -7
  154. package/src/types/index.ts +0 -2
  155. package/src/types/xss-white-list.interface.ts +0 -7
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2019 eSteem
1
+ Copyright (c) 2019 Ecency
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -1,22 +1,176 @@
1
- # Render Helper
1
+ # Ecency render helper
2
2
 
3
- Ecency markdown+html render helper
3
+ ## Project Overview
4
4
 
5
- - custom markdown to html
6
- - custom markdown to amp
7
- - content summary extract
8
- - customize proxy endpoint
9
- - image links proxify
10
- - catch content image/thumbnail
11
- - embedded media filters
12
- - internalized links
13
- - cached responses
14
- - xss protection
5
+ `@ecency/render-helper` is a TypeScript library for rendering and sanitizing markdown+HTML content for the Ecency social platform (built on Hive blockchain). It provides security-focused content rendering with XSS protection, image proxification, and embedded media filtering.
15
6
 
7
+ ## Key Commands
16
8
 
17
- ## Pull request guide
9
+ ### Development (from package directory)
10
+ - `pnpm build` - Build with tsup (creates browser ESM + Node CJS/ESM bundles)
11
+ - `pnpm test` - Run vitest tests
12
+ - `pnpm lint` - Run ESLint on TypeScript source files
18
13
 
19
- - If you want to fix a bug or add new feature, please add relevant tests
20
- - Do not load external js or libraries when adding embed supports, it compromises the security
21
- - Make sure all tests pass without breaking existing structure
22
- - Make sure to increase version number in package.json, so CI can auto publish new version
14
+ ### From Monorepo Root
15
+ - `pnpm render-helper` - Build render-helper package
16
+ - `pnpm build:packages` - Build all packages (sdk, wallets, renderer, render-helper)
17
+ - `pnpm publish:render-helper` - Manually publish to npm
18
+
19
+ ### CI/CD
20
+ - Part of vision-next monorepo with centralized GitHub Actions (`.github/workflows/packages.yml`)
21
+ - Automatically detects changes to `packages/render-helper/**` using git diff
22
+ - On push to `develop` branch, builds and publishes to npm
23
+ - Uses OIDC (OpenID Connect) for secure npm authentication
24
+ - Version must be incremented in package.json for successful publish
25
+
26
+ ## Architecture
27
+
28
+ ### Core Modules
29
+
30
+ **Main Entry Point** (`src/index.ts`):
31
+ - `renderPostBody` - Convert markdown+HTML to sanitized HTML
32
+ - `catchPostImage` - Extract first image from post content or metadata
33
+ - `postBodySummary` - Generate text summary from post body
34
+ - `proxifyImageSrc` - Proxify image URLs through Ecency's image server
35
+ - `setProxyBase` - Configure image proxy endpoint (default: `https://images.ecency.com`)
36
+ - `setCacheSize` - Configure LRU cache size (default: 60 entries)
37
+
38
+ ### Processing Pipeline
39
+
40
+ 1. **markdown-2-html.ts**: Main rendering pipeline
41
+ - Converts Entry objects (Hive posts) or raw strings
42
+ - Uses LRU cache with keys based on `author-permlink-last_update-updated`
43
+ - Calls `cleanReply()` to normalize input, then `markdownToHTML()` to render
44
+
45
+ 2. **traverse.method.ts**: DOM tree walker
46
+ - Recursively processes parsed HTML nodes
47
+ - Applies specialized handlers for `<a>`, `<iframe>`, `<img>`, `<p>`, and text nodes
48
+ - Passes `forApp` flag to differentiate app vs web rendering
49
+ - Tracks `firstImageFound` state for image processing
50
+
51
+ 3. **sanitize-html.method.ts**: XSS protection
52
+ - Uses `xss` library with strict whitelist (ALLOWED_ATTRIBUTES)
53
+ - Blocks event handlers (`on*` attributes)
54
+ - Validates URLs (must be `https?://`, blocks `javascript:`)
55
+ - Strips `<style>` tags and CSS
56
+ - Whitelists specific `id` attributes via `ID_WHITELIST` regex
57
+
58
+ 4. **Node-specific processors** (`src/methods/`):
59
+ - `a.method.ts` - Link handling, internalization for whitelisted domains
60
+ - `iframe.method.ts` - Embedded content filtering (YouTube, 3speak, etc.)
61
+ - `img.method.ts` - Image proxification and lazy loading
62
+ - `p.method.ts` - Paragraph processing for inline data attributes
63
+ - `text.method.ts` - Link detection and linkification
64
+
65
+ ### Image Processing
66
+
67
+ **proxify-image-src.ts**:
68
+ - Converts external image URLs to Ecency's CDN using multihash encoding
69
+ - Reuses existing proxy hashes to avoid re-encoding
70
+ - Supports format conversion (webp/png) and resizing (width/height)
71
+ - Handles legacy domains (images.hive.blog, steemitimages.com)
72
+
73
+ **catch-post-image.ts**:
74
+ - Extracts featured image from `json_metadata.image` field
75
+ - Falls back to first `<img>` in rendered HTML body
76
+ - Preserves GIF format (doesn't resize animated images)
77
+
78
+ ### Security Considerations
79
+
80
+ **Whitelist Management** (`src/consts/white-list.const.ts`):
81
+ - `WHITE_LIST` - Trusted Hive frontend domains (ecency.com, peakd.com, etc.)
82
+ - `OLD_LIST` - Deprecated Steem-era domains
83
+ - Links to whitelisted domains are converted to internal app routes
84
+
85
+ **XSS Protection**:
86
+ - All HTML goes through `sanitizeHtml()` with strict attribute whitelist
87
+ - No inline styles allowed (CSS disabled)
88
+ - Event handlers stripped
89
+ - URL schemes validated
90
+
91
+ ### Caching Strategy
92
+
93
+ - LRU cache v11 (`src/cache.ts`) stores rendered HTML and extracted images
94
+ - Cache keys include content hash (last_update, updated) to auto-invalidate
95
+ - Separate cache entries for different image sizes and formats
96
+ - Default size: 60 entries
97
+ - Uses modern `LRUCache` named import from lru-cache v11
98
+
99
+ ## File Structure
100
+
101
+ ```
102
+ src/
103
+ ├── index.ts # Main exports
104
+ ├── markdown-2-html.ts # Primary rendering entry point
105
+ ├── catch-post-image.ts # Image extraction
106
+ ├── post-body-summary.ts # Summary generation
107
+ ├── proxify-image-src.ts # Image URL proxification
108
+ ├── helper.ts # Utilities (doc creation, cache keys, validators)
109
+ ├── cache.ts # LRU cache wrapper
110
+ ├── consts/ # Configuration constants
111
+ │ ├── white-list.const.ts # Trusted domains
112
+ │ ├── allowed-attributes.const.ts # XSS whitelist
113
+ │ ├── section-list.const.ts # App route sections
114
+ │ └── regexes.const.ts # Pattern matching
115
+ ├── methods/ # DOM node processors
116
+ │ ├── traverse.method.ts # Main tree walker
117
+ │ ├── sanitize-html.method.ts # XSS protection
118
+ │ ├── markdown-to-html.method.ts # Remarkable wrapper
119
+ │ ├── a.method.ts # Link processing
120
+ │ ├── iframe.method.ts # Embed handling
121
+ │ ├── img.method.ts # Image processing
122
+ │ └── ...
123
+ └── types/ # TypeScript interfaces
124
+ └── entry.interface.ts # Hive post structure
125
+ ```
126
+
127
+ ## Build System
128
+
129
+ - Uses **tsup** for bundling (shared with other vision-next packages)
130
+ - TypeScript 5.6+ with strict mode enabled
131
+ - Outputs three bundles:
132
+ - `dist/browser/index.js` - Browser ESM with TypeScript declarations
133
+ - `dist/node/index.mjs` - Node ESM
134
+ - `dist/node/index.cjs` - Node CommonJS
135
+ - Configuration: `tsup.config.ts` (dual browser/node builds)
136
+ - All dependencies are externalized (not bundled)
137
+
138
+ ## Platform Support
139
+
140
+ ### React Native (iOS/Android)
141
+
142
+ This package fully supports React Native applications:
143
+
144
+ - **Package Resolution**: The `react-native` field in `package.json` exports ensures React Native bundlers (Metro) automatically resolve to the browser build (`dist/browser/index.js`)
145
+ - **Crypto Support**: When `platform !== 'web'` (i.e., React Native environment), the package uses `react-native-crypto-js` for cryptographic operations
146
+ - **Usage**: Simply import the package normally in your React Native project - no special configuration needed
147
+
148
+ ```typescript
149
+ import { renderPostBody, postBodySummary } from '@ecency/render-helper'
150
+
151
+ // The platform parameter determines the build target
152
+ // 'web' = browser/web, any other value = React Native
153
+ const html = renderPostBody(entry, false, 'react-native')
154
+ const summary = postBodySummary(entry, 200, 'react-native')
155
+ ```
156
+
157
+ **Platform Parameter**:
158
+ - `'web'` - Uses browser APIs and standard crypto
159
+ - Any other value (e.g., `'react-native'`, `'mobile'`) - Uses React Native-compatible implementations including `react-native-crypto-js`
160
+ - The platform parameter is required in `renderPostBody()` and `postBodySummary()` functions
161
+
162
+ ## Testing
163
+
164
+ - Vitest for unit tests (to be migrated from Jest)
165
+ - Test files: `*.spec.ts` adjacent to source
166
+ - Tests should verify XSS protection for new features
167
+ - Do not load external JS/libraries in embed support (security requirement)
168
+
169
+ ## Important Notes
170
+
171
+ - When adding embed support for new platforms, implement server-side rendering only
172
+ - All external content must go through sanitization
173
+ - Version bumps required for auto-publish via CI
174
+ - Part of vision-next monorepo - changes require updating across dependent packages
175
+ - The `@ecency/renderer` package uses this as a workspace dependency
176
+ - Exports are optimized for tree-shaking with proper ESM structure
@@ -0,0 +1,32 @@
1
+ interface Entry {
2
+ author?: string;
3
+ permlink?: string;
4
+ last_update?: string;
5
+ body: any;
6
+ json_metadata?: any;
7
+ }
8
+
9
+ declare function markdown2Html(obj: Entry | string, forApp?: boolean, webp?: boolean, parentDomain?: string): string;
10
+
11
+ declare function catchPostImage(obj: Entry | string, width?: number, height?: number, format?: string): string | null;
12
+
13
+ /**
14
+ * Generate a text summary from an Entry object or raw string
15
+ * @param obj - Entry object or raw post body string
16
+ * @param length - Maximum length of the summary (default: 200, use 0 for no truncation)
17
+ * @param platform - Target platform: 'web' for browser/Node.js, 'ios'/'android' for React Native (default: 'web')
18
+ * Controls entity/placeholder handling - 'web' skips placeholder substitution, other values enable it
19
+ * @returns Text summary of the post body
20
+ */
21
+ declare function getPostBodySummary(obj: Entry | string, length?: number, platform?: 'ios' | 'android' | 'web'): string;
22
+
23
+ declare function setProxyBase(p: string): void;
24
+ declare function proxifyImageSrc(url?: string, width?: number, height?: number, format?: string): string;
25
+
26
+ declare function setCacheSize(size: number): void;
27
+
28
+ declare const SECTION_LIST: string[];
29
+
30
+ declare function isValidPermlink(permlink: string): boolean;
31
+
32
+ export { SECTION_LIST, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase };