@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.
@@ -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
- .openapi({ description: "Post content", example: "Hello world" }),
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(500, { message: "Post content cannot exceed 500 characters" })
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(/&nbsp;/g, " ")
44
+ .replace(/&amp;/g, "&")
45
+ .replace(/&quot;/g, '"')
46
+ .replace(/&lt;/g, "<")
47
+ .replace(/&gt;/g, ">")
48
+ .replace(/&apos;/g, "'")
49
+ // Handle numeric entities (e.g., &#123;)
50
+ .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec)));
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyacreatives/shared",
3
- "version": "2.1.97",
3
+ "version": "2.1.98",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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
- .openapi({ description: "Post content", example: "Hello world" }),
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(500, { message: "Post content cannot exceed 500 characters" })
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(/&nbsp;/g, " ")
48
+ .replace(/&amp;/g, "&")
49
+ .replace(/&quot;/g, '"')
50
+ .replace(/&lt;/g, "<")
51
+ .replace(/&gt;/g, ">")
52
+ .replace(/&apos;/g, "'")
53
+ // Handle numeric entities (e.g., &#123;)
54
+ .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec))
55
+ );
56
+ }