chaingrow-blog 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/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # chaingrow-blog
2
+
3
+ Drop-in Next.js SDK for rendering blogs published from [ChainGrow](https://chaingrow.ai).
4
+
5
+ - **Zero database.** Your site stores nothing — ChainGrow is the source of truth.
6
+ - **Instant publishing.** When you press Publish in ChainGrow, the SDK's webhook handler invalidates the relevant page via `revalidateTag`. Next request serves fresh HTML.
7
+ - **Headless-first.** The core SDK is unstyled semantic HTML. Supply your own components for any block type you want to customize.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add chaingrow-blog
13
+ # or
14
+ npm install chaingrow-blog
15
+ ```
16
+
17
+ Requires Next.js ≥ 14 (App Router) and React ≥ 18.
18
+
19
+ ## Setup (3 files)
20
+
21
+ ### 1. Environment variables
22
+
23
+ ```env
24
+ CHAINGROW_API_URL=https://api.chaingrow.ai
25
+ CHAINGROW_API_KEY=ck_live_xxx
26
+ CHAINGROW_WEBHOOK_SECRET=whsec_xxx
27
+ ```
28
+
29
+ Generate the API key + webhook secret in your ChainGrow dashboard under **Settings → API Keys**. Set the webhook URL on the key to `https://your-site.com/api/chaingrow/webhook`.
30
+
31
+ ### 2. Webhook receiver
32
+
33
+ ```ts
34
+ // app/api/chaingrow/webhook/route.ts
35
+ export { POST } from 'chaingrow-blog/next/webhook';
36
+ ```
37
+
38
+ ### 3. Blog page
39
+
40
+ **Option A — full design control:**
41
+
42
+ ```tsx
43
+ // app/blog/[slug]/page.tsx
44
+ import { notFound } from 'next/navigation';
45
+ import { getBlog } from 'chaingrow-blog/next';
46
+
47
+ export default async function BlogPage({
48
+ params,
49
+ }: {
50
+ params: { slug: string };
51
+ }) {
52
+ const blog = await getBlog(params.slug);
53
+ if (!blog) notFound();
54
+
55
+ return (
56
+ <article>
57
+ <h1>{blog.title}</h1>
58
+ {blog.blocks.map((b, i) => (
59
+ /* render however you want */
60
+ <YourCustomBlock key={i} block={b} />
61
+ ))}
62
+ </article>
63
+ );
64
+ }
65
+ ```
66
+
67
+ **Option B — default renderer with per-block overrides:**
68
+
69
+ ```tsx
70
+ import { notFound } from 'next/navigation';
71
+ import { getBlog } from 'chaingrow-blog/next';
72
+ import { BlogRenderer } from 'chaingrow-blog/react';
73
+
74
+ export default async function BlogPage({
75
+ params,
76
+ }: {
77
+ params: { slug: string };
78
+ }) {
79
+ const blog = await getBlog(params.slug);
80
+ if (!blog) notFound();
81
+
82
+ return (
83
+ <article className="prose">
84
+ <h1>{blog.title}</h1>
85
+ <BlogRenderer
86
+ blog={blog}
87
+ components={{
88
+ heading: ({ level, children }) =>
89
+ level === 2 ? (
90
+ <h2 className="text-3xl font-bold">{children}</h2>
91
+ ) : (
92
+ <h3 className="text-2xl font-semibold">{children}</h3>
93
+ ),
94
+ quote: ({ children }) => (
95
+ <blockquote className="border-l-4 border-blue-500 pl-4 italic">
96
+ {children}
97
+ </blockquote>
98
+ ),
99
+ }}
100
+ />
101
+ </article>
102
+ );
103
+ }
104
+ ```
105
+
106
+ ## API
107
+
108
+ ### `getBlog(slug, language?, opts?)`
109
+
110
+ Fetches a blog by slug (server-side, ISR-cached under tag `chaingrow:blog:<slug>`). Returns `null` if not found.
111
+
112
+ ### `listBlogs(opts?, clientOpts?)`
113
+
114
+ Lists blogs (optionally filtered by `status` or `language`).
115
+
116
+ ### `createClient({ apiUrl, apiKey })`
117
+
118
+ Framework-agnostic client. Use from edge functions, scripts, or non-Next.js environments.
119
+
120
+ ### `<BlogRenderer blog={blog} components={...} />`
121
+
122
+ Renders all `blog.blocks` using unstyled default components. Every block type is overridable via the `components` prop — supply only the ones you want to customize.
123
+
124
+ Block types: `heading`, `paragraph`, `list`, `quote`, `code`, `image_placeholder`.
125
+
126
+ ### `createWebhookHandler({ secret?, onEvent? })`
127
+
128
+ Returns a `{ POST }` route handler. Verifies the `X-ChainGrow-Signature` HMAC and calls `revalidateTag`. Pass `onEvent` to run custom logic after revalidation (e.g. cache warming, analytics).
129
+
130
+ ## How publishing works
131
+
132
+ ```
133
+ ChainGrow dashboard Your Next.js site
134
+ ─────────────────── ─────────────────
135
+ Press Publish
136
+
137
+
138
+ blog.status = PUBLISHED
139
+
140
+
141
+ HMAC-signed webhook ──────────────► /api/chaingrow/webhook
142
+
143
+
144
+ verify signature
145
+
146
+
147
+ revalidateTag('chaingrow:blog:<slug>')
148
+
149
+
150
+ Next user request /blog/<slug> cache miss
151
+ │ │
152
+ ▼ ▼
153
+ Fresh HTML getBlog() → GET /api/v1/blogs/by-slug/<slug>
154
+
155
+
156
+ cached for next visitors
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT
@@ -0,0 +1,52 @@
1
+ // src/client.ts
2
+ function createClient(opts) {
3
+ const baseUrl = opts.apiUrl.replace(/\/$/, "");
4
+ const doFetch = opts.fetch ?? fetch;
5
+ const request = async (path, init) => {
6
+ const res = await doFetch(`${baseUrl}${path}`, {
7
+ ...init,
8
+ headers: {
9
+ "X-API-Key": opts.apiKey,
10
+ Accept: "application/json",
11
+ ...init?.headers ?? {}
12
+ }
13
+ });
14
+ if (res.status === 404) return null;
15
+ if (!res.ok) {
16
+ throw new Error(
17
+ `chaingrow-blog: ${init?.method ?? "GET"} ${path} failed (${res.status})`
18
+ );
19
+ }
20
+ return await res.json();
21
+ };
22
+ return {
23
+ async getBlog(slug, language) {
24
+ const qs = language ? `?language=${encodeURIComponent(language)}` : "";
25
+ return request(
26
+ `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`
27
+ );
28
+ },
29
+ async getBlogById(id) {
30
+ return request(`/api/v1/blogs/${encodeURIComponent(id)}`);
31
+ },
32
+ async listBlogs(listOpts = {}) {
33
+ const params = new URLSearchParams();
34
+ if (listOpts.status) params.set("status", listOpts.status);
35
+ const qs = params.toString();
36
+ const response = await request(
37
+ `/api/v1/blogs${qs ? `?${qs}` : ""}`
38
+ );
39
+ if (!response) return [];
40
+ const blogs = response.data ?? [];
41
+ if (listOpts.language) {
42
+ return blogs.filter((b) => b.language === listOpts.language);
43
+ }
44
+ return blogs;
45
+ }
46
+ };
47
+ }
48
+
49
+ export {
50
+ createClient
51
+ };
52
+ //# sourceMappingURL=chunk-TDHCPX4T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type { Blog } from './types';\n\nexport type ClientOptions = {\n apiUrl: string;\n apiKey: string;\n fetch?: typeof fetch;\n};\n\nexport type ListBlogsOptions = {\n status?: 'DRAFT' | 'PUBLISHED';\n language?: string;\n};\n\nexport type ChaingrowClient = {\n getBlog(slug: string, language?: string): Promise<Blog | null>;\n getBlogById(id: string): Promise<Blog | null>;\n listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;\n};\n\nexport function createClient(opts: ClientOptions): ChaingrowClient {\n const baseUrl = opts.apiUrl.replace(/\\/$/, '');\n const doFetch = opts.fetch ?? fetch;\n\n const request = async <T>(\n path: string,\n init?: RequestInit,\n ): Promise<T | null> => {\n const res = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n 'X-API-Key': opts.apiKey,\n Accept: 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(\n `chaingrow-blog: ${init?.method ?? 'GET'} ${path} failed (${res.status})`,\n );\n }\n return (await res.json()) as T;\n };\n\n return {\n async getBlog(slug, language) {\n const qs = language ? `?language=${encodeURIComponent(language)}` : '';\n return request<Blog>(\n `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`,\n );\n },\n\n async getBlogById(id) {\n return request<Blog>(`/api/v1/blogs/${encodeURIComponent(id)}`);\n },\n\n async listBlogs(listOpts = {}) {\n const params = new URLSearchParams();\n if (listOpts.status) params.set('status', listOpts.status);\n const qs = params.toString();\n const response = await request<{ data: Blog[]; total: number }>(\n `/api/v1/blogs${qs ? `?${qs}` : ''}`,\n );\n if (!response) return [];\n const blogs = response.data ?? [];\n if (listOpts.language) {\n return blogs.filter((b) => b.language === listOpts.language);\n }\n return blogs;\n },\n };\n}\n"],"mappings":";AAmBO,SAAS,aAAa,MAAsC;AACjE,QAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,EAAE;AAC7C,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,UAAU,OACd,MACA,SACsB;AACtB,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,IAAI;AAAA,MAC7C,GAAG;AAAA,MACH,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,QACR,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,UAAU,KAAK,IAAI,IAAI,YAAY,IAAI,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,UAAU;AAC5B,YAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,aAAO;AAAA,QACL,yBAAyB,mBAAmB,IAAI,CAAC,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,IAAI;AACpB,aAAO,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,EAAE;AAAA,IAChE;AAAA,IAEA,MAAM,UAAU,WAAW,CAAC,GAAG;AAC7B,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,SAAS,MAAM;AACzD,YAAM,KAAK,OAAO,SAAS;AAC3B,YAAM,WAAW,MAAM;AAAA,QACrB,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACpC;AACA,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,QAAQ,SAAS,QAAQ,CAAC;AAChC,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,QAAQ;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
package/dist/index.cjs ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ createClient: () => createClient
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/client.ts
28
+ function createClient(opts) {
29
+ const baseUrl = opts.apiUrl.replace(/\/$/, "");
30
+ const doFetch = opts.fetch ?? fetch;
31
+ const request = async (path, init) => {
32
+ const res = await doFetch(`${baseUrl}${path}`, {
33
+ ...init,
34
+ headers: {
35
+ "X-API-Key": opts.apiKey,
36
+ Accept: "application/json",
37
+ ...init?.headers ?? {}
38
+ }
39
+ });
40
+ if (res.status === 404) return null;
41
+ if (!res.ok) {
42
+ throw new Error(
43
+ `chaingrow-blog: ${init?.method ?? "GET"} ${path} failed (${res.status})`
44
+ );
45
+ }
46
+ return await res.json();
47
+ };
48
+ return {
49
+ async getBlog(slug, language) {
50
+ const qs = language ? `?language=${encodeURIComponent(language)}` : "";
51
+ return request(
52
+ `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`
53
+ );
54
+ },
55
+ async getBlogById(id) {
56
+ return request(`/api/v1/blogs/${encodeURIComponent(id)}`);
57
+ },
58
+ async listBlogs(listOpts = {}) {
59
+ const params = new URLSearchParams();
60
+ if (listOpts.status) params.set("status", listOpts.status);
61
+ const qs = params.toString();
62
+ const response = await request(
63
+ `/api/v1/blogs${qs ? `?${qs}` : ""}`
64
+ );
65
+ if (!response) return [];
66
+ const blogs = response.data ?? [];
67
+ if (listOpts.language) {
68
+ return blogs.filter((b) => b.language === listOpts.language);
69
+ }
70
+ return blogs;
71
+ }
72
+ };
73
+ }
74
+ // Annotate the CommonJS export names for ESM import in node:
75
+ 0 && (module.exports = {
76
+ createClient
77
+ });
78
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export * from './types';\nexport { createClient, type ChaingrowClient, type ClientOptions } from './client';\n","import type { Blog } from './types';\n\nexport type ClientOptions = {\n apiUrl: string;\n apiKey: string;\n fetch?: typeof fetch;\n};\n\nexport type ListBlogsOptions = {\n status?: 'DRAFT' | 'PUBLISHED';\n language?: string;\n};\n\nexport type ChaingrowClient = {\n getBlog(slug: string, language?: string): Promise<Blog | null>;\n getBlogById(id: string): Promise<Blog | null>;\n listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;\n};\n\nexport function createClient(opts: ClientOptions): ChaingrowClient {\n const baseUrl = opts.apiUrl.replace(/\\/$/, '');\n const doFetch = opts.fetch ?? fetch;\n\n const request = async <T>(\n path: string,\n init?: RequestInit,\n ): Promise<T | null> => {\n const res = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n 'X-API-Key': opts.apiKey,\n Accept: 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(\n `chaingrow-blog: ${init?.method ?? 'GET'} ${path} failed (${res.status})`,\n );\n }\n return (await res.json()) as T;\n };\n\n return {\n async getBlog(slug, language) {\n const qs = language ? `?language=${encodeURIComponent(language)}` : '';\n return request<Blog>(\n `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`,\n );\n },\n\n async getBlogById(id) {\n return request<Blog>(`/api/v1/blogs/${encodeURIComponent(id)}`);\n },\n\n async listBlogs(listOpts = {}) {\n const params = new URLSearchParams();\n if (listOpts.status) params.set('status', listOpts.status);\n const qs = params.toString();\n const response = await request<{ data: Blog[]; total: number }>(\n `/api/v1/blogs${qs ? `?${qs}` : ''}`,\n );\n if (!response) return [];\n const blogs = response.data ?? [];\n if (listOpts.language) {\n return blogs.filter((b) => b.language === listOpts.language);\n }\n return blogs;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,aAAa,MAAsC;AACjE,QAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,EAAE;AAC7C,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,UAAU,OACd,MACA,SACsB;AACtB,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,IAAI;AAAA,MAC7C,GAAG;AAAA,MACH,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,QACR,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,UAAU,KAAK,IAAI,IAAI,YAAY,IAAI,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,UAAU;AAC5B,YAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,aAAO;AAAA,QACL,yBAAyB,mBAAmB,IAAI,CAAC,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,IAAI;AACpB,aAAO,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,EAAE;AAAA,IAChE;AAAA,IAEA,MAAM,UAAU,WAAW,CAAC,GAAG;AAC7B,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,SAAS,MAAM;AACzD,YAAM,KAAK,OAAO,SAAS;AAC3B,YAAM,WAAW,MAAM;AAAA,QACrB,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACpC;AACA,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,QAAQ,SAAS,QAAQ,CAAC;AAChC,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,QAAQ;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,20 @@
1
+ import { B as Blog } from './types-CoPD0ejB.cjs';
2
+ export { a as Block, F as FaqItem, S as SocialPost, W as WebhookEvent, b as WebhookPayload } from './types-CoPD0ejB.cjs';
3
+
4
+ type ClientOptions = {
5
+ apiUrl: string;
6
+ apiKey: string;
7
+ fetch?: typeof fetch;
8
+ };
9
+ type ListBlogsOptions = {
10
+ status?: 'DRAFT' | 'PUBLISHED';
11
+ language?: string;
12
+ };
13
+ type ChaingrowClient = {
14
+ getBlog(slug: string, language?: string): Promise<Blog | null>;
15
+ getBlogById(id: string): Promise<Blog | null>;
16
+ listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;
17
+ };
18
+ declare function createClient(opts: ClientOptions): ChaingrowClient;
19
+
20
+ export { Blog, type ChaingrowClient, type ClientOptions, createClient };
@@ -0,0 +1,20 @@
1
+ import { B as Blog } from './types-CoPD0ejB.js';
2
+ export { a as Block, F as FaqItem, S as SocialPost, W as WebhookEvent, b as WebhookPayload } from './types-CoPD0ejB.js';
3
+
4
+ type ClientOptions = {
5
+ apiUrl: string;
6
+ apiKey: string;
7
+ fetch?: typeof fetch;
8
+ };
9
+ type ListBlogsOptions = {
10
+ status?: 'DRAFT' | 'PUBLISHED';
11
+ language?: string;
12
+ };
13
+ type ChaingrowClient = {
14
+ getBlog(slug: string, language?: string): Promise<Blog | null>;
15
+ getBlogById(id: string): Promise<Blog | null>;
16
+ listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;
17
+ };
18
+ declare function createClient(opts: ClientOptions): ChaingrowClient;
19
+
20
+ export { Blog, type ChaingrowClient, type ClientOptions, createClient };
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ createClient
3
+ } from "./chunk-TDHCPX4T.js";
4
+ export {
5
+ createClient
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/next/index.ts
21
+ var next_exports = {};
22
+ __export(next_exports, {
23
+ getBlog: () => getBlog,
24
+ listBlogs: () => listBlogs
25
+ });
26
+ module.exports = __toCommonJS(next_exports);
27
+
28
+ // src/client.ts
29
+ function createClient(opts) {
30
+ const baseUrl = opts.apiUrl.replace(/\/$/, "");
31
+ const doFetch = opts.fetch ?? fetch;
32
+ const request = async (path, init) => {
33
+ const res = await doFetch(`${baseUrl}${path}`, {
34
+ ...init,
35
+ headers: {
36
+ "X-API-Key": opts.apiKey,
37
+ Accept: "application/json",
38
+ ...init?.headers ?? {}
39
+ }
40
+ });
41
+ if (res.status === 404) return null;
42
+ if (!res.ok) {
43
+ throw new Error(
44
+ `chaingrow-blog: ${init?.method ?? "GET"} ${path} failed (${res.status})`
45
+ );
46
+ }
47
+ return await res.json();
48
+ };
49
+ return {
50
+ async getBlog(slug, language) {
51
+ const qs = language ? `?language=${encodeURIComponent(language)}` : "";
52
+ return request(
53
+ `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`
54
+ );
55
+ },
56
+ async getBlogById(id) {
57
+ return request(`/api/v1/blogs/${encodeURIComponent(id)}`);
58
+ },
59
+ async listBlogs(listOpts = {}) {
60
+ const params = new URLSearchParams();
61
+ if (listOpts.status) params.set("status", listOpts.status);
62
+ const qs = params.toString();
63
+ const response = await request(
64
+ `/api/v1/blogs${qs ? `?${qs}` : ""}`
65
+ );
66
+ if (!response) return [];
67
+ const blogs = response.data ?? [];
68
+ if (listOpts.language) {
69
+ return blogs.filter((b) => b.language === listOpts.language);
70
+ }
71
+ return blogs;
72
+ }
73
+ };
74
+ }
75
+
76
+ // src/next/index.ts
77
+ var resolveEnv = (opts = {}) => {
78
+ const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;
79
+ const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;
80
+ if (!apiUrl) {
81
+ throw new Error(
82
+ "chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)"
83
+ );
84
+ }
85
+ if (!apiKey) {
86
+ throw new Error(
87
+ "chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)"
88
+ );
89
+ }
90
+ return { apiUrl, apiKey };
91
+ };
92
+ var buildFetch = (tag, revalidate) => {
93
+ return ((input, init) => fetch(input, {
94
+ ...init,
95
+ next: {
96
+ tags: ["chaingrow:blog", tag],
97
+ ...revalidate === false ? { revalidate: false } : revalidate !== void 0 ? { revalidate } : {}
98
+ }
99
+ }));
100
+ };
101
+ async function getBlog(slug, language, opts = {}) {
102
+ const { apiUrl, apiKey } = resolveEnv(opts);
103
+ const client = createClient({
104
+ apiUrl,
105
+ apiKey,
106
+ fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate)
107
+ });
108
+ return client.getBlog(slug, language);
109
+ }
110
+ async function listBlogs(listOpts = {}, opts = {}) {
111
+ const { apiUrl, apiKey } = resolveEnv(opts);
112
+ const client = createClient({
113
+ apiUrl,
114
+ apiKey,
115
+ fetch: buildFetch("chaingrow:blog:list", opts.revalidate)
116
+ });
117
+ return client.listBlogs(listOpts);
118
+ }
119
+ // Annotate the CommonJS export names for ESM import in node:
120
+ 0 && (module.exports = {
121
+ getBlog,
122
+ listBlogs
123
+ });
124
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/next/index.ts","../../src/client.ts"],"sourcesContent":["import type { Blog } from '../types';\nimport { createClient } from '../client';\n\nexport type NextClientOptions = {\n apiUrl?: string;\n apiKey?: string;\n revalidate?: number | false;\n};\n\nconst resolveEnv = (opts: NextClientOptions = {}) => {\n const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;\n const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;\n if (!apiUrl) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)',\n );\n }\n if (!apiKey) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)',\n );\n }\n return { apiUrl, apiKey };\n};\n\nconst buildFetch = (tag: string, revalidate: number | false | undefined) => {\n return ((input: RequestInfo | URL, init?: RequestInit) =>\n fetch(input, {\n ...init,\n next: {\n tags: ['chaingrow:blog', tag],\n ...(revalidate === false\n ? { revalidate: false as const }\n : revalidate !== undefined\n ? { revalidate }\n : {}),\n },\n } as RequestInit)) as typeof fetch;\n};\n\nexport async function getBlog(\n slug: string,\n language?: string,\n opts: NextClientOptions = {},\n): Promise<Blog | null> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate),\n });\n return client.getBlog(slug, language);\n}\n\nexport async function listBlogs(\n listOpts: { status?: 'DRAFT' | 'PUBLISHED'; language?: string } = {},\n opts: NextClientOptions = {},\n): Promise<Blog[]> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch('chaingrow:blog:list', opts.revalidate),\n });\n return client.listBlogs(listOpts);\n}\n\nexport type { Blog } from '../types';\n","import type { Blog } from './types';\n\nexport type ClientOptions = {\n apiUrl: string;\n apiKey: string;\n fetch?: typeof fetch;\n};\n\nexport type ListBlogsOptions = {\n status?: 'DRAFT' | 'PUBLISHED';\n language?: string;\n};\n\nexport type ChaingrowClient = {\n getBlog(slug: string, language?: string): Promise<Blog | null>;\n getBlogById(id: string): Promise<Blog | null>;\n listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;\n};\n\nexport function createClient(opts: ClientOptions): ChaingrowClient {\n const baseUrl = opts.apiUrl.replace(/\\/$/, '');\n const doFetch = opts.fetch ?? fetch;\n\n const request = async <T>(\n path: string,\n init?: RequestInit,\n ): Promise<T | null> => {\n const res = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n 'X-API-Key': opts.apiKey,\n Accept: 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(\n `chaingrow-blog: ${init?.method ?? 'GET'} ${path} failed (${res.status})`,\n );\n }\n return (await res.json()) as T;\n };\n\n return {\n async getBlog(slug, language) {\n const qs = language ? `?language=${encodeURIComponent(language)}` : '';\n return request<Blog>(\n `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`,\n );\n },\n\n async getBlogById(id) {\n return request<Blog>(`/api/v1/blogs/${encodeURIComponent(id)}`);\n },\n\n async listBlogs(listOpts = {}) {\n const params = new URLSearchParams();\n if (listOpts.status) params.set('status', listOpts.status);\n const qs = params.toString();\n const response = await request<{ data: Blog[]; total: number }>(\n `/api/v1/blogs${qs ? `?${qs}` : ''}`,\n );\n if (!response) return [];\n const blogs = response.data ?? [];\n if (listOpts.language) {\n return blogs.filter((b) => b.language === listOpts.language);\n }\n return blogs;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,aAAa,MAAsC;AACjE,QAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,EAAE;AAC7C,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,UAAU,OACd,MACA,SACsB;AACtB,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,IAAI;AAAA,MAC7C,GAAG;AAAA,MACH,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,QACR,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,UAAU,KAAK,IAAI,IAAI,YAAY,IAAI,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,UAAU;AAC5B,YAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,aAAO;AAAA,QACL,yBAAyB,mBAAmB,IAAI,CAAC,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,IAAI;AACpB,aAAO,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,EAAE;AAAA,IAChE;AAAA,IAEA,MAAM,UAAU,WAAW,CAAC,GAAG;AAC7B,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,SAAS,MAAM;AACzD,YAAM,KAAK,OAAO,SAAS;AAC3B,YAAM,WAAW,MAAM;AAAA,QACrB,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACpC;AACA,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,QAAQ,SAAS,QAAQ,CAAC;AAChC,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,QAAQ;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AD9DA,IAAM,aAAa,CAAC,OAA0B,CAAC,MAAM;AACnD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAEA,IAAM,aAAa,CAAC,KAAa,eAA2C;AAC1E,UAAQ,CAAC,OAA0B,SACjC,MAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,MAAM;AAAA,MACJ,MAAM,CAAC,kBAAkB,GAAG;AAAA,MAC5B,GAAI,eAAe,QACf,EAAE,YAAY,MAAe,IAC7B,eAAe,SACb,EAAE,WAAW,IACb,CAAC;AAAA,IACT;AAAA,EACF,CAAgB;AACpB;AAEA,eAAsB,QACpB,MACA,UACA,OAA0B,CAAC,GACL;AACtB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,kBAAkB,IAAI,IAAI,KAAK,UAAU;AAAA,EAC7D,CAAC;AACD,SAAO,OAAO,QAAQ,MAAM,QAAQ;AACtC;AAEA,eAAsB,UACpB,WAAkE,CAAC,GACnE,OAA0B,CAAC,GACV;AACjB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,uBAAuB,KAAK,UAAU;AAAA,EAC1D,CAAC;AACD,SAAO,OAAO,UAAU,QAAQ;AAClC;","names":[]}
@@ -0,0 +1,14 @@
1
+ import { B as Blog } from '../types-CoPD0ejB.cjs';
2
+
3
+ type NextClientOptions = {
4
+ apiUrl?: string;
5
+ apiKey?: string;
6
+ revalidate?: number | false;
7
+ };
8
+ declare function getBlog(slug: string, language?: string, opts?: NextClientOptions): Promise<Blog | null>;
9
+ declare function listBlogs(listOpts?: {
10
+ status?: 'DRAFT' | 'PUBLISHED';
11
+ language?: string;
12
+ }, opts?: NextClientOptions): Promise<Blog[]>;
13
+
14
+ export { Blog, type NextClientOptions, getBlog, listBlogs };
@@ -0,0 +1,14 @@
1
+ import { B as Blog } from '../types-CoPD0ejB.js';
2
+
3
+ type NextClientOptions = {
4
+ apiUrl?: string;
5
+ apiKey?: string;
6
+ revalidate?: number | false;
7
+ };
8
+ declare function getBlog(slug: string, language?: string, opts?: NextClientOptions): Promise<Blog | null>;
9
+ declare function listBlogs(listOpts?: {
10
+ status?: 'DRAFT' | 'PUBLISHED';
11
+ language?: string;
12
+ }, opts?: NextClientOptions): Promise<Blog[]>;
13
+
14
+ export { Blog, type NextClientOptions, getBlog, listBlogs };
@@ -0,0 +1,52 @@
1
+ import {
2
+ createClient
3
+ } from "../chunk-TDHCPX4T.js";
4
+
5
+ // src/next/index.ts
6
+ var resolveEnv = (opts = {}) => {
7
+ const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;
8
+ const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;
9
+ if (!apiUrl) {
10
+ throw new Error(
11
+ "chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)"
12
+ );
13
+ }
14
+ if (!apiKey) {
15
+ throw new Error(
16
+ "chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)"
17
+ );
18
+ }
19
+ return { apiUrl, apiKey };
20
+ };
21
+ var buildFetch = (tag, revalidate) => {
22
+ return ((input, init) => fetch(input, {
23
+ ...init,
24
+ next: {
25
+ tags: ["chaingrow:blog", tag],
26
+ ...revalidate === false ? { revalidate: false } : revalidate !== void 0 ? { revalidate } : {}
27
+ }
28
+ }));
29
+ };
30
+ async function getBlog(slug, language, opts = {}) {
31
+ const { apiUrl, apiKey } = resolveEnv(opts);
32
+ const client = createClient({
33
+ apiUrl,
34
+ apiKey,
35
+ fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate)
36
+ });
37
+ return client.getBlog(slug, language);
38
+ }
39
+ async function listBlogs(listOpts = {}, opts = {}) {
40
+ const { apiUrl, apiKey } = resolveEnv(opts);
41
+ const client = createClient({
42
+ apiUrl,
43
+ apiKey,
44
+ fetch: buildFetch("chaingrow:blog:list", opts.revalidate)
45
+ });
46
+ return client.listBlogs(listOpts);
47
+ }
48
+ export {
49
+ getBlog,
50
+ listBlogs
51
+ };
52
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/next/index.ts"],"sourcesContent":["import type { Blog } from '../types';\nimport { createClient } from '../client';\n\nexport type NextClientOptions = {\n apiUrl?: string;\n apiKey?: string;\n revalidate?: number | false;\n};\n\nconst resolveEnv = (opts: NextClientOptions = {}) => {\n const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;\n const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;\n if (!apiUrl) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)',\n );\n }\n if (!apiKey) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)',\n );\n }\n return { apiUrl, apiKey };\n};\n\nconst buildFetch = (tag: string, revalidate: number | false | undefined) => {\n return ((input: RequestInfo | URL, init?: RequestInit) =>\n fetch(input, {\n ...init,\n next: {\n tags: ['chaingrow:blog', tag],\n ...(revalidate === false\n ? { revalidate: false as const }\n : revalidate !== undefined\n ? { revalidate }\n : {}),\n },\n } as RequestInit)) as typeof fetch;\n};\n\nexport async function getBlog(\n slug: string,\n language?: string,\n opts: NextClientOptions = {},\n): Promise<Blog | null> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate),\n });\n return client.getBlog(slug, language);\n}\n\nexport async function listBlogs(\n listOpts: { status?: 'DRAFT' | 'PUBLISHED'; language?: string } = {},\n opts: NextClientOptions = {},\n): Promise<Blog[]> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch('chaingrow:blog:list', opts.revalidate),\n });\n return client.listBlogs(listOpts);\n}\n\nexport type { Blog } from '../types';\n"],"mappings":";;;;;AASA,IAAM,aAAa,CAAC,OAA0B,CAAC,MAAM;AACnD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAEA,IAAM,aAAa,CAAC,KAAa,eAA2C;AAC1E,UAAQ,CAAC,OAA0B,SACjC,MAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,MAAM;AAAA,MACJ,MAAM,CAAC,kBAAkB,GAAG;AAAA,MAC5B,GAAI,eAAe,QACf,EAAE,YAAY,MAAe,IAC7B,eAAe,SACb,EAAE,WAAW,IACb,CAAC;AAAA,IACT;AAAA,EACF,CAAgB;AACpB;AAEA,eAAsB,QACpB,MACA,UACA,OAA0B,CAAC,GACL;AACtB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,kBAAkB,IAAI,IAAI,KAAK,UAAU;AAAA,EAC7D,CAAC;AACD,SAAO,OAAO,QAAQ,MAAM,QAAQ;AACtC;AAEA,eAAsB,UACpB,WAAkE,CAAC,GACnE,OAA0B,CAAC,GACV;AACjB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,uBAAuB,KAAK,UAAU;AAAA,EAC1D,CAAC;AACD,SAAO,OAAO,UAAU,QAAQ;AAClC;","names":[]}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/next/webhook.ts
21
+ var webhook_exports = {};
22
+ __export(webhook_exports, {
23
+ POST: () => POST,
24
+ createWebhookHandler: () => createWebhookHandler
25
+ });
26
+ module.exports = __toCommonJS(webhook_exports);
27
+ var import_cache = require("next/cache");
28
+ var import_node_crypto = require("crypto");
29
+ var verifySignature = (body, signature, secret) => {
30
+ const expected = (0, import_node_crypto.createHmac)("sha256", secret).update(body).digest("hex");
31
+ const provided = signature.trim();
32
+ if (expected.length !== provided.length) return false;
33
+ try {
34
+ return (0, import_node_crypto.timingSafeEqual)(Buffer.from(expected), Buffer.from(provided));
35
+ } catch {
36
+ return false;
37
+ }
38
+ };
39
+ var json = (status, body) => new Response(JSON.stringify(body), {
40
+ status,
41
+ headers: { "Content-Type": "application/json" }
42
+ });
43
+ function createWebhookHandler(opts = {}) {
44
+ const POST2 = async (req) => {
45
+ const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;
46
+ const body = await req.text();
47
+ if (secret) {
48
+ const signature = req.headers.get("x-chaingrow-signature") ?? req.headers.get("X-ChainGrow-Signature") ?? "";
49
+ if (!signature || !verifySignature(body, signature, secret)) {
50
+ return json(401, { error: "Invalid signature" });
51
+ }
52
+ }
53
+ let payload;
54
+ try {
55
+ payload = JSON.parse(body);
56
+ } catch {
57
+ return json(400, { error: "Invalid JSON body" });
58
+ }
59
+ const slug = typeof payload?.blog?.slug === "string" ? payload.blog.slug : null;
60
+ (0, import_cache.revalidateTag)("chaingrow:blog");
61
+ (0, import_cache.revalidateTag)("chaingrow:blog:list");
62
+ if (slug) (0, import_cache.revalidateTag)(`chaingrow:blog:${slug}`);
63
+ if (opts.onEvent) {
64
+ try {
65
+ await opts.onEvent(payload);
66
+ } catch (err) {
67
+ console.error("chaingrow-blog onEvent handler failed:", err);
68
+ }
69
+ }
70
+ return json(200, { ok: true, revalidated: slug ?? null });
71
+ };
72
+ return { POST: POST2 };
73
+ }
74
+ var { POST } = createWebhookHandler();
75
+ // Annotate the CommonJS export names for ESM import in node:
76
+ 0 && (module.exports = {
77
+ POST,
78
+ createWebhookHandler
79
+ });
80
+ //# sourceMappingURL=webhook.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/next/webhook.ts"],"sourcesContent":["import { revalidateTag } from 'next/cache';\nimport { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookPayload } from '../types';\n\nexport type WebhookHandlerOptions = {\n secret?: string;\n onEvent?: (payload: WebhookPayload) => void | Promise<void>;\n};\n\nconst verifySignature = (body: string, signature: string, secret: string) => {\n const expected = createHmac('sha256', secret).update(body).digest('hex');\n const provided = signature.trim();\n if (expected.length !== provided.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));\n } catch {\n return false;\n }\n};\n\nconst json = (status: number, body: Record<string, unknown>) =>\n new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n\nexport function createWebhookHandler(opts: WebhookHandlerOptions = {}) {\n const POST = async (req: Request): Promise<Response> => {\n const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;\n const body = await req.text();\n\n if (secret) {\n const signature =\n req.headers.get('x-chaingrow-signature') ??\n req.headers.get('X-ChainGrow-Signature') ??\n '';\n if (!signature || !verifySignature(body, signature, secret)) {\n return json(401, { error: 'Invalid signature' });\n }\n }\n\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(body) as WebhookPayload;\n } catch {\n return json(400, { error: 'Invalid JSON body' });\n }\n\n const slug =\n typeof payload?.blog?.slug === 'string' ? payload.blog.slug : null;\n\n revalidateTag('chaingrow:blog');\n revalidateTag('chaingrow:blog:list');\n if (slug) revalidateTag(`chaingrow:blog:${slug}`);\n\n if (opts.onEvent) {\n try {\n await opts.onEvent(payload);\n } catch (err) {\n console.error('chaingrow-blog onEvent handler failed:', err);\n }\n }\n\n return json(200, { ok: true, revalidated: slug ?? null });\n };\n\n return { POST };\n}\n\nexport const { POST } = createWebhookHandler();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA8B;AAC9B,yBAA4C;AAQ5C,IAAM,kBAAkB,CAAC,MAAc,WAAmB,WAAmB;AAC3E,QAAM,eAAW,+BAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvE,QAAM,WAAW,UAAU,KAAK;AAChC,MAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAChD,MAAI;AACF,eAAO,oCAAgB,OAAO,KAAK,QAAQ,GAAG,OAAO,KAAK,QAAQ,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,OAAO,CAAC,QAAgB,SAC5B,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,EACjC;AAAA,EACA,SAAS,EAAE,gBAAgB,mBAAmB;AAChD,CAAC;AAEI,SAAS,qBAAqB,OAA8B,CAAC,GAAG;AACrE,QAAMA,QAAO,OAAO,QAAoC;AACtD,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,QAAQ;AACV,YAAM,YACJ,IAAI,QAAQ,IAAI,uBAAuB,KACvC,IAAI,QAAQ,IAAI,uBAAuB,KACvC;AACF,UAAI,CAAC,aAAa,CAAC,gBAAgB,MAAM,WAAW,MAAM,GAAG;AAC3D,eAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,MACjD;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,IAAI;AAAA,IAC3B,QAAQ;AACN,aAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,IACjD;AAEA,UAAM,OACJ,OAAO,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,OAAO;AAEhE,oCAAc,gBAAgB;AAC9B,oCAAc,qBAAqB;AACnC,QAAI,KAAM,iCAAc,kBAAkB,IAAI,EAAE;AAEhD,QAAI,KAAK,SAAS;AAChB,UAAI;AACF,cAAM,KAAK,QAAQ,OAAO;AAAA,MAC5B,SAAS,KAAK;AACZ,gBAAQ,MAAM,0CAA0C,GAAG;AAAA,MAC7D;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,EAAE,IAAI,MAAM,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC1D;AAEA,SAAO,EAAE,MAAAA,MAAK;AAChB;AAEO,IAAM,EAAE,KAAK,IAAI,qBAAqB;","names":["POST"]}
@@ -0,0 +1,12 @@
1
+ import { b as WebhookPayload } from '../types-CoPD0ejB.cjs';
2
+
3
+ type WebhookHandlerOptions = {
4
+ secret?: string;
5
+ onEvent?: (payload: WebhookPayload) => void | Promise<void>;
6
+ };
7
+ declare function createWebhookHandler(opts?: WebhookHandlerOptions): {
8
+ POST: (req: Request) => Promise<Response>;
9
+ };
10
+ declare const POST: (req: Request) => Promise<Response>;
11
+
12
+ export { POST, type WebhookHandlerOptions, createWebhookHandler };
@@ -0,0 +1,12 @@
1
+ import { b as WebhookPayload } from '../types-CoPD0ejB.js';
2
+
3
+ type WebhookHandlerOptions = {
4
+ secret?: string;
5
+ onEvent?: (payload: WebhookPayload) => void | Promise<void>;
6
+ };
7
+ declare function createWebhookHandler(opts?: WebhookHandlerOptions): {
8
+ POST: (req: Request) => Promise<Response>;
9
+ };
10
+ declare const POST: (req: Request) => Promise<Response>;
11
+
12
+ export { POST, type WebhookHandlerOptions, createWebhookHandler };
@@ -0,0 +1,54 @@
1
+ // src/next/webhook.ts
2
+ import { revalidateTag } from "next/cache";
3
+ import { createHmac, timingSafeEqual } from "crypto";
4
+ var verifySignature = (body, signature, secret) => {
5
+ const expected = createHmac("sha256", secret).update(body).digest("hex");
6
+ const provided = signature.trim();
7
+ if (expected.length !== provided.length) return false;
8
+ try {
9
+ return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
10
+ } catch {
11
+ return false;
12
+ }
13
+ };
14
+ var json = (status, body) => new Response(JSON.stringify(body), {
15
+ status,
16
+ headers: { "Content-Type": "application/json" }
17
+ });
18
+ function createWebhookHandler(opts = {}) {
19
+ const POST2 = async (req) => {
20
+ const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;
21
+ const body = await req.text();
22
+ if (secret) {
23
+ const signature = req.headers.get("x-chaingrow-signature") ?? req.headers.get("X-ChainGrow-Signature") ?? "";
24
+ if (!signature || !verifySignature(body, signature, secret)) {
25
+ return json(401, { error: "Invalid signature" });
26
+ }
27
+ }
28
+ let payload;
29
+ try {
30
+ payload = JSON.parse(body);
31
+ } catch {
32
+ return json(400, { error: "Invalid JSON body" });
33
+ }
34
+ const slug = typeof payload?.blog?.slug === "string" ? payload.blog.slug : null;
35
+ revalidateTag("chaingrow:blog");
36
+ revalidateTag("chaingrow:blog:list");
37
+ if (slug) revalidateTag(`chaingrow:blog:${slug}`);
38
+ if (opts.onEvent) {
39
+ try {
40
+ await opts.onEvent(payload);
41
+ } catch (err) {
42
+ console.error("chaingrow-blog onEvent handler failed:", err);
43
+ }
44
+ }
45
+ return json(200, { ok: true, revalidated: slug ?? null });
46
+ };
47
+ return { POST: POST2 };
48
+ }
49
+ var { POST } = createWebhookHandler();
50
+ export {
51
+ POST,
52
+ createWebhookHandler
53
+ };
54
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/next/webhook.ts"],"sourcesContent":["import { revalidateTag } from 'next/cache';\nimport { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookPayload } from '../types';\n\nexport type WebhookHandlerOptions = {\n secret?: string;\n onEvent?: (payload: WebhookPayload) => void | Promise<void>;\n};\n\nconst verifySignature = (body: string, signature: string, secret: string) => {\n const expected = createHmac('sha256', secret).update(body).digest('hex');\n const provided = signature.trim();\n if (expected.length !== provided.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));\n } catch {\n return false;\n }\n};\n\nconst json = (status: number, body: Record<string, unknown>) =>\n new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n\nexport function createWebhookHandler(opts: WebhookHandlerOptions = {}) {\n const POST = async (req: Request): Promise<Response> => {\n const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;\n const body = await req.text();\n\n if (secret) {\n const signature =\n req.headers.get('x-chaingrow-signature') ??\n req.headers.get('X-ChainGrow-Signature') ??\n '';\n if (!signature || !verifySignature(body, signature, secret)) {\n return json(401, { error: 'Invalid signature' });\n }\n }\n\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(body) as WebhookPayload;\n } catch {\n return json(400, { error: 'Invalid JSON body' });\n }\n\n const slug =\n typeof payload?.blog?.slug === 'string' ? payload.blog.slug : null;\n\n revalidateTag('chaingrow:blog');\n revalidateTag('chaingrow:blog:list');\n if (slug) revalidateTag(`chaingrow:blog:${slug}`);\n\n if (opts.onEvent) {\n try {\n await opts.onEvent(payload);\n } catch (err) {\n console.error('chaingrow-blog onEvent handler failed:', err);\n }\n }\n\n return json(200, { ok: true, revalidated: slug ?? null });\n };\n\n return { POST };\n}\n\nexport const { POST } = createWebhookHandler();\n"],"mappings":";AAAA,SAAS,qBAAqB;AAC9B,SAAS,YAAY,uBAAuB;AAQ5C,IAAM,kBAAkB,CAAC,MAAc,WAAmB,WAAmB;AAC3E,QAAM,WAAW,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvE,QAAM,WAAW,UAAU,KAAK;AAChC,MAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAChD,MAAI;AACF,WAAO,gBAAgB,OAAO,KAAK,QAAQ,GAAG,OAAO,KAAK,QAAQ,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,OAAO,CAAC,QAAgB,SAC5B,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,EACjC;AAAA,EACA,SAAS,EAAE,gBAAgB,mBAAmB;AAChD,CAAC;AAEI,SAAS,qBAAqB,OAA8B,CAAC,GAAG;AACrE,QAAMA,QAAO,OAAO,QAAoC;AACtD,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,QAAQ;AACV,YAAM,YACJ,IAAI,QAAQ,IAAI,uBAAuB,KACvC,IAAI,QAAQ,IAAI,uBAAuB,KACvC;AACF,UAAI,CAAC,aAAa,CAAC,gBAAgB,MAAM,WAAW,MAAM,GAAG;AAC3D,eAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,MACjD;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,IAAI;AAAA,IAC3B,QAAQ;AACN,aAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,IACjD;AAEA,UAAM,OACJ,OAAO,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,OAAO;AAEhE,kBAAc,gBAAgB;AAC9B,kBAAc,qBAAqB;AACnC,QAAI,KAAM,eAAc,kBAAkB,IAAI,EAAE;AAEhD,QAAI,KAAK,SAAS;AAChB,UAAI;AACF,cAAM,KAAK,QAAQ,OAAO;AAAA,MAC5B,SAAS,KAAK;AACZ,gBAAQ,MAAM,0CAA0C,GAAG;AAAA,MAC7D;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,EAAE,IAAI,MAAM,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC1D;AAEA,SAAO,EAAE,MAAAA,MAAK;AAChB;AAEO,IAAM,EAAE,KAAK,IAAI,qBAAqB;","names":["POST"]}
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/react/index.ts
21
+ var react_exports = {};
22
+ __export(react_exports, {
23
+ BlockRenderer: () => BlockRenderer,
24
+ BlogRenderer: () => BlogRenderer,
25
+ DefaultCode: () => DefaultCode,
26
+ DefaultHeading: () => DefaultHeading,
27
+ DefaultImage: () => DefaultImage,
28
+ DefaultList: () => DefaultList,
29
+ DefaultParagraph: () => DefaultParagraph,
30
+ DefaultQuote: () => DefaultQuote
31
+ });
32
+ module.exports = __toCommonJS(react_exports);
33
+
34
+ // src/react/defaults.tsx
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var DefaultHeading = ({
37
+ level,
38
+ children
39
+ }) => {
40
+ const Tag = level === 2 ? "h2" : level === 4 ? "h4" : "h3";
41
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { children });
42
+ };
43
+ var DefaultParagraph = ({
44
+ children
45
+ }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children });
46
+ var DefaultList = ({ items }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: item }, i)) });
47
+ var DefaultQuote = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", { children });
48
+ var DefaultCode = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children }) });
49
+ var DefaultImage = ({
50
+ src,
51
+ alt
52
+ }) => {
53
+ if (!src) return null;
54
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src, alt });
55
+ };
56
+
57
+ // src/react/BlogRenderer.tsx
58
+ var import_jsx_runtime2 = require("react/jsx-runtime");
59
+ var parseListItems = (content) => content.split("\n").map((line) => line.replace(/^\s*[-•*]\s*/, "").trim()).filter(Boolean);
60
+ function BlockRenderer({
61
+ block,
62
+ components = {}
63
+ }) {
64
+ switch (block.type) {
65
+ case "heading": {
66
+ const Heading = components.heading ?? DefaultHeading;
67
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Heading, { level: block.level ?? 2, children: block.content });
68
+ }
69
+ case "paragraph": {
70
+ const Paragraph = components.paragraph ?? DefaultParagraph;
71
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Paragraph, { children: block.content });
72
+ }
73
+ case "list": {
74
+ const List = components.list ?? DefaultList;
75
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(List, { items: parseListItems(block.content) });
76
+ }
77
+ case "quote": {
78
+ const Quote = components.quote ?? DefaultQuote;
79
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Quote, { children: block.content });
80
+ }
81
+ case "code": {
82
+ const Code = components.code ?? DefaultCode;
83
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Code, { children: block.content });
84
+ }
85
+ case "image_placeholder": {
86
+ const Image = components.image ?? DefaultImage;
87
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Image, { alt: block.content });
88
+ }
89
+ default:
90
+ return null;
91
+ }
92
+ }
93
+ function BlogRenderer({
94
+ blog,
95
+ components = {}
96
+ }) {
97
+ const Wrapper = components.wrapper;
98
+ const content = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: blog.blocks.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BlockRenderer, { block, components }, i)) });
99
+ if (Wrapper) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Wrapper, { blog, children: content });
100
+ return content;
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ BlockRenderer,
105
+ BlogRenderer,
106
+ DefaultCode,
107
+ DefaultHeading,
108
+ DefaultImage,
109
+ DefaultList,
110
+ DefaultParagraph,
111
+ DefaultQuote
112
+ });
113
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/react/defaults.tsx","../../src/react/BlogRenderer.tsx"],"sourcesContent":["export {\n BlogRenderer,\n BlockRenderer,\n type BlockComponents,\n} from './BlogRenderer';\nexport * from './defaults';\nexport type { Blog, Block, FaqItem, SocialPost } from '../types';\n","import * as React from 'react';\n\nexport const DefaultHeading = ({\n level,\n children,\n}: {\n level: number;\n children: React.ReactNode;\n}) => {\n const Tag = (level === 2 ? 'h2' : level === 4 ? 'h4' : 'h3') as\n | 'h2'\n | 'h3'\n | 'h4';\n return <Tag>{children}</Tag>;\n};\n\nexport const DefaultParagraph = ({\n children,\n}: {\n children: React.ReactNode;\n}) => <p>{children}</p>;\n\nexport const DefaultList = ({ items }: { items: string[] }) => (\n <ul>\n {items.map((item, i) => (\n <li key={i}>{item}</li>\n ))}\n </ul>\n);\n\nexport const DefaultQuote = ({ children }: { children: React.ReactNode }) => (\n <blockquote>{children}</blockquote>\n);\n\nexport const DefaultCode = ({ children }: { children: React.ReactNode }) => (\n <pre>\n <code>{children}</code>\n </pre>\n);\n\nexport const DefaultImage = ({\n src,\n alt,\n}: {\n src?: string;\n alt: string;\n}) => {\n if (!src) return null;\n // eslint-disable-next-line @next/next/no-img-element\n return <img src={src} alt={alt} />;\n};\n","import * as React from 'react';\nimport type { Blog, Block } from '../types';\nimport {\n DefaultHeading,\n DefaultParagraph,\n DefaultList,\n DefaultQuote,\n DefaultCode,\n DefaultImage,\n} from './defaults';\n\nexport type BlockComponents = Partial<{\n heading: React.ComponentType<{ level: number; children: React.ReactNode }>;\n paragraph: React.ComponentType<{ children: React.ReactNode }>;\n list: React.ComponentType<{ items: string[] }>;\n quote: React.ComponentType<{ children: React.ReactNode }>;\n code: React.ComponentType<{ children: React.ReactNode }>;\n image: React.ComponentType<{ src?: string; alt: string }>;\n wrapper: React.ComponentType<{ children: React.ReactNode; blog: Blog }>;\n}>;\n\nconst parseListItems = (content: string): string[] =>\n content\n .split('\\n')\n .map((line) => line.replace(/^\\s*[-•*]\\s*/, '').trim())\n .filter(Boolean);\n\nexport function BlockRenderer({\n block,\n components = {},\n}: {\n block: Block;\n components?: BlockComponents;\n}) {\n switch (block.type) {\n case 'heading': {\n const Heading = components.heading ?? DefaultHeading;\n return <Heading level={block.level ?? 2}>{block.content}</Heading>;\n }\n case 'paragraph': {\n const Paragraph = components.paragraph ?? DefaultParagraph;\n return <Paragraph>{block.content}</Paragraph>;\n }\n case 'list': {\n const List = components.list ?? DefaultList;\n return <List items={parseListItems(block.content)} />;\n }\n case 'quote': {\n const Quote = components.quote ?? DefaultQuote;\n return <Quote>{block.content}</Quote>;\n }\n case 'code': {\n const Code = components.code ?? DefaultCode;\n return <Code>{block.content}</Code>;\n }\n case 'image_placeholder': {\n const Image = components.image ?? DefaultImage;\n return <Image alt={block.content} />;\n }\n default:\n return null;\n }\n}\n\nexport function BlogRenderer({\n blog,\n components = {},\n}: {\n blog: Blog;\n components?: BlockComponents;\n}) {\n const Wrapper = components.wrapper;\n const content = (\n <>\n {blog.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} components={components} />\n ))}\n </>\n );\n if (Wrapper) return <Wrapper blog={blog}>{content}</Wrapper>;\n return content;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACaS;AAXF,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA;AACF,MAGM;AACJ,QAAM,MAAO,UAAU,IAAI,OAAO,UAAU,IAAI,OAAO;AAIvD,SAAO,4CAAC,OAAK,UAAS;AACxB;AAEO,IAAM,mBAAmB,CAAC;AAAA,EAC/B;AACF,MAEM,4CAAC,OAAG,UAAS;AAEZ,IAAM,cAAc,CAAC,EAAE,MAAM,MAClC,4CAAC,QACE,gBAAM,IAAI,CAAC,MAAM,MAChB,4CAAC,QAAY,kBAAJ,CAAS,CACnB,GACH;AAGK,IAAM,eAAe,CAAC,EAAE,SAAS,MACtC,4CAAC,gBAAY,UAAS;AAGjB,IAAM,cAAc,CAAC,EAAE,SAAS,MACrC,4CAAC,SACC,sDAAC,UAAM,UAAS,GAClB;AAGK,IAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AACF,MAGM;AACJ,MAAI,CAAC,IAAK,QAAO;AAEjB,SAAO,4CAAC,SAAI,KAAU,KAAU;AAClC;;;ACba,IAAAA,sBAAA;AAhBb,IAAM,iBAAiB,CAAC,YACtB,QACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,QAAQ,gBAAgB,EAAE,EAAE,KAAK,CAAC,EACrD,OAAO,OAAO;AAEZ,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,WAAW;AACd,YAAM,UAAU,WAAW,WAAW;AACtC,aAAO,6CAAC,WAAQ,OAAO,MAAM,SAAS,GAAI,gBAAM,SAAQ;AAAA,IAC1D;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,YAAY,WAAW,aAAa;AAC1C,aAAO,6CAAC,aAAW,gBAAM,SAAQ;AAAA,IACnC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,6CAAC,QAAK,OAAO,eAAe,MAAM,OAAO,GAAG;AAAA,IACrD;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,6CAAC,SAAO,gBAAM,SAAQ;AAAA,IAC/B;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,6CAAC,QAAM,gBAAM,SAAQ;AAAA,IAC9B;AAAA,IACA,KAAK,qBAAqB;AACxB,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,6CAAC,SAAM,KAAK,MAAM,SAAS;AAAA,IACpC;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,QAAM,UAAU,WAAW;AAC3B,QAAM,UACJ,6EACG,eAAK,OAAO,IAAI,CAAC,OAAO,MACvB,6CAAC,iBAAsB,OAAc,cAAjB,CAAyC,CAC9D,GACH;AAEF,MAAI,QAAS,QAAO,6CAAC,WAAQ,MAAa,mBAAQ;AAClD,SAAO;AACT;","names":["import_jsx_runtime"]}
@@ -0,0 +1,62 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { B as Blog, a as Block } from '../types-CoPD0ejB.cjs';
4
+ export { F as FaqItem, S as SocialPost } from '../types-CoPD0ejB.cjs';
5
+
6
+ type BlockComponents = Partial<{
7
+ heading: React.ComponentType<{
8
+ level: number;
9
+ children: React.ReactNode;
10
+ }>;
11
+ paragraph: React.ComponentType<{
12
+ children: React.ReactNode;
13
+ }>;
14
+ list: React.ComponentType<{
15
+ items: string[];
16
+ }>;
17
+ quote: React.ComponentType<{
18
+ children: React.ReactNode;
19
+ }>;
20
+ code: React.ComponentType<{
21
+ children: React.ReactNode;
22
+ }>;
23
+ image: React.ComponentType<{
24
+ src?: string;
25
+ alt: string;
26
+ }>;
27
+ wrapper: React.ComponentType<{
28
+ children: React.ReactNode;
29
+ blog: Blog;
30
+ }>;
31
+ }>;
32
+ declare function BlockRenderer({ block, components, }: {
33
+ block: Block;
34
+ components?: BlockComponents;
35
+ }): react_jsx_runtime.JSX.Element | null;
36
+ declare function BlogRenderer({ blog, components, }: {
37
+ blog: Blog;
38
+ components?: BlockComponents;
39
+ }): react_jsx_runtime.JSX.Element;
40
+
41
+ declare const DefaultHeading: ({ level, children, }: {
42
+ level: number;
43
+ children: React.ReactNode;
44
+ }) => react_jsx_runtime.JSX.Element;
45
+ declare const DefaultParagraph: ({ children, }: {
46
+ children: React.ReactNode;
47
+ }) => react_jsx_runtime.JSX.Element;
48
+ declare const DefaultList: ({ items }: {
49
+ items: string[];
50
+ }) => react_jsx_runtime.JSX.Element;
51
+ declare const DefaultQuote: ({ children }: {
52
+ children: React.ReactNode;
53
+ }) => react_jsx_runtime.JSX.Element;
54
+ declare const DefaultCode: ({ children }: {
55
+ children: React.ReactNode;
56
+ }) => react_jsx_runtime.JSX.Element;
57
+ declare const DefaultImage: ({ src, alt, }: {
58
+ src?: string;
59
+ alt: string;
60
+ }) => react_jsx_runtime.JSX.Element | null;
61
+
62
+ export { Block, type BlockComponents, BlockRenderer, Blog, BlogRenderer, DefaultCode, DefaultHeading, DefaultImage, DefaultList, DefaultParagraph, DefaultQuote };
@@ -0,0 +1,62 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import { B as Blog, a as Block } from '../types-CoPD0ejB.js';
4
+ export { F as FaqItem, S as SocialPost } from '../types-CoPD0ejB.js';
5
+
6
+ type BlockComponents = Partial<{
7
+ heading: React.ComponentType<{
8
+ level: number;
9
+ children: React.ReactNode;
10
+ }>;
11
+ paragraph: React.ComponentType<{
12
+ children: React.ReactNode;
13
+ }>;
14
+ list: React.ComponentType<{
15
+ items: string[];
16
+ }>;
17
+ quote: React.ComponentType<{
18
+ children: React.ReactNode;
19
+ }>;
20
+ code: React.ComponentType<{
21
+ children: React.ReactNode;
22
+ }>;
23
+ image: React.ComponentType<{
24
+ src?: string;
25
+ alt: string;
26
+ }>;
27
+ wrapper: React.ComponentType<{
28
+ children: React.ReactNode;
29
+ blog: Blog;
30
+ }>;
31
+ }>;
32
+ declare function BlockRenderer({ block, components, }: {
33
+ block: Block;
34
+ components?: BlockComponents;
35
+ }): react_jsx_runtime.JSX.Element | null;
36
+ declare function BlogRenderer({ blog, components, }: {
37
+ blog: Blog;
38
+ components?: BlockComponents;
39
+ }): react_jsx_runtime.JSX.Element;
40
+
41
+ declare const DefaultHeading: ({ level, children, }: {
42
+ level: number;
43
+ children: React.ReactNode;
44
+ }) => react_jsx_runtime.JSX.Element;
45
+ declare const DefaultParagraph: ({ children, }: {
46
+ children: React.ReactNode;
47
+ }) => react_jsx_runtime.JSX.Element;
48
+ declare const DefaultList: ({ items }: {
49
+ items: string[];
50
+ }) => react_jsx_runtime.JSX.Element;
51
+ declare const DefaultQuote: ({ children }: {
52
+ children: React.ReactNode;
53
+ }) => react_jsx_runtime.JSX.Element;
54
+ declare const DefaultCode: ({ children }: {
55
+ children: React.ReactNode;
56
+ }) => react_jsx_runtime.JSX.Element;
57
+ declare const DefaultImage: ({ src, alt, }: {
58
+ src?: string;
59
+ alt: string;
60
+ }) => react_jsx_runtime.JSX.Element | null;
61
+
62
+ export { Block, type BlockComponents, BlockRenderer, Blog, BlogRenderer, DefaultCode, DefaultHeading, DefaultImage, DefaultList, DefaultParagraph, DefaultQuote };
@@ -0,0 +1,79 @@
1
+ // src/react/defaults.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ var DefaultHeading = ({
4
+ level,
5
+ children
6
+ }) => {
7
+ const Tag = level === 2 ? "h2" : level === 4 ? "h4" : "h3";
8
+ return /* @__PURE__ */ jsx(Tag, { children });
9
+ };
10
+ var DefaultParagraph = ({
11
+ children
12
+ }) => /* @__PURE__ */ jsx("p", { children });
13
+ var DefaultList = ({ items }) => /* @__PURE__ */ jsx("ul", { children: items.map((item, i) => /* @__PURE__ */ jsx("li", { children: item }, i)) });
14
+ var DefaultQuote = ({ children }) => /* @__PURE__ */ jsx("blockquote", { children });
15
+ var DefaultCode = ({ children }) => /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children }) });
16
+ var DefaultImage = ({
17
+ src,
18
+ alt
19
+ }) => {
20
+ if (!src) return null;
21
+ return /* @__PURE__ */ jsx("img", { src, alt });
22
+ };
23
+
24
+ // src/react/BlogRenderer.tsx
25
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
26
+ var parseListItems = (content) => content.split("\n").map((line) => line.replace(/^\s*[-•*]\s*/, "").trim()).filter(Boolean);
27
+ function BlockRenderer({
28
+ block,
29
+ components = {}
30
+ }) {
31
+ switch (block.type) {
32
+ case "heading": {
33
+ const Heading = components.heading ?? DefaultHeading;
34
+ return /* @__PURE__ */ jsx2(Heading, { level: block.level ?? 2, children: block.content });
35
+ }
36
+ case "paragraph": {
37
+ const Paragraph = components.paragraph ?? DefaultParagraph;
38
+ return /* @__PURE__ */ jsx2(Paragraph, { children: block.content });
39
+ }
40
+ case "list": {
41
+ const List = components.list ?? DefaultList;
42
+ return /* @__PURE__ */ jsx2(List, { items: parseListItems(block.content) });
43
+ }
44
+ case "quote": {
45
+ const Quote = components.quote ?? DefaultQuote;
46
+ return /* @__PURE__ */ jsx2(Quote, { children: block.content });
47
+ }
48
+ case "code": {
49
+ const Code = components.code ?? DefaultCode;
50
+ return /* @__PURE__ */ jsx2(Code, { children: block.content });
51
+ }
52
+ case "image_placeholder": {
53
+ const Image = components.image ?? DefaultImage;
54
+ return /* @__PURE__ */ jsx2(Image, { alt: block.content });
55
+ }
56
+ default:
57
+ return null;
58
+ }
59
+ }
60
+ function BlogRenderer({
61
+ blog,
62
+ components = {}
63
+ }) {
64
+ const Wrapper = components.wrapper;
65
+ const content = /* @__PURE__ */ jsx2(Fragment, { children: blog.blocks.map((block, i) => /* @__PURE__ */ jsx2(BlockRenderer, { block, components }, i)) });
66
+ if (Wrapper) return /* @__PURE__ */ jsx2(Wrapper, { blog, children: content });
67
+ return content;
68
+ }
69
+ export {
70
+ BlockRenderer,
71
+ BlogRenderer,
72
+ DefaultCode,
73
+ DefaultHeading,
74
+ DefaultImage,
75
+ DefaultList,
76
+ DefaultParagraph,
77
+ DefaultQuote
78
+ };
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/defaults.tsx","../../src/react/BlogRenderer.tsx"],"sourcesContent":["import * as React from 'react';\n\nexport const DefaultHeading = ({\n level,\n children,\n}: {\n level: number;\n children: React.ReactNode;\n}) => {\n const Tag = (level === 2 ? 'h2' : level === 4 ? 'h4' : 'h3') as\n | 'h2'\n | 'h3'\n | 'h4';\n return <Tag>{children}</Tag>;\n};\n\nexport const DefaultParagraph = ({\n children,\n}: {\n children: React.ReactNode;\n}) => <p>{children}</p>;\n\nexport const DefaultList = ({ items }: { items: string[] }) => (\n <ul>\n {items.map((item, i) => (\n <li key={i}>{item}</li>\n ))}\n </ul>\n);\n\nexport const DefaultQuote = ({ children }: { children: React.ReactNode }) => (\n <blockquote>{children}</blockquote>\n);\n\nexport const DefaultCode = ({ children }: { children: React.ReactNode }) => (\n <pre>\n <code>{children}</code>\n </pre>\n);\n\nexport const DefaultImage = ({\n src,\n alt,\n}: {\n src?: string;\n alt: string;\n}) => {\n if (!src) return null;\n // eslint-disable-next-line @next/next/no-img-element\n return <img src={src} alt={alt} />;\n};\n","import * as React from 'react';\nimport type { Blog, Block } from '../types';\nimport {\n DefaultHeading,\n DefaultParagraph,\n DefaultList,\n DefaultQuote,\n DefaultCode,\n DefaultImage,\n} from './defaults';\n\nexport type BlockComponents = Partial<{\n heading: React.ComponentType<{ level: number; children: React.ReactNode }>;\n paragraph: React.ComponentType<{ children: React.ReactNode }>;\n list: React.ComponentType<{ items: string[] }>;\n quote: React.ComponentType<{ children: React.ReactNode }>;\n code: React.ComponentType<{ children: React.ReactNode }>;\n image: React.ComponentType<{ src?: string; alt: string }>;\n wrapper: React.ComponentType<{ children: React.ReactNode; blog: Blog }>;\n}>;\n\nconst parseListItems = (content: string): string[] =>\n content\n .split('\\n')\n .map((line) => line.replace(/^\\s*[-•*]\\s*/, '').trim())\n .filter(Boolean);\n\nexport function BlockRenderer({\n block,\n components = {},\n}: {\n block: Block;\n components?: BlockComponents;\n}) {\n switch (block.type) {\n case 'heading': {\n const Heading = components.heading ?? DefaultHeading;\n return <Heading level={block.level ?? 2}>{block.content}</Heading>;\n }\n case 'paragraph': {\n const Paragraph = components.paragraph ?? DefaultParagraph;\n return <Paragraph>{block.content}</Paragraph>;\n }\n case 'list': {\n const List = components.list ?? DefaultList;\n return <List items={parseListItems(block.content)} />;\n }\n case 'quote': {\n const Quote = components.quote ?? DefaultQuote;\n return <Quote>{block.content}</Quote>;\n }\n case 'code': {\n const Code = components.code ?? DefaultCode;\n return <Code>{block.content}</Code>;\n }\n case 'image_placeholder': {\n const Image = components.image ?? DefaultImage;\n return <Image alt={block.content} />;\n }\n default:\n return null;\n }\n}\n\nexport function BlogRenderer({\n blog,\n components = {},\n}: {\n blog: Blog;\n components?: BlockComponents;\n}) {\n const Wrapper = components.wrapper;\n const content = (\n <>\n {blog.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} components={components} />\n ))}\n </>\n );\n if (Wrapper) return <Wrapper blog={blog}>{content}</Wrapper>;\n return content;\n}\n"],"mappings":";AAaS;AAXF,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA;AACF,MAGM;AACJ,QAAM,MAAO,UAAU,IAAI,OAAO,UAAU,IAAI,OAAO;AAIvD,SAAO,oBAAC,OAAK,UAAS;AACxB;AAEO,IAAM,mBAAmB,CAAC;AAAA,EAC/B;AACF,MAEM,oBAAC,OAAG,UAAS;AAEZ,IAAM,cAAc,CAAC,EAAE,MAAM,MAClC,oBAAC,QACE,gBAAM,IAAI,CAAC,MAAM,MAChB,oBAAC,QAAY,kBAAJ,CAAS,CACnB,GACH;AAGK,IAAM,eAAe,CAAC,EAAE,SAAS,MACtC,oBAAC,gBAAY,UAAS;AAGjB,IAAM,cAAc,CAAC,EAAE,SAAS,MACrC,oBAAC,SACC,8BAAC,UAAM,UAAS,GAClB;AAGK,IAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AACF,MAGM;AACJ,MAAI,CAAC,IAAK,QAAO;AAEjB,SAAO,oBAAC,SAAI,KAAU,KAAU;AAClC;;;ACba,SAoCT,UApCS,OAAAA,YAAA;AAhBb,IAAM,iBAAiB,CAAC,YACtB,QACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,QAAQ,gBAAgB,EAAE,EAAE,KAAK,CAAC,EACrD,OAAO,OAAO;AAEZ,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,WAAW;AACd,YAAM,UAAU,WAAW,WAAW;AACtC,aAAO,gBAAAA,KAAC,WAAQ,OAAO,MAAM,SAAS,GAAI,gBAAM,SAAQ;AAAA,IAC1D;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,YAAY,WAAW,aAAa;AAC1C,aAAO,gBAAAA,KAAC,aAAW,gBAAM,SAAQ;AAAA,IACnC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,gBAAAA,KAAC,QAAK,OAAO,eAAe,MAAM,OAAO,GAAG;AAAA,IACrD;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,gBAAAA,KAAC,SAAO,gBAAM,SAAQ;AAAA,IAC/B;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,gBAAAA,KAAC,QAAM,gBAAM,SAAQ;AAAA,IAC9B;AAAA,IACA,KAAK,qBAAqB;AACxB,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,gBAAAA,KAAC,SAAM,KAAK,MAAM,SAAS;AAAA,IACpC;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,QAAM,UAAU,WAAW;AAC3B,QAAM,UACJ,gBAAAA,KAAA,YACG,eAAK,OAAO,IAAI,CAAC,OAAO,MACvB,gBAAAA,KAAC,iBAAsB,OAAc,cAAjB,CAAyC,CAC9D,GACH;AAEF,MAAI,QAAS,QAAO,gBAAAA,KAAC,WAAQ,MAAa,mBAAQ;AAClD,SAAO;AACT;","names":["jsx"]}
@@ -0,0 +1,53 @@
1
+ type Block = {
2
+ type: 'heading';
3
+ level?: number;
4
+ content: string;
5
+ } | {
6
+ type: 'paragraph';
7
+ content: string;
8
+ } | {
9
+ type: 'list';
10
+ content: string;
11
+ } | {
12
+ type: 'quote';
13
+ content: string;
14
+ } | {
15
+ type: 'code';
16
+ content: string;
17
+ } | {
18
+ type: 'image_placeholder';
19
+ content: string;
20
+ };
21
+ type FaqItem = {
22
+ question: string;
23
+ answer: string;
24
+ };
25
+ type SocialPost = {
26
+ provider: string;
27
+ content: string;
28
+ social_title: string | null;
29
+ };
30
+ type Blog = {
31
+ id: string;
32
+ slug: string;
33
+ language: string;
34
+ title: string;
35
+ description: string | null;
36
+ blocks: Block[];
37
+ faq: FaqItem[];
38
+ og_image: string | null;
39
+ image_url: string | null;
40
+ video_url: string | null;
41
+ video_prompt: string | null;
42
+ structured_data: Record<string, unknown> | null;
43
+ published_at: string;
44
+ social_posts: SocialPost[];
45
+ };
46
+ type WebhookEvent = 'blog.created' | 'blog.updated' | 'blog.published';
47
+ type WebhookPayload = {
48
+ event: WebhookEvent;
49
+ timestamp: string;
50
+ blog: Blog & Record<string, unknown>;
51
+ };
52
+
53
+ export type { Blog as B, FaqItem as F, SocialPost as S, WebhookEvent as W, Block as a, WebhookPayload as b };
@@ -0,0 +1,53 @@
1
+ type Block = {
2
+ type: 'heading';
3
+ level?: number;
4
+ content: string;
5
+ } | {
6
+ type: 'paragraph';
7
+ content: string;
8
+ } | {
9
+ type: 'list';
10
+ content: string;
11
+ } | {
12
+ type: 'quote';
13
+ content: string;
14
+ } | {
15
+ type: 'code';
16
+ content: string;
17
+ } | {
18
+ type: 'image_placeholder';
19
+ content: string;
20
+ };
21
+ type FaqItem = {
22
+ question: string;
23
+ answer: string;
24
+ };
25
+ type SocialPost = {
26
+ provider: string;
27
+ content: string;
28
+ social_title: string | null;
29
+ };
30
+ type Blog = {
31
+ id: string;
32
+ slug: string;
33
+ language: string;
34
+ title: string;
35
+ description: string | null;
36
+ blocks: Block[];
37
+ faq: FaqItem[];
38
+ og_image: string | null;
39
+ image_url: string | null;
40
+ video_url: string | null;
41
+ video_prompt: string | null;
42
+ structured_data: Record<string, unknown> | null;
43
+ published_at: string;
44
+ social_posts: SocialPost[];
45
+ };
46
+ type WebhookEvent = 'blog.created' | 'blog.updated' | 'blog.published';
47
+ type WebhookPayload = {
48
+ event: WebhookEvent;
49
+ timestamp: string;
50
+ blog: Blog & Record<string, unknown>;
51
+ };
52
+
53
+ export type { Blog as B, FaqItem as F, SocialPost as S, WebhookEvent as W, Block as a, WebhookPayload as b };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "chaingrow-blog",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in Next.js SDK for rendering blogs published from ChainGrow.",
5
+ "license": "MIT",
6
+ "author": "ChainGrow",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/chainmatics/chaingrow.git",
10
+ "directory": "packages/blog-sdk"
11
+ },
12
+ "keywords": ["chaingrow", "blog", "nextjs", "cms", "sdk"],
13
+ "sideEffects": false,
14
+ "type": "module",
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ },
24
+ "./next": {
25
+ "types": "./dist/next/index.d.ts",
26
+ "import": "./dist/next/index.js",
27
+ "require": "./dist/next/index.cjs"
28
+ },
29
+ "./next/webhook": {
30
+ "types": "./dist/next/webhook.d.ts",
31
+ "import": "./dist/next/webhook.js",
32
+ "require": "./dist/next/webhook.cjs"
33
+ },
34
+ "./react": {
35
+ "types": "./dist/react/index.d.ts",
36
+ "import": "./dist/react/index.js",
37
+ "require": "./dist/react/index.cjs"
38
+ }
39
+ },
40
+ "files": ["dist", "README.md"],
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "prepublishOnly": "yarn build"
46
+ },
47
+ "peerDependencies": {
48
+ "next": ">=14.0.0",
49
+ "react": ">=18.0.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "next": { "optional": true },
53
+ "react": { "optional": true }
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^20.0.0",
57
+ "@types/react": "^18.2.0",
58
+ "next": "^14.2.0",
59
+ "react": "^18.3.0",
60
+ "tsup": "^8.0.0",
61
+ "typescript": "^5.4.0"
62
+ }
63
+ }