@utilsy/cms-nextjs 0.1.0

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/react.cjs ADDED
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/react/context.ts
7
+ var CmsClientContext = react.createContext(null);
8
+ function CmsProvider({ client, children }) {
9
+ return /* @__PURE__ */ jsxRuntime.jsx(CmsClientContext.Provider, { value: client, children });
10
+ }
11
+ function useCmsClient() {
12
+ const client = react.useContext(CmsClientContext);
13
+ if (!client) {
14
+ throw new Error("useCmsClient must be used within a <CmsProvider client={...}>");
15
+ }
16
+ return client;
17
+ }
18
+ var DEFAULT_STORAGE_KEY = "utilsy_cms_visitor_id";
19
+ function useVisitorId(options) {
20
+ const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;
21
+ const [visitorId, setVisitorId] = react.useState("");
22
+ react.useEffect(() => {
23
+ if (typeof window === "undefined") return;
24
+ let id = localStorage.getItem(storageKey);
25
+ if (!id) {
26
+ id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;
27
+ localStorage.setItem(storageKey, id);
28
+ }
29
+ setVisitorId(id);
30
+ }, [storageKey]);
31
+ return visitorId;
32
+ }
33
+ function getVisitorId(storageKey = DEFAULT_STORAGE_KEY) {
34
+ if (typeof window === "undefined") return "";
35
+ let id = localStorage.getItem(storageKey);
36
+ if (!id) {
37
+ id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;
38
+ localStorage.setItem(storageKey, id);
39
+ }
40
+ return id;
41
+ }
42
+ function useBlogEngagement(postId, options) {
43
+ const client = useCmsClient();
44
+ const [engagement, setEngagement] = react.useState(null);
45
+ const [loading, setLoading] = react.useState(true);
46
+ const [error, setError] = react.useState(null);
47
+ const refetch = react.useCallback(async () => {
48
+ if (!postId) return;
49
+ setLoading(true);
50
+ setError(null);
51
+ try {
52
+ const visitorId = options?.visitorId ?? (typeof window !== "undefined" ? getVisitorId(options?.storageKey) : void 0);
53
+ const data = await client.blog.getEngagement(postId, { visitorId });
54
+ setEngagement(data);
55
+ } catch (e) {
56
+ setError(e instanceof Error ? e : new Error(String(e)));
57
+ } finally {
58
+ setLoading(false);
59
+ }
60
+ }, [client, postId, options?.visitorId, options?.storageKey]);
61
+ react.useEffect(() => {
62
+ refetch();
63
+ }, [refetch]);
64
+ return { engagement, loading, error, refetch };
65
+ }
66
+ function useBlogLike(postId, options) {
67
+ const client = useCmsClient();
68
+ const { engagement, loading: engagementLoading, refetch } = useBlogEngagement(postId, options);
69
+ const [toggling, setToggling] = react.useState(false);
70
+ const [error, setError] = react.useState(null);
71
+ const toggle = react.useCallback(async () => {
72
+ if (!postId) return;
73
+ setToggling(true);
74
+ setError(null);
75
+ try {
76
+ const visitorId = options?.visitorId ?? (typeof window !== "undefined" ? getVisitorId(options?.storageKey) : void 0);
77
+ await client.blog.toggleLike(postId, { visitorId });
78
+ await refetch();
79
+ } catch (e) {
80
+ setError(e instanceof Error ? e : new Error(String(e)));
81
+ } finally {
82
+ setToggling(false);
83
+ }
84
+ }, [client, postId, options?.visitorId, options?.storageKey, refetch]);
85
+ return {
86
+ liked: engagement?.likedByMe ?? false,
87
+ likeCount: engagement?.likeCount ?? 0,
88
+ viewCount: engagement?.viewCount ?? 0,
89
+ commentCount: engagement?.commentCount ?? 0,
90
+ loading: engagementLoading,
91
+ toggling,
92
+ error,
93
+ toggle,
94
+ refetch
95
+ };
96
+ }
97
+ function useBlogComments(postId) {
98
+ const client = useCmsClient();
99
+ const [comments, setComments] = react.useState([]);
100
+ const [loading, setLoading] = react.useState(true);
101
+ const [submitting, setSubmitting] = react.useState(false);
102
+ const [error, setError] = react.useState(null);
103
+ const refetch = react.useCallback(async () => {
104
+ if (!postId) return;
105
+ setLoading(true);
106
+ setError(null);
107
+ try {
108
+ const data = await client.blog.listMappedComments(postId);
109
+ setComments(data ?? []);
110
+ } catch (e) {
111
+ setError(e instanceof Error ? e : new Error(String(e)));
112
+ } finally {
113
+ setLoading(false);
114
+ }
115
+ }, [client, postId]);
116
+ react.useEffect(() => {
117
+ refetch();
118
+ }, [refetch]);
119
+ const submitComment = react.useCallback(
120
+ async (input) => {
121
+ if (!postId) return;
122
+ setSubmitting(true);
123
+ setError(null);
124
+ try {
125
+ await client.blog.createComment(postId, input);
126
+ await refetch();
127
+ } catch (e) {
128
+ const err = e instanceof Error ? e : new Error(String(e));
129
+ setError(err);
130
+ throw err;
131
+ } finally {
132
+ setSubmitting(false);
133
+ }
134
+ },
135
+ [client, postId, refetch]
136
+ );
137
+ return { comments, loading, submitting, error, refetch, submitComment };
138
+ }
139
+
140
+ exports.CmsProvider = CmsProvider;
141
+ exports.getVisitorId = getVisitorId;
142
+ exports.useBlogComments = useBlogComments;
143
+ exports.useBlogEngagement = useBlogEngagement;
144
+ exports.useBlogLike = useBlogLike;
145
+ exports.useCmsClient = useCmsClient;
146
+ exports.useVisitorId = useVisitorId;
147
+ //# sourceMappingURL=react.cjs.map
148
+ //# sourceMappingURL=react.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/context.ts","../src/react/provider.tsx","../src/react/use-cms-client.ts","../src/react/use-visitor-id.ts","../src/react/use-blog-engagement.ts","../src/react/use-blog-like.ts","../src/react/use-blog-comments.ts"],"names":["createContext","useContext","useState","useEffect","useCallback"],"mappings":"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","file":"react.cjs","sourcesContent":["import { createContext } from \"react\";\nimport type { CmsClient } from \"../client.js\";\n\nexport const CmsClientContext = createContext<CmsClient | null>(null);\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport type { CmsClient } from \"../client.js\";\nimport { CmsClientContext } from \"./context.js\";\n\nexport type CmsProviderProps = {\n client: CmsClient;\n children: ReactNode;\n};\n\nexport function CmsProvider({ client, children }: CmsProviderProps) {\n return <CmsClientContext.Provider value={client}>{children}</CmsClientContext.Provider>;\n}\n","\"use client\";\n\nimport { useContext } from \"react\";\nimport type { CmsClient } from \"../client.js\";\nimport { CmsClientContext } from \"./context.js\";\n\nexport function useCmsClient(): CmsClient {\n const client = useContext(CmsClientContext);\n if (!client) {\n throw new Error(\"useCmsClient must be used within a <CmsProvider client={...}>\");\n }\n return client;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nconst DEFAULT_STORAGE_KEY = \"utilsy_cms_visitor_id\";\n\nexport type UseVisitorIdOptions = {\n storageKey?: string;\n};\n\nexport function useVisitorId(options?: UseVisitorIdOptions): string {\n const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;\n const [visitorId, setVisitorId] = useState(\"\");\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n let id = localStorage.getItem(storageKey);\n if (!id) {\n id =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n localStorage.setItem(storageKey, id);\n }\n setVisitorId(id);\n }, [storageKey]);\n\n return visitorId;\n}\n\n/** Non-hook helper for event handlers */\nexport function getVisitorId(storageKey = DEFAULT_STORAGE_KEY): string {\n if (typeof window === \"undefined\") return \"\";\n let id = localStorage.getItem(storageKey);\n if (!id) {\n id =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n localStorage.setItem(storageKey, id);\n }\n return id;\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { BlogEngagement } from \"../blog/types.js\";\nimport { useCmsClient } from \"./use-cms-client.js\";\nimport { getVisitorId } from \"./use-visitor-id.js\";\n\nexport type UseBlogEngagementOptions = {\n visitorId?: string;\n storageKey?: string;\n};\n\nexport function useBlogEngagement(postId: string, options?: UseBlogEngagementOptions) {\n const client = useCmsClient();\n const [engagement, setEngagement] = useState<BlogEngagement | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const refetch = useCallback(async () => {\n if (!postId) return;\n setLoading(true);\n setError(null);\n try {\n const visitorId =\n options?.visitorId ??\n (typeof window !== \"undefined\" ? getVisitorId(options?.storageKey) : undefined);\n const data = await client.blog.getEngagement(postId, { visitorId });\n setEngagement(data);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [client, postId, options?.visitorId, options?.storageKey]);\n\n useEffect(() => {\n refetch();\n }, [refetch]);\n\n return { engagement, loading, error, refetch };\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport { useBlogEngagement } from \"./use-blog-engagement.js\";\nimport { useCmsClient } from \"./use-cms-client.js\";\nimport { getVisitorId } from \"./use-visitor-id.js\";\n\nexport type UseBlogLikeOptions = {\n visitorId?: string;\n storageKey?: string;\n};\n\nexport function useBlogLike(postId: string, options?: UseBlogLikeOptions) {\n const client = useCmsClient();\n const { engagement, loading: engagementLoading, refetch } = useBlogEngagement(postId, options);\n const [toggling, setToggling] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const toggle = useCallback(async () => {\n if (!postId) return;\n setToggling(true);\n setError(null);\n try {\n const visitorId =\n options?.visitorId ??\n (typeof window !== \"undefined\" ? getVisitorId(options?.storageKey) : undefined);\n await client.blog.toggleLike(postId, { visitorId });\n await refetch();\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setToggling(false);\n }\n }, [client, postId, options?.visitorId, options?.storageKey, refetch]);\n\n return {\n liked: engagement?.likedByMe ?? false,\n likeCount: engagement?.likeCount ?? 0,\n viewCount: engagement?.viewCount ?? 0,\n commentCount: engagement?.commentCount ?? 0,\n loading: engagementLoading,\n toggling,\n error,\n toggle,\n refetch,\n };\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { BlogComment, CreateBlogCommentInput } from \"../blog/types.js\";\nimport { useCmsClient } from \"./use-cms-client.js\";\n\nexport function useBlogComments(postId: string) {\n const client = useCmsClient();\n const [comments, setComments] = useState<BlogComment[]>([]);\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const refetch = useCallback(async () => {\n if (!postId) return;\n setLoading(true);\n setError(null);\n try {\n const data = await client.blog.listMappedComments(postId);\n setComments(data ?? []);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [client, postId]);\n\n useEffect(() => {\n refetch();\n }, [refetch]);\n\n const submitComment = useCallback(\n async (input: CreateBlogCommentInput) => {\n if (!postId) return;\n setSubmitting(true);\n setError(null);\n try {\n await client.blog.createComment(postId, input);\n await refetch();\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n throw err;\n } finally {\n setSubmitting(false);\n }\n },\n [client, postId, refetch],\n );\n\n return { comments, loading, submitting, error, refetch, submitComment };\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { l as CmsClient, e as BlogEngagement, c as BlogComment, n as CreateBlogCommentInput } from './client-C3E94syg.cjs';
4
+
5
+ type CmsProviderProps = {
6
+ client: CmsClient;
7
+ children: ReactNode;
8
+ };
9
+ declare function CmsProvider({ client, children }: CmsProviderProps): react_jsx_runtime.JSX.Element;
10
+
11
+ declare function useCmsClient(): CmsClient;
12
+
13
+ type UseVisitorIdOptions = {
14
+ storageKey?: string;
15
+ };
16
+ declare function useVisitorId(options?: UseVisitorIdOptions): string;
17
+ /** Non-hook helper for event handlers */
18
+ declare function getVisitorId(storageKey?: string): string;
19
+
20
+ type UseBlogEngagementOptions = {
21
+ visitorId?: string;
22
+ storageKey?: string;
23
+ };
24
+ declare function useBlogEngagement(postId: string, options?: UseBlogEngagementOptions): {
25
+ engagement: BlogEngagement | null;
26
+ loading: boolean;
27
+ error: Error | null;
28
+ refetch: () => Promise<void>;
29
+ };
30
+
31
+ type UseBlogLikeOptions = {
32
+ visitorId?: string;
33
+ storageKey?: string;
34
+ };
35
+ declare function useBlogLike(postId: string, options?: UseBlogLikeOptions): {
36
+ liked: boolean;
37
+ likeCount: number;
38
+ viewCount: number;
39
+ commentCount: number;
40
+ loading: boolean;
41
+ toggling: boolean;
42
+ error: Error | null;
43
+ toggle: () => Promise<void>;
44
+ refetch: () => Promise<void>;
45
+ };
46
+
47
+ declare function useBlogComments(postId: string): {
48
+ comments: BlogComment[];
49
+ loading: boolean;
50
+ submitting: boolean;
51
+ error: Error | null;
52
+ refetch: () => Promise<void>;
53
+ submitComment: (input: CreateBlogCommentInput) => Promise<void>;
54
+ };
55
+
56
+ export { CmsProvider, type CmsProviderProps, type UseBlogEngagementOptions, type UseBlogLikeOptions, type UseVisitorIdOptions, getVisitorId, useBlogComments, useBlogEngagement, useBlogLike, useCmsClient, useVisitorId };
@@ -0,0 +1,56 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { l as CmsClient, e as BlogEngagement, c as BlogComment, n as CreateBlogCommentInput } from './client-C3E94syg.js';
4
+
5
+ type CmsProviderProps = {
6
+ client: CmsClient;
7
+ children: ReactNode;
8
+ };
9
+ declare function CmsProvider({ client, children }: CmsProviderProps): react_jsx_runtime.JSX.Element;
10
+
11
+ declare function useCmsClient(): CmsClient;
12
+
13
+ type UseVisitorIdOptions = {
14
+ storageKey?: string;
15
+ };
16
+ declare function useVisitorId(options?: UseVisitorIdOptions): string;
17
+ /** Non-hook helper for event handlers */
18
+ declare function getVisitorId(storageKey?: string): string;
19
+
20
+ type UseBlogEngagementOptions = {
21
+ visitorId?: string;
22
+ storageKey?: string;
23
+ };
24
+ declare function useBlogEngagement(postId: string, options?: UseBlogEngagementOptions): {
25
+ engagement: BlogEngagement | null;
26
+ loading: boolean;
27
+ error: Error | null;
28
+ refetch: () => Promise<void>;
29
+ };
30
+
31
+ type UseBlogLikeOptions = {
32
+ visitorId?: string;
33
+ storageKey?: string;
34
+ };
35
+ declare function useBlogLike(postId: string, options?: UseBlogLikeOptions): {
36
+ liked: boolean;
37
+ likeCount: number;
38
+ viewCount: number;
39
+ commentCount: number;
40
+ loading: boolean;
41
+ toggling: boolean;
42
+ error: Error | null;
43
+ toggle: () => Promise<void>;
44
+ refetch: () => Promise<void>;
45
+ };
46
+
47
+ declare function useBlogComments(postId: string): {
48
+ comments: BlogComment[];
49
+ loading: boolean;
50
+ submitting: boolean;
51
+ error: Error | null;
52
+ refetch: () => Promise<void>;
53
+ submitComment: (input: CreateBlogCommentInput) => Promise<void>;
54
+ };
55
+
56
+ export { CmsProvider, type CmsProviderProps, type UseBlogEngagementOptions, type UseBlogLikeOptions, type UseVisitorIdOptions, getVisitorId, useBlogComments, useBlogEngagement, useBlogLike, useCmsClient, useVisitorId };
package/dist/react.js ADDED
@@ -0,0 +1,140 @@
1
+ import { createContext, useContext, useState, useEffect, useCallback } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/react/context.ts
5
+ var CmsClientContext = createContext(null);
6
+ function CmsProvider({ client, children }) {
7
+ return /* @__PURE__ */ jsx(CmsClientContext.Provider, { value: client, children });
8
+ }
9
+ function useCmsClient() {
10
+ const client = useContext(CmsClientContext);
11
+ if (!client) {
12
+ throw new Error("useCmsClient must be used within a <CmsProvider client={...}>");
13
+ }
14
+ return client;
15
+ }
16
+ var DEFAULT_STORAGE_KEY = "utilsy_cms_visitor_id";
17
+ function useVisitorId(options) {
18
+ const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;
19
+ const [visitorId, setVisitorId] = useState("");
20
+ useEffect(() => {
21
+ if (typeof window === "undefined") return;
22
+ let id = localStorage.getItem(storageKey);
23
+ if (!id) {
24
+ id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;
25
+ localStorage.setItem(storageKey, id);
26
+ }
27
+ setVisitorId(id);
28
+ }, [storageKey]);
29
+ return visitorId;
30
+ }
31
+ function getVisitorId(storageKey = DEFAULT_STORAGE_KEY) {
32
+ if (typeof window === "undefined") return "";
33
+ let id = localStorage.getItem(storageKey);
34
+ if (!id) {
35
+ id = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;
36
+ localStorage.setItem(storageKey, id);
37
+ }
38
+ return id;
39
+ }
40
+ function useBlogEngagement(postId, options) {
41
+ const client = useCmsClient();
42
+ const [engagement, setEngagement] = useState(null);
43
+ const [loading, setLoading] = useState(true);
44
+ const [error, setError] = useState(null);
45
+ const refetch = useCallback(async () => {
46
+ if (!postId) return;
47
+ setLoading(true);
48
+ setError(null);
49
+ try {
50
+ const visitorId = options?.visitorId ?? (typeof window !== "undefined" ? getVisitorId(options?.storageKey) : void 0);
51
+ const data = await client.blog.getEngagement(postId, { visitorId });
52
+ setEngagement(data);
53
+ } catch (e) {
54
+ setError(e instanceof Error ? e : new Error(String(e)));
55
+ } finally {
56
+ setLoading(false);
57
+ }
58
+ }, [client, postId, options?.visitorId, options?.storageKey]);
59
+ useEffect(() => {
60
+ refetch();
61
+ }, [refetch]);
62
+ return { engagement, loading, error, refetch };
63
+ }
64
+ function useBlogLike(postId, options) {
65
+ const client = useCmsClient();
66
+ const { engagement, loading: engagementLoading, refetch } = useBlogEngagement(postId, options);
67
+ const [toggling, setToggling] = useState(false);
68
+ const [error, setError] = useState(null);
69
+ const toggle = useCallback(async () => {
70
+ if (!postId) return;
71
+ setToggling(true);
72
+ setError(null);
73
+ try {
74
+ const visitorId = options?.visitorId ?? (typeof window !== "undefined" ? getVisitorId(options?.storageKey) : void 0);
75
+ await client.blog.toggleLike(postId, { visitorId });
76
+ await refetch();
77
+ } catch (e) {
78
+ setError(e instanceof Error ? e : new Error(String(e)));
79
+ } finally {
80
+ setToggling(false);
81
+ }
82
+ }, [client, postId, options?.visitorId, options?.storageKey, refetch]);
83
+ return {
84
+ liked: engagement?.likedByMe ?? false,
85
+ likeCount: engagement?.likeCount ?? 0,
86
+ viewCount: engagement?.viewCount ?? 0,
87
+ commentCount: engagement?.commentCount ?? 0,
88
+ loading: engagementLoading,
89
+ toggling,
90
+ error,
91
+ toggle,
92
+ refetch
93
+ };
94
+ }
95
+ function useBlogComments(postId) {
96
+ const client = useCmsClient();
97
+ const [comments, setComments] = useState([]);
98
+ const [loading, setLoading] = useState(true);
99
+ const [submitting, setSubmitting] = useState(false);
100
+ const [error, setError] = useState(null);
101
+ const refetch = useCallback(async () => {
102
+ if (!postId) return;
103
+ setLoading(true);
104
+ setError(null);
105
+ try {
106
+ const data = await client.blog.listMappedComments(postId);
107
+ setComments(data ?? []);
108
+ } catch (e) {
109
+ setError(e instanceof Error ? e : new Error(String(e)));
110
+ } finally {
111
+ setLoading(false);
112
+ }
113
+ }, [client, postId]);
114
+ useEffect(() => {
115
+ refetch();
116
+ }, [refetch]);
117
+ const submitComment = useCallback(
118
+ async (input) => {
119
+ if (!postId) return;
120
+ setSubmitting(true);
121
+ setError(null);
122
+ try {
123
+ await client.blog.createComment(postId, input);
124
+ await refetch();
125
+ } catch (e) {
126
+ const err = e instanceof Error ? e : new Error(String(e));
127
+ setError(err);
128
+ throw err;
129
+ } finally {
130
+ setSubmitting(false);
131
+ }
132
+ },
133
+ [client, postId, refetch]
134
+ );
135
+ return { comments, loading, submitting, error, refetch, submitComment };
136
+ }
137
+
138
+ export { CmsProvider, getVisitorId, useBlogComments, useBlogEngagement, useBlogLike, useCmsClient, useVisitorId };
139
+ //# sourceMappingURL=react.js.map
140
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/context.ts","../src/react/provider.tsx","../src/react/use-cms-client.ts","../src/react/use-visitor-id.ts","../src/react/use-blog-engagement.ts","../src/react/use-blog-like.ts","../src/react/use-blog-comments.ts"],"names":["useState","useEffect","useCallback"],"mappings":"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","file":"react.js","sourcesContent":["import { createContext } from \"react\";\nimport type { CmsClient } from \"../client.js\";\n\nexport const CmsClientContext = createContext<CmsClient | null>(null);\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport type { CmsClient } from \"../client.js\";\nimport { CmsClientContext } from \"./context.js\";\n\nexport type CmsProviderProps = {\n client: CmsClient;\n children: ReactNode;\n};\n\nexport function CmsProvider({ client, children }: CmsProviderProps) {\n return <CmsClientContext.Provider value={client}>{children}</CmsClientContext.Provider>;\n}\n","\"use client\";\n\nimport { useContext } from \"react\";\nimport type { CmsClient } from \"../client.js\";\nimport { CmsClientContext } from \"./context.js\";\n\nexport function useCmsClient(): CmsClient {\n const client = useContext(CmsClientContext);\n if (!client) {\n throw new Error(\"useCmsClient must be used within a <CmsProvider client={...}>\");\n }\n return client;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nconst DEFAULT_STORAGE_KEY = \"utilsy_cms_visitor_id\";\n\nexport type UseVisitorIdOptions = {\n storageKey?: string;\n};\n\nexport function useVisitorId(options?: UseVisitorIdOptions): string {\n const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;\n const [visitorId, setVisitorId] = useState(\"\");\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n let id = localStorage.getItem(storageKey);\n if (!id) {\n id =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n localStorage.setItem(storageKey, id);\n }\n setVisitorId(id);\n }, [storageKey]);\n\n return visitorId;\n}\n\n/** Non-hook helper for event handlers */\nexport function getVisitorId(storageKey = DEFAULT_STORAGE_KEY): string {\n if (typeof window === \"undefined\") return \"\";\n let id = localStorage.getItem(storageKey);\n if (!id) {\n id =\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : `v-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n localStorage.setItem(storageKey, id);\n }\n return id;\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { BlogEngagement } from \"../blog/types.js\";\nimport { useCmsClient } from \"./use-cms-client.js\";\nimport { getVisitorId } from \"./use-visitor-id.js\";\n\nexport type UseBlogEngagementOptions = {\n visitorId?: string;\n storageKey?: string;\n};\n\nexport function useBlogEngagement(postId: string, options?: UseBlogEngagementOptions) {\n const client = useCmsClient();\n const [engagement, setEngagement] = useState<BlogEngagement | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const refetch = useCallback(async () => {\n if (!postId) return;\n setLoading(true);\n setError(null);\n try {\n const visitorId =\n options?.visitorId ??\n (typeof window !== \"undefined\" ? getVisitorId(options?.storageKey) : undefined);\n const data = await client.blog.getEngagement(postId, { visitorId });\n setEngagement(data);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [client, postId, options?.visitorId, options?.storageKey]);\n\n useEffect(() => {\n refetch();\n }, [refetch]);\n\n return { engagement, loading, error, refetch };\n}\n","\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport { useBlogEngagement } from \"./use-blog-engagement.js\";\nimport { useCmsClient } from \"./use-cms-client.js\";\nimport { getVisitorId } from \"./use-visitor-id.js\";\n\nexport type UseBlogLikeOptions = {\n visitorId?: string;\n storageKey?: string;\n};\n\nexport function useBlogLike(postId: string, options?: UseBlogLikeOptions) {\n const client = useCmsClient();\n const { engagement, loading: engagementLoading, refetch } = useBlogEngagement(postId, options);\n const [toggling, setToggling] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const toggle = useCallback(async () => {\n if (!postId) return;\n setToggling(true);\n setError(null);\n try {\n const visitorId =\n options?.visitorId ??\n (typeof window !== \"undefined\" ? getVisitorId(options?.storageKey) : undefined);\n await client.blog.toggleLike(postId, { visitorId });\n await refetch();\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setToggling(false);\n }\n }, [client, postId, options?.visitorId, options?.storageKey, refetch]);\n\n return {\n liked: engagement?.likedByMe ?? false,\n likeCount: engagement?.likeCount ?? 0,\n viewCount: engagement?.viewCount ?? 0,\n commentCount: engagement?.commentCount ?? 0,\n loading: engagementLoading,\n toggling,\n error,\n toggle,\n refetch,\n };\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport type { BlogComment, CreateBlogCommentInput } from \"../blog/types.js\";\nimport { useCmsClient } from \"./use-cms-client.js\";\n\nexport function useBlogComments(postId: string) {\n const client = useCmsClient();\n const [comments, setComments] = useState<BlogComment[]>([]);\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const refetch = useCallback(async () => {\n if (!postId) return;\n setLoading(true);\n setError(null);\n try {\n const data = await client.blog.listMappedComments(postId);\n setComments(data ?? []);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n } finally {\n setLoading(false);\n }\n }, [client, postId]);\n\n useEffect(() => {\n refetch();\n }, [refetch]);\n\n const submitComment = useCallback(\n async (input: CreateBlogCommentInput) => {\n if (!postId) return;\n setSubmitting(true);\n setError(null);\n try {\n await client.blog.createComment(postId, input);\n await refetch();\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setError(err);\n throw err;\n } finally {\n setSubmitting(false);\n }\n },\n [client, postId, refetch],\n );\n\n return { comments, loading, submitting, error, refetch, submitComment };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@utilsy/cms-nextjs",
3
+ "version": "0.1.0",
4
+ "description": "Headless Next.js SDK for Utilsy gateway-cms public blog APIs",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ },
22
+ "./react": {
23
+ "import": {
24
+ "types": "./dist/react.d.ts",
25
+ "default": "./dist/react.js"
26
+ },
27
+ "require": {
28
+ "types": "./dist/react.d.cts",
29
+ "default": "./dist/react.cjs"
30
+ }
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "typecheck": "tsc --noEmit",
43
+ "prepublishOnly": "npm run build && npm test"
44
+ },
45
+ "peerDependencies": {
46
+ "next": ">=14.0.0",
47
+ "react": ">=18.0.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "next": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@testing-library/react": "^16.3.0",
56
+ "@types/react": "^19.0.0",
57
+ "jsdom": "^26.1.0",
58
+ "react": "^19.0.0",
59
+ "react-dom": "^19.0.0",
60
+ "tsup": "^8.5.0",
61
+ "typescript": "^5.8.0",
62
+ "vitest": "^3.2.0"
63
+ },
64
+ "keywords": [
65
+ "utilsy",
66
+ "cms",
67
+ "nextjs",
68
+ "blog",
69
+ "headless"
70
+ ],
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "https://github.com/utilsy/utilsy-cms-nextjs-sdk"
74
+ }
75
+ }