@zyacreatives/shared 2.1.97 → 2.1.98
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/schemas/post.js +10 -2
- package/dist/utils/clean-html.d.ts +1 -0
- package/dist/utils/clean-html.js +51 -0
- package/package.json +1 -1
- package/src/schemas/post.ts +12 -2
- package/src/utils/clean-html.ts +56 -0
package/dist/schemas/post.js
CHANGED
|
@@ -7,6 +7,7 @@ const file_1 = require("./file");
|
|
|
7
7
|
const comment_1 = require("./comment");
|
|
8
8
|
const entity_stats_1 = require("./entity-stats");
|
|
9
9
|
const activity_1 = require("./activity");
|
|
10
|
+
const clean_html_1 = require("../utils/clean-html");
|
|
10
11
|
exports.PostEntitySchema = zod_openapi_1.z.object({
|
|
11
12
|
id: zod_openapi_1.z
|
|
12
13
|
.cuid2()
|
|
@@ -32,7 +33,14 @@ exports.PostEntitySchema = zod_openapi_1.z.object({
|
|
|
32
33
|
content: zod_openapi_1.z
|
|
33
34
|
.string()
|
|
34
35
|
.optional()
|
|
35
|
-
.
|
|
36
|
+
.refine((val) => {
|
|
37
|
+
if (!val)
|
|
38
|
+
return true;
|
|
39
|
+
const plainText = (0, clean_html_1.cleanHtml)(val, Number.MAX_SAFE_INTEGER);
|
|
40
|
+
return plainText.length <= 300;
|
|
41
|
+
}, {
|
|
42
|
+
message: "Post content cannot exceed 300 characters",
|
|
43
|
+
}),
|
|
36
44
|
postType: zod_openapi_1.z.enum(constants_1.POST_TYPES).openapi({
|
|
37
45
|
description: "Type of the post entity this statistic belongs to.",
|
|
38
46
|
title: "Post Type",
|
|
@@ -91,7 +99,7 @@ exports.CreatePostInputSchema = zod_openapi_1.z.object({
|
|
|
91
99
|
parentType: zod_openapi_1.z.enum(constants_1.ACTIVITY_PARENT_TYPES).default(constants_1.ACTIVITY_PARENT_TYPES.POST),
|
|
92
100
|
content: zod_openapi_1.z
|
|
93
101
|
.string()
|
|
94
|
-
.max(
|
|
102
|
+
.max(300, { message: "Post content cannot exceed 300 characters" })
|
|
95
103
|
.optional()
|
|
96
104
|
.openapi({
|
|
97
105
|
description: "Post content",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cleanHtml(html: string, maxLength?: number): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cleanHtml = cleanHtml;
|
|
4
|
+
function cleanHtml(html, maxLength = 60) {
|
|
5
|
+
if (!html)
|
|
6
|
+
return "";
|
|
7
|
+
let plainText = "";
|
|
8
|
+
// 1. Browser/Client-side: Use the native DOM parser (Bulletproof)
|
|
9
|
+
if (typeof window !== "undefined" && typeof DOMParser !== "undefined") {
|
|
10
|
+
try {
|
|
11
|
+
const parser = new DOMParser();
|
|
12
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
13
|
+
plainText = doc.body.textContent || "";
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
// Fallback to regex if parsing fails for some reason
|
|
17
|
+
plainText = regexClean(html);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// 2. Server-side (Node.js/SSR): Use robust regex
|
|
21
|
+
else {
|
|
22
|
+
plainText = regexClean(html);
|
|
23
|
+
}
|
|
24
|
+
// Final Cleanup: Collapse multiple spaces/newlines into one single space
|
|
25
|
+
const cleaned = plainText.replace(/\s+/g, " ").trim();
|
|
26
|
+
if (cleaned.length <= maxLength)
|
|
27
|
+
return cleaned;
|
|
28
|
+
// Truncate and add ellipsis, ensuring no space before the dots
|
|
29
|
+
return cleaned.substring(0, maxLength).trim() + "...";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Robust Regex fallback for environments without DOM access (SSR)
|
|
33
|
+
*/
|
|
34
|
+
function regexClean(html) {
|
|
35
|
+
return (html
|
|
36
|
+
// Remove script, style, and title tags AND their contents
|
|
37
|
+
.replace(/<(script|style|title)[^>]*>[\s\S]*?<\/\1>/gi, "")
|
|
38
|
+
// Replace block-level tags with a space to prevent "HelloWorld" squashing
|
|
39
|
+
.replace(/<(br|p|div|h[1-6]|li|tr|section|article|header|footer)[^>]*\/?>/gi, " ")
|
|
40
|
+
// Remove all remaining HTML tags
|
|
41
|
+
.replace(/<[^>]*>/g, "")
|
|
42
|
+
// Decode common HTML entities
|
|
43
|
+
.replace(/ /g, " ")
|
|
44
|
+
.replace(/&/g, "&")
|
|
45
|
+
.replace(/"/g, '"')
|
|
46
|
+
.replace(/</g, "<")
|
|
47
|
+
.replace(/>/g, ">")
|
|
48
|
+
.replace(/'/g, "'")
|
|
49
|
+
// Handle numeric entities (e.g., {)
|
|
50
|
+
.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec)));
|
|
51
|
+
}
|
package/package.json
CHANGED
package/src/schemas/post.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { CreateFileInputSchema } from "./file";
|
|
|
8
8
|
import { CommentEntitySchema } from "./comment";
|
|
9
9
|
import { EntityStatsSchema } from "./entity-stats";
|
|
10
10
|
import { ActivitySchema } from "./activity";
|
|
11
|
+
import { cleanHtml } from "../utils/clean-html";
|
|
11
12
|
|
|
12
13
|
export const PostEntitySchema = z.object({
|
|
13
14
|
id: z
|
|
@@ -36,7 +37,16 @@ export const PostEntitySchema = z.object({
|
|
|
36
37
|
content: z
|
|
37
38
|
.string()
|
|
38
39
|
.optional()
|
|
39
|
-
.
|
|
40
|
+
.refine(
|
|
41
|
+
(val) => {
|
|
42
|
+
if (!val) return true;
|
|
43
|
+
const plainText = cleanHtml(val, Number.MAX_SAFE_INTEGER);
|
|
44
|
+
return plainText.length <= 300;
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
message: "Post content cannot exceed 300 characters",
|
|
48
|
+
},
|
|
49
|
+
),
|
|
40
50
|
postType: z.enum(POST_TYPES).openapi({
|
|
41
51
|
description: "Type of the post entity this statistic belongs to.",
|
|
42
52
|
title: "Post Type",
|
|
@@ -102,7 +112,7 @@ export const CreatePostInputSchema = z.object({
|
|
|
102
112
|
parentType: z.enum(ACTIVITY_PARENT_TYPES).default(ACTIVITY_PARENT_TYPES.POST),
|
|
103
113
|
content: z
|
|
104
114
|
.string()
|
|
105
|
-
.max(
|
|
115
|
+
.max(300, { message: "Post content cannot exceed 300 characters" })
|
|
106
116
|
.optional()
|
|
107
117
|
.openapi({
|
|
108
118
|
description: "Post content",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function cleanHtml(html: string, maxLength: number = 60) {
|
|
2
|
+
if (!html) return "";
|
|
3
|
+
|
|
4
|
+
let plainText = "";
|
|
5
|
+
|
|
6
|
+
// 1. Browser/Client-side: Use the native DOM parser (Bulletproof)
|
|
7
|
+
if (typeof window !== "undefined" && typeof DOMParser !== "undefined") {
|
|
8
|
+
try {
|
|
9
|
+
const parser = new DOMParser();
|
|
10
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
11
|
+
plainText = doc.body.textContent || "";
|
|
12
|
+
} catch (e) {
|
|
13
|
+
// Fallback to regex if parsing fails for some reason
|
|
14
|
+
plainText = regexClean(html);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// 2. Server-side (Node.js/SSR): Use robust regex
|
|
18
|
+
else {
|
|
19
|
+
plainText = regexClean(html);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Final Cleanup: Collapse multiple spaces/newlines into one single space
|
|
23
|
+
const cleaned = plainText.replace(/\s+/g, " ").trim();
|
|
24
|
+
|
|
25
|
+
if (cleaned.length <= maxLength) return cleaned;
|
|
26
|
+
|
|
27
|
+
// Truncate and add ellipsis, ensuring no space before the dots
|
|
28
|
+
return cleaned.substring(0, maxLength).trim() + "...";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Robust Regex fallback for environments without DOM access (SSR)
|
|
33
|
+
*/
|
|
34
|
+
function regexClean(html: string): string {
|
|
35
|
+
return (
|
|
36
|
+
html
|
|
37
|
+
// Remove script, style, and title tags AND their contents
|
|
38
|
+
.replace(/<(script|style|title)[^>]*>[\s\S]*?<\/\1>/gi, "")
|
|
39
|
+
// Replace block-level tags with a space to prevent "HelloWorld" squashing
|
|
40
|
+
.replace(
|
|
41
|
+
/<(br|p|div|h[1-6]|li|tr|section|article|header|footer)[^>]*\/?>/gi,
|
|
42
|
+
" ",
|
|
43
|
+
)
|
|
44
|
+
// Remove all remaining HTML tags
|
|
45
|
+
.replace(/<[^>]*>/g, "")
|
|
46
|
+
// Decode common HTML entities
|
|
47
|
+
.replace(/ /g, " ")
|
|
48
|
+
.replace(/&/g, "&")
|
|
49
|
+
.replace(/"/g, '"')
|
|
50
|
+
.replace(/</g, "<")
|
|
51
|
+
.replace(/>/g, ">")
|
|
52
|
+
.replace(/'/g, "'")
|
|
53
|
+
// Handle numeric entities (e.g., {)
|
|
54
|
+
.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec))
|
|
55
|
+
);
|
|
56
|
+
}
|