@dispatchcms/next 0.0.6 → 0.0.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @dispatchcms/next
2
2
 
3
- Fetch published posts from a [Dispatch](https://github.com/dispatch-cms/dispatch) CMS in your Next.js app.
3
+ Fetch published entries from a [Dispatch](https://github.com/dispatch-cms/dispatch) CMS in your Next.js app.
4
4
 
5
5
  ## Install
6
6
 
@@ -36,43 +36,41 @@ You can omit `initDispatch` entirely if `NEXT_PUBLIC_DISPATCH_SITE_KEY` is set;
36
36
 
37
37
  ## Caching
38
38
 
39
- The package does **not** cache responses in memory. Each call to `getPosts()` or `getPost(slug)` fetches from the API, so your app always sees up-to-date data (e.g. when you unpublish a post in the CMS it disappears on the next load). For performance, rely on Next.js: use Server Components and the default `fetch` caching, or `revalidate` / ISR, so that responses are cached at the request level and stay fresh according to your revalidation settings.
39
+ The package does **not** cache responses in memory. Each call to `getEntries()` or `getEntry()` fetches from the API, so your app always sees up-to-date data. For performance, rely on Next.js: use Server Components and the default `fetch` caching, or `revalidate` / ISR.
40
40
 
41
41
  ## API
42
42
 
43
- ### `getPosts(siteKey?)`
43
+ ### `getEntries(collection, siteKey?)`
44
44
 
45
- Returns all published posts for the site. Always fetches from the API (no in-memory cache).
45
+ Returns published entries for a collection, ordered as in the CMS.
46
46
 
47
- - **Returns:** `Promise<Post[]>`
47
+ - **Returns:** `Promise<Entry[]>`
48
48
  - **Optional:** pass `siteKey` to override the configured site for this call.
49
49
 
50
- ### `getPost(slug, siteKey?)`
50
+ ### `getEntry(collection, slug, siteKey?)`
51
51
 
52
- Returns a single published post by slug, or `null` if not found. Always fetches from the API (no in-memory cache).
52
+ Returns one published entry by collection and slug, or `null` if not found.
53
53
 
54
- - **Returns:** `Promise<Post | null>`
55
- - **Optional:** pass `siteKey` as the second argument to override the configured site.
54
+ - **Returns:** `Promise<Entry | null>`
56
55
 
57
- ### `getPostByPreviewToken(token)`
56
+ ### `getEntryByPreviewToken(token)`
58
57
 
59
- Returns a single post by its preview token (draft or published). Use this in your app’s **preview** route so editors can open a shareable link and see the post as it will appear. No site key is required; the token is the secret.
58
+ Returns one entry by its preview token (draft or published). No site key is required; the token is the secret.
60
59
 
61
- - **Returns:** `Promise<Post | null>`
62
- - **Example:** Implement a route at `/preview` (or `/blog/preview`) that reads `token` from the query and renders the post with the same layout as your live post page (see below).
60
+ - **Returns:** `Promise<Entry | null>`
63
61
 
64
62
  ## Types
65
63
 
66
64
  Exportable types:
67
65
 
68
- - **`Post`** – A post from the CMS. Main fields: `id`, `title`, `slug`, `content` (TipTap/ProseMirror JSON), `excerpt`, `featured_image`, `published`, `published_at`, `created_at`, `updated_at`, `site_id`. See **Rendering content** below for how to render `content`.
66
+ - **`Entry`** – An entry from a collection. Main fields: `id`, `collection`, `title`, `slug`, `data` (field values; rich text fields are TipTap JSON), `published`, `published_at`, `sort_order`, `created_at`, `updated_at`, `site_id`. A migrated blog stores the body at `data.body` and the excerpt at `data.excerpt`.
69
67
  - **`DispatchConfig`** – Options for `initDispatch`: `{ siteKey: string }`.
70
68
 
71
69
  Use your editor’s type hints or the generated `.d.ts` for the full shape.
72
70
 
73
71
  ## Rendering content
74
72
 
75
- Post `content` is TipTap (ProseMirror) JSON. Render it with TipTap’s static renderer so formatting, links, and images match the CMS:
73
+ Rich text field values are TipTap (ProseMirror) JSON. Render them with TipTap’s static renderer so formatting, links, and images match the CMS:
76
74
 
77
75
  - **Server Components:** Use `renderToHTMLString` from `@tiptap/static-renderer/pm/html-string` with the same extensions as the CMS (e.g. Document, Paragraph, Heading, Bold, Italic, Blockquote, BulletList, ListItem, Link, Image), then render the HTML in a wrapper `div` (the output is safe TipTap-generated HTML).
78
76
  - **Client:** Use `renderToReactElement` from `@tiptap/static-renderer/pm/react` for a React node with no `dangerouslySetInnerHTML`.
@@ -85,15 +83,15 @@ Use the same extensions as the CMS editor for full parity. See [TipTap static re
85
83
 
86
84
  ```tsx
87
85
  // app/blog/page.tsx
88
- import { getPosts } from "@dispatchcms/next";
86
+ import { getEntries } from "@dispatchcms/next";
89
87
 
90
88
  export default async function BlogPage() {
91
- const posts = await getPosts();
89
+ const entries = await getEntries("posts");
92
90
  return (
93
91
  <ul>
94
- {posts.map((post) => (
95
- <li key={post.id}>
96
- <a href={`/blog/${post.slug}`}>{post.title}</a>
92
+ {entries.map((entry) => (
93
+ <li key={entry.id}>
94
+ <a href={`/blog/${entry.slug}`}>{entry.title}</a>
97
95
  </li>
98
96
  ))}
99
97
  </ul>
@@ -101,21 +99,20 @@ export default async function BlogPage() {
101
99
  }
102
100
  ```
103
101
 
104
- **Single post (Server Component):**
102
+ **Single entry (Server Component):**
105
103
 
106
104
  ```tsx
107
105
  // app/blog/[slug]/page.tsx
108
- import { getPost } from "@dispatchcms/next";
106
+ import { getEntry } from "@dispatchcms/next";
109
107
  import { notFound } from "next/navigation";
110
108
 
111
109
  export default async function PostPage({ params }: { params: { slug: string } }) {
112
- const post = await getPost(params.slug);
113
- if (!post) notFound();
110
+ const entry = await getEntry("posts", params.slug);
111
+ if (!entry) notFound();
114
112
  return (
115
113
  <article>
116
- <h1>{post.title}</h1>
117
- {post.excerpt && <p>{post.excerpt}</p>}
118
- {/* Render post.content with TipTap: renderToHTMLString(extensions, post.content) then a div, or use renderToReactElement in a client component */}
114
+ <h1>{entry.title}</h1>
115
+ {/* Render entry.data.body with TipTap */}
119
116
  </article>
120
117
  );
121
118
  }
@@ -129,7 +126,7 @@ To support public preview links from the CMS (e.g. `https://yoursite.com/preview
129
126
 
130
127
  ```tsx
131
128
  // app/preview/page.tsx
132
- import { getPostByPreviewToken } from "@dispatchcms/next";
129
+ import { getEntryByPreviewToken } from "@dispatchcms/next";
133
130
  import { notFound } from "next/navigation";
134
131
 
135
132
  export default async function PreviewPage({
@@ -139,14 +136,12 @@ export default async function PreviewPage({
139
136
  }) {
140
137
  const { token } = await searchParams;
141
138
  if (!token) notFound();
142
- const post = await getPostByPreviewToken(token);
143
- if (!post) notFound();
139
+ const entry = await getEntryByPreviewToken(token);
140
+ if (!entry) notFound();
144
141
  return (
145
142
  <article>
146
143
  <p className="text-sm text-muted-foreground">Preview</p>
147
- <h1>{post.title}</h1>
148
- {post.excerpt && <p>{post.excerpt}</p>}
149
- {/* Render post.content with TipTap (same as your live post page) */}
144
+ <h1>{entry.title}</h1>
150
145
  </article>
151
146
  );
152
147
  }
package/dist/index.d.mts CHANGED
@@ -1,14 +1,14 @@
1
- type Post = {
1
+ type Entry = {
2
2
  id: string;
3
3
  created_at: string;
4
- published_at: string;
4
+ published_at: string | null;
5
5
  updated_at: string;
6
6
  site_id: string;
7
+ collection: string;
7
8
  title: string;
8
- slug: string;
9
- content: unknown;
10
- excerpt: string | null;
11
- featured_image: string | null;
9
+ slug: string | null;
10
+ data: Record<string, unknown>;
11
+ sort_order: number;
12
12
  published: boolean;
13
13
  };
14
14
  type DispatchConfig = {
@@ -18,12 +18,9 @@ declare function initDispatch(options: DispatchConfig): void;
18
18
  declare function getConfig(): {
19
19
  siteKey: string;
20
20
  };
21
- declare function getPosts(siteKey?: string): Promise<Post[]>;
22
- declare function getPost(slug: string, siteKey?: string): Promise<Post | null>;
23
- /**
24
- * Fetch a single post by its preview token (for preview/draft pages).
25
- * No site key required. Use this in your app's preview route (e.g. /preview?token=...).
26
- */
27
- declare function getPostByPreviewToken(token: string): Promise<Post | null>;
21
+ declare function getEntries(collection: string, siteKey?: string): Promise<Entry[]>;
22
+ declare function getEntry(collection: string, slug: string, siteKey?: string): Promise<Entry | null>;
23
+ /** Draft or published entry. The token is the secret; no site key is required. */
24
+ declare function getEntryByPreviewToken(token: string): Promise<Entry | null>;
28
25
 
29
- export { type DispatchConfig, type Post, getConfig, getPost, getPostByPreviewToken, getPosts, initDispatch };
26
+ export { type DispatchConfig, type Entry, getConfig, getEntries, getEntry, getEntryByPreviewToken, initDispatch };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- type Post = {
1
+ type Entry = {
2
2
  id: string;
3
3
  created_at: string;
4
- published_at: string;
4
+ published_at: string | null;
5
5
  updated_at: string;
6
6
  site_id: string;
7
+ collection: string;
7
8
  title: string;
8
- slug: string;
9
- content: unknown;
10
- excerpt: string | null;
11
- featured_image: string | null;
9
+ slug: string | null;
10
+ data: Record<string, unknown>;
11
+ sort_order: number;
12
12
  published: boolean;
13
13
  };
14
14
  type DispatchConfig = {
@@ -18,12 +18,9 @@ declare function initDispatch(options: DispatchConfig): void;
18
18
  declare function getConfig(): {
19
19
  siteKey: string;
20
20
  };
21
- declare function getPosts(siteKey?: string): Promise<Post[]>;
22
- declare function getPost(slug: string, siteKey?: string): Promise<Post | null>;
23
- /**
24
- * Fetch a single post by its preview token (for preview/draft pages).
25
- * No site key required. Use this in your app's preview route (e.g. /preview?token=...).
26
- */
27
- declare function getPostByPreviewToken(token: string): Promise<Post | null>;
21
+ declare function getEntries(collection: string, siteKey?: string): Promise<Entry[]>;
22
+ declare function getEntry(collection: string, slug: string, siteKey?: string): Promise<Entry | null>;
23
+ /** Draft or published entry. The token is the secret; no site key is required. */
24
+ declare function getEntryByPreviewToken(token: string): Promise<Entry | null>;
28
25
 
29
- export { type DispatchConfig, type Post, getConfig, getPost, getPostByPreviewToken, getPosts, initDispatch };
26
+ export { type DispatchConfig, type Entry, getConfig, getEntries, getEntry, getEntryByPreviewToken, initDispatch };
package/dist/index.js CHANGED
@@ -21,9 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  getConfig: () => getConfig,
24
- getPost: () => getPost,
25
- getPostByPreviewToken: () => getPostByPreviewToken,
26
- getPosts: () => getPosts,
24
+ getEntries: () => getEntries,
25
+ getEntry: () => getEntry,
26
+ getEntryByPreviewToken: () => getEntryByPreviewToken,
27
27
  initDispatch: () => initDispatch
28
28
  });
29
29
  module.exports = __toCommonJS(index_exports);
@@ -42,10 +42,10 @@ function getConfig() {
42
42
  }
43
43
  return { siteKey };
44
44
  }
45
- async function getPosts(siteKey) {
45
+ async function getEntries(collection, siteKey) {
46
46
  const { siteKey: key } = getConfig();
47
47
  const resolvedKey = siteKey ?? key;
48
- const url = `${DISPATCH_API_BASE}/api/posts`;
48
+ const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}`;
49
49
  const res = await fetch(url, {
50
50
  headers: { "X-Site-Key": resolvedKey }
51
51
  });
@@ -53,14 +53,14 @@ async function getPosts(siteKey) {
53
53
  if (res.status === 401) {
54
54
  throw new Error("Invalid or missing site key");
55
55
  }
56
- throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);
56
+ throw new Error(`Failed to fetch entries: ${res.status} ${res.statusText}`);
57
57
  }
58
58
  return await res.json();
59
59
  }
60
- async function getPost(slug, siteKey) {
60
+ async function getEntry(collection, slug, siteKey) {
61
61
  const { siteKey: key } = getConfig();
62
62
  const resolvedKey = siteKey ?? key;
63
- const url = `${DISPATCH_API_BASE}/api/posts/${encodeURIComponent(slug)}`;
63
+ const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}`;
64
64
  const res = await fetch(url, {
65
65
  headers: { "X-Site-Key": resolvedKey }
66
66
  });
@@ -71,11 +71,11 @@ async function getPost(slug, siteKey) {
71
71
  if (res.status === 401) {
72
72
  throw new Error("Invalid or missing site key");
73
73
  }
74
- throw new Error(`Failed to fetch post: ${res.status} ${res.statusText}`);
74
+ throw new Error(`Failed to fetch entry: ${res.status} ${res.statusText}`);
75
75
  }
76
76
  return await res.json();
77
77
  }
78
- async function getPostByPreviewToken(token) {
78
+ async function getEntryByPreviewToken(token) {
79
79
  if (!token?.trim()) return null;
80
80
  const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;
81
81
  const res = await fetch(url);
@@ -88,9 +88,9 @@ async function getPostByPreviewToken(token) {
88
88
  // Annotate the CommonJS export names for ESM import in node:
89
89
  0 && (module.exports = {
90
90
  getConfig,
91
- getPost,
92
- getPostByPreviewToken,
93
- getPosts,
91
+ getEntries,
92
+ getEntry,
93
+ getEntryByPreviewToken,
94
94
  initDispatch
95
95
  });
96
96
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export type { Post, DispatchConfig } from \"./client\";\nexport {\n initDispatch,\n getConfig,\n getPosts,\n getPost,\n getPostByPreviewToken,\n} from \"./client\";\n","export type Post = {\n id: string;\n created_at: string;\n published_at: string;\n updated_at: string;\n site_id: string;\n title: string;\n slug: string;\n content: unknown;\n excerpt: string | null;\n featured_image: string | null;\n published: boolean;\n};\n\nconst DISPATCH_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport type DispatchConfig = {\n siteKey: string;\n};\n\nlet config: DispatchConfig | null = null;\n\nexport function initDispatch(options: DispatchConfig): void {\n config = { siteKey: options.siteKey };\n}\n\nexport function getConfig(): { siteKey: string } {\n const envKey = typeof process !== \"undefined\" ? process.env?.NEXT_PUBLIC_DISPATCH_SITE_KEY : undefined;\n const siteKey = config?.siteKey ?? (typeof envKey === \"string\" ? envKey : undefined);\n if (!siteKey || typeof siteKey !== \"string\") {\n throw new Error(\"Dispatch site key is required. Call initDispatch({ siteKey }) or set NEXT_PUBLIC_DISPATCH_SITE_KEY.\");\n }\n return { siteKey };\n}\n\nexport async function getPosts(siteKey?: string): Promise<Post[]> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/posts`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Post[];\n}\n\nexport async function getPost(slug: string, siteKey?: string): Promise<Post | null> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/posts/${encodeURIComponent(slug)}`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (res.status === 404) {\n return null;\n }\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch post: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Post;\n}\n\n/**\n * Fetch a single post by its preview token (for preview/draft pages).\n * No site key required. Use this in your app's preview route (e.g. /preview?token=...).\n */\nexport async function getPostByPreviewToken(token: string): Promise<Post | null> {\n if (!token?.trim()) return null;\n const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;\n const res = await fetch(url);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Failed to fetch preview: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Post;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcA,IAAM,oBAAoB;AAM1B,IAAI,SAAgC;AAE7B,SAAS,aAAa,SAA+B;AAC1D,WAAS,EAAE,SAAS,QAAQ,QAAQ;AACtC;AAEO,SAAS,YAAiC;AAC/C,QAAM,SAAS,OAAO,YAAY,cAAc,QAAQ,KAAK,gCAAgC;AAC7F,QAAM,UAAU,QAAQ,YAAY,OAAO,WAAW,WAAW,SAAS;AAC1E,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,MAAM,qGAAqG;AAAA,EACvH;AACA,SAAO,EAAE,QAAQ;AACnB;AAEA,eAAsB,SAAS,SAAmC;AAChE,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB;AAChC,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC1E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,QAAQ,MAAc,SAAwC;AAClF,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB,cAAc,mBAAmB,IAAI,CAAC;AACtE,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,IAAI,WAAW,KAAK;AACtB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,yBAAyB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EACzE;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAMA,eAAsB,sBAAsB,OAAqC;AAC/E,MAAI,CAAC,OAAO,KAAK,EAAG,QAAO;AAC3B,QAAM,MAAM,GAAG,iBAAiB,sBAAsB,mBAAmB,MAAM,KAAK,CAAC,CAAC;AACtF,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC5E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export type { Entry, DispatchConfig } from \"./client\";\nexport {\n initDispatch,\n getConfig,\n getEntries,\n getEntry,\n getEntryByPreviewToken,\n} from \"./client\";\n","export type Entry = {\n id: string;\n created_at: string;\n published_at: string | null;\n updated_at: string;\n site_id: string;\n collection: string;\n title: string;\n slug: string | null;\n data: Record<string, unknown>;\n sort_order: number;\n published: boolean;\n};\n\nconst DISPATCH_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport type DispatchConfig = {\n siteKey: string;\n};\n\nlet config: DispatchConfig | null = null;\n\nexport function initDispatch(options: DispatchConfig): void {\n config = { siteKey: options.siteKey };\n}\n\nexport function getConfig(): { siteKey: string } {\n const envKey = typeof process !== \"undefined\" ? process.env?.NEXT_PUBLIC_DISPATCH_SITE_KEY : undefined;\n const siteKey = config?.siteKey ?? (typeof envKey === \"string\" ? envKey : undefined);\n if (!siteKey || typeof siteKey !== \"string\") {\n throw new Error(\"Dispatch site key is required. Call initDispatch({ siteKey }) or set NEXT_PUBLIC_DISPATCH_SITE_KEY.\");\n }\n return { siteKey };\n}\n\nexport async function getEntries(collection: string, siteKey?: string): Promise<Entry[]> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch entries: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Entry[];\n}\n\nexport async function getEntry(collection: string, slug: string, siteKey?: string): Promise<Entry | null> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (res.status === 404) {\n return null;\n }\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch entry: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Entry;\n}\n\n/** Draft or published entry. The token is the secret; no site key is required. */\nexport async function getEntryByPreviewToken(token: string): Promise<Entry | null> {\n if (!token?.trim()) return null;\n const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;\n const res = await fetch(url);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Failed to fetch preview: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Entry;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcA,IAAM,oBAAoB;AAM1B,IAAI,SAAgC;AAE7B,SAAS,aAAa,SAA+B;AAC1D,WAAS,EAAE,SAAS,QAAQ,QAAQ;AACtC;AAEO,SAAS,YAAiC;AAC/C,QAAM,SAAS,OAAO,YAAY,cAAc,QAAQ,KAAK,gCAAgC;AAC7F,QAAM,UAAU,QAAQ,YAAY,OAAO,WAAW,WAAW,SAAS;AAC1E,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,MAAM,qGAAqG;AAAA,EACvH;AACA,SAAO,EAAE,QAAQ;AACnB;AAEA,eAAsB,WAAW,YAAoB,SAAoC;AACvF,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB,gBAAgB,mBAAmB,UAAU,CAAC;AAC9E,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC5E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,SAAS,YAAoB,MAAc,SAAyC;AACxG,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB,gBAAgB,mBAAmB,UAAU,CAAC,IAAI,mBAAmB,IAAI,CAAC;AAC1G,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,IAAI,WAAW,KAAK;AACtB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC1E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAGA,eAAsB,uBAAuB,OAAsC;AACjF,MAAI,CAAC,OAAO,KAAK,EAAG,QAAO;AAC3B,QAAM,MAAM,GAAG,iBAAiB,sBAAsB,mBAAmB,MAAM,KAAK,CAAC,CAAC;AACtF,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC5E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;","names":[]}
package/dist/index.mjs CHANGED
@@ -12,10 +12,10 @@ function getConfig() {
12
12
  }
13
13
  return { siteKey };
14
14
  }
15
- async function getPosts(siteKey) {
15
+ async function getEntries(collection, siteKey) {
16
16
  const { siteKey: key } = getConfig();
17
17
  const resolvedKey = siteKey ?? key;
18
- const url = `${DISPATCH_API_BASE}/api/posts`;
18
+ const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}`;
19
19
  const res = await fetch(url, {
20
20
  headers: { "X-Site-Key": resolvedKey }
21
21
  });
@@ -23,14 +23,14 @@ async function getPosts(siteKey) {
23
23
  if (res.status === 401) {
24
24
  throw new Error("Invalid or missing site key");
25
25
  }
26
- throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);
26
+ throw new Error(`Failed to fetch entries: ${res.status} ${res.statusText}`);
27
27
  }
28
28
  return await res.json();
29
29
  }
30
- async function getPost(slug, siteKey) {
30
+ async function getEntry(collection, slug, siteKey) {
31
31
  const { siteKey: key } = getConfig();
32
32
  const resolvedKey = siteKey ?? key;
33
- const url = `${DISPATCH_API_BASE}/api/posts/${encodeURIComponent(slug)}`;
33
+ const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}`;
34
34
  const res = await fetch(url, {
35
35
  headers: { "X-Site-Key": resolvedKey }
36
36
  });
@@ -41,11 +41,11 @@ async function getPost(slug, siteKey) {
41
41
  if (res.status === 401) {
42
42
  throw new Error("Invalid or missing site key");
43
43
  }
44
- throw new Error(`Failed to fetch post: ${res.status} ${res.statusText}`);
44
+ throw new Error(`Failed to fetch entry: ${res.status} ${res.statusText}`);
45
45
  }
46
46
  return await res.json();
47
47
  }
48
- async function getPostByPreviewToken(token) {
48
+ async function getEntryByPreviewToken(token) {
49
49
  if (!token?.trim()) return null;
50
50
  const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;
51
51
  const res = await fetch(url);
@@ -57,9 +57,9 @@ async function getPostByPreviewToken(token) {
57
57
  }
58
58
  export {
59
59
  getConfig,
60
- getPost,
61
- getPostByPreviewToken,
62
- getPosts,
60
+ getEntries,
61
+ getEntry,
62
+ getEntryByPreviewToken,
63
63
  initDispatch
64
64
  };
65
65
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["export type Post = {\n id: string;\n created_at: string;\n published_at: string;\n updated_at: string;\n site_id: string;\n title: string;\n slug: string;\n content: unknown;\n excerpt: string | null;\n featured_image: string | null;\n published: boolean;\n};\n\nconst DISPATCH_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport type DispatchConfig = {\n siteKey: string;\n};\n\nlet config: DispatchConfig | null = null;\n\nexport function initDispatch(options: DispatchConfig): void {\n config = { siteKey: options.siteKey };\n}\n\nexport function getConfig(): { siteKey: string } {\n const envKey = typeof process !== \"undefined\" ? process.env?.NEXT_PUBLIC_DISPATCH_SITE_KEY : undefined;\n const siteKey = config?.siteKey ?? (typeof envKey === \"string\" ? envKey : undefined);\n if (!siteKey || typeof siteKey !== \"string\") {\n throw new Error(\"Dispatch site key is required. Call initDispatch({ siteKey }) or set NEXT_PUBLIC_DISPATCH_SITE_KEY.\");\n }\n return { siteKey };\n}\n\nexport async function getPosts(siteKey?: string): Promise<Post[]> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/posts`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Post[];\n}\n\nexport async function getPost(slug: string, siteKey?: string): Promise<Post | null> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/posts/${encodeURIComponent(slug)}`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (res.status === 404) {\n return null;\n }\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch post: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Post;\n}\n\n/**\n * Fetch a single post by its preview token (for preview/draft pages).\n * No site key required. Use this in your app's preview route (e.g. /preview?token=...).\n */\nexport async function getPostByPreviewToken(token: string): Promise<Post | null> {\n if (!token?.trim()) return null;\n const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;\n const res = await fetch(url);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Failed to fetch preview: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Post;\n}\n"],"mappings":";AAcA,IAAM,oBAAoB;AAM1B,IAAI,SAAgC;AAE7B,SAAS,aAAa,SAA+B;AAC1D,WAAS,EAAE,SAAS,QAAQ,QAAQ;AACtC;AAEO,SAAS,YAAiC;AAC/C,QAAM,SAAS,OAAO,YAAY,cAAc,QAAQ,KAAK,gCAAgC;AAC7F,QAAM,UAAU,QAAQ,YAAY,OAAO,WAAW,WAAW,SAAS;AAC1E,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,MAAM,qGAAqG;AAAA,EACvH;AACA,SAAO,EAAE,QAAQ;AACnB;AAEA,eAAsB,SAAS,SAAmC;AAChE,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB;AAChC,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC1E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,QAAQ,MAAc,SAAwC;AAClF,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB,cAAc,mBAAmB,IAAI,CAAC;AACtE,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,IAAI,WAAW,KAAK;AACtB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,yBAAyB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EACzE;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAMA,eAAsB,sBAAsB,OAAqC;AAC/E,MAAI,CAAC,OAAO,KAAK,EAAG,QAAO;AAC3B,QAAM,MAAM,GAAG,iBAAiB,sBAAsB,mBAAmB,MAAM,KAAK,CAAC,CAAC;AACtF,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC5E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;","names":[]}
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["export type Entry = {\n id: string;\n created_at: string;\n published_at: string | null;\n updated_at: string;\n site_id: string;\n collection: string;\n title: string;\n slug: string | null;\n data: Record<string, unknown>;\n sort_order: number;\n published: boolean;\n};\n\nconst DISPATCH_API_BASE = \"https://dispatch-cms.vercel.app\";\n\nexport type DispatchConfig = {\n siteKey: string;\n};\n\nlet config: DispatchConfig | null = null;\n\nexport function initDispatch(options: DispatchConfig): void {\n config = { siteKey: options.siteKey };\n}\n\nexport function getConfig(): { siteKey: string } {\n const envKey = typeof process !== \"undefined\" ? process.env?.NEXT_PUBLIC_DISPATCH_SITE_KEY : undefined;\n const siteKey = config?.siteKey ?? (typeof envKey === \"string\" ? envKey : undefined);\n if (!siteKey || typeof siteKey !== \"string\") {\n throw new Error(\"Dispatch site key is required. Call initDispatch({ siteKey }) or set NEXT_PUBLIC_DISPATCH_SITE_KEY.\");\n }\n return { siteKey };\n}\n\nexport async function getEntries(collection: string, siteKey?: string): Promise<Entry[]> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch entries: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Entry[];\n}\n\nexport async function getEntry(collection: string, slug: string, siteKey?: string): Promise<Entry | null> {\n const { siteKey: key } = getConfig();\n const resolvedKey = siteKey ?? key;\n const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}`;\n const res = await fetch(url, {\n headers: { \"X-Site-Key\": resolvedKey },\n });\n if (res.status === 404) {\n return null;\n }\n if (!res.ok) {\n if (res.status === 401) {\n throw new Error(\"Invalid or missing site key\");\n }\n throw new Error(`Failed to fetch entry: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Entry;\n}\n\n/** Draft or published entry. The token is the secret; no site key is required. */\nexport async function getEntryByPreviewToken(token: string): Promise<Entry | null> {\n if (!token?.trim()) return null;\n const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;\n const res = await fetch(url);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Failed to fetch preview: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Entry;\n}\n"],"mappings":";AAcA,IAAM,oBAAoB;AAM1B,IAAI,SAAgC;AAE7B,SAAS,aAAa,SAA+B;AAC1D,WAAS,EAAE,SAAS,QAAQ,QAAQ;AACtC;AAEO,SAAS,YAAiC;AAC/C,QAAM,SAAS,OAAO,YAAY,cAAc,QAAQ,KAAK,gCAAgC;AAC7F,QAAM,UAAU,QAAQ,YAAY,OAAO,WAAW,WAAW,SAAS;AAC1E,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,MAAM,qGAAqG;AAAA,EACvH;AACA,SAAO,EAAE,QAAQ;AACnB;AAEA,eAAsB,WAAW,YAAoB,SAAoC;AACvF,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB,gBAAgB,mBAAmB,UAAU,CAAC;AAC9E,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC5E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAsB,SAAS,YAAoB,MAAc,SAAyC;AACxG,QAAM,EAAE,SAAS,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,WAAW;AAC/B,QAAM,MAAM,GAAG,iBAAiB,gBAAgB,mBAAmB,UAAU,CAAC,IAAI,mBAAmB,IAAI,CAAC;AAC1G,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,SAAS,EAAE,cAAc,YAAY;AAAA,EACvC,CAAC;AACD,MAAI,IAAI,WAAW,KAAK;AACtB,WAAO;AAAA,EACT;AACA,MAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AACA,UAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC1E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAGA,eAAsB,uBAAuB,OAAsC;AACjF,MAAI,CAAC,OAAO,KAAK,EAAG,QAAO;AAC3B,QAAM,MAAM,GAAG,iBAAiB,sBAAsB,mBAAmB,MAAM,KAAK,CAAC,CAAC;AACtF,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,4BAA4B,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,EAC5E;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dispatchcms/next",
3
- "version": "0.0.6",
4
- "description": "Fetch published posts from a Dispatch CMS in Next.js",
3
+ "version": "0.0.7",
4
+ "description": "Fetch published entries from a Dispatch CMS in Next.js",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -12,15 +12,11 @@
12
12
  "require": "./dist/index.js"
13
13
  }
14
14
  },
15
- "scripts": {
16
- "build": "tsup",
17
- "test": "tsx test/client.cache.test.ts",
18
- "test:manual": "tsx test/getPage.test.ts"
19
- },
20
15
  "devDependencies": {
21
- "@types/node": "^22.10.1",
16
+ "@types/node": "^22.20.1",
17
+ "next": "^16.3.0",
22
18
  "tsup": "^8.3.5",
23
- "tsx": "^4.19.2",
19
+ "tsx": "4.23.8",
24
20
  "typescript": "^5.7.2"
25
21
  },
26
22
  "engines": {
@@ -35,5 +31,10 @@
35
31
  "license": "MIT",
36
32
  "peerDependencies": {
37
33
  "next": ">=16.0.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup",
37
+ "test": "tsx test/client.cache.test.ts",
38
+ "test:manual": "tsx test/getPage.test.ts"
38
39
  }
39
40
  }
package/src/client.ts CHANGED
@@ -1,14 +1,14 @@
1
- export type Post = {
1
+ export type Entry = {
2
2
  id: string;
3
3
  created_at: string;
4
- published_at: string;
4
+ published_at: string | null;
5
5
  updated_at: string;
6
6
  site_id: string;
7
+ collection: string;
7
8
  title: string;
8
- slug: string;
9
- content: unknown;
10
- excerpt: string | null;
11
- featured_image: string | null;
9
+ slug: string | null;
10
+ data: Record<string, unknown>;
11
+ sort_order: number;
12
12
  published: boolean;
13
13
  };
14
14
 
@@ -33,10 +33,10 @@ export function getConfig(): { siteKey: string } {
33
33
  return { siteKey };
34
34
  }
35
35
 
36
- export async function getPosts(siteKey?: string): Promise<Post[]> {
36
+ export async function getEntries(collection: string, siteKey?: string): Promise<Entry[]> {
37
37
  const { siteKey: key } = getConfig();
38
38
  const resolvedKey = siteKey ?? key;
39
- const url = `${DISPATCH_API_BASE}/api/posts`;
39
+ const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}`;
40
40
  const res = await fetch(url, {
41
41
  headers: { "X-Site-Key": resolvedKey },
42
42
  });
@@ -44,15 +44,15 @@ export async function getPosts(siteKey?: string): Promise<Post[]> {
44
44
  if (res.status === 401) {
45
45
  throw new Error("Invalid or missing site key");
46
46
  }
47
- throw new Error(`Failed to fetch posts: ${res.status} ${res.statusText}`);
47
+ throw new Error(`Failed to fetch entries: ${res.status} ${res.statusText}`);
48
48
  }
49
- return (await res.json()) as Post[];
49
+ return (await res.json()) as Entry[];
50
50
  }
51
51
 
52
- export async function getPost(slug: string, siteKey?: string): Promise<Post | null> {
52
+ export async function getEntry(collection: string, slug: string, siteKey?: string): Promise<Entry | null> {
53
53
  const { siteKey: key } = getConfig();
54
54
  const resolvedKey = siteKey ?? key;
55
- const url = `${DISPATCH_API_BASE}/api/posts/${encodeURIComponent(slug)}`;
55
+ const url = `${DISPATCH_API_BASE}/api/content/${encodeURIComponent(collection)}/${encodeURIComponent(slug)}`;
56
56
  const res = await fetch(url, {
57
57
  headers: { "X-Site-Key": resolvedKey },
58
58
  });
@@ -63,16 +63,13 @@ export async function getPost(slug: string, siteKey?: string): Promise<Post | nu
63
63
  if (res.status === 401) {
64
64
  throw new Error("Invalid or missing site key");
65
65
  }
66
- throw new Error(`Failed to fetch post: ${res.status} ${res.statusText}`);
66
+ throw new Error(`Failed to fetch entry: ${res.status} ${res.statusText}`);
67
67
  }
68
- return (await res.json()) as Post;
68
+ return (await res.json()) as Entry;
69
69
  }
70
70
 
71
- /**
72
- * Fetch a single post by its preview token (for preview/draft pages).
73
- * No site key required. Use this in your app's preview route (e.g. /preview?token=...).
74
- */
75
- export async function getPostByPreviewToken(token: string): Promise<Post | null> {
71
+ /** Draft or published entry. The token is the secret; no site key is required. */
72
+ export async function getEntryByPreviewToken(token: string): Promise<Entry | null> {
76
73
  if (!token?.trim()) return null;
77
74
  const url = `${DISPATCH_API_BASE}/api/preview?token=${encodeURIComponent(token.trim())}`;
78
75
  const res = await fetch(url);
@@ -80,5 +77,5 @@ export async function getPostByPreviewToken(token: string): Promise<Post | null>
80
77
  if (!res.ok) {
81
78
  throw new Error(`Failed to fetch preview: ${res.status} ${res.statusText}`);
82
79
  }
83
- return (await res.json()) as Post;
80
+ return (await res.json()) as Entry;
84
81
  }
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
- export type { Post, DispatchConfig } from "./client";
1
+ export type { Entry, DispatchConfig } from "./client";
2
2
  export {
3
3
  initDispatch,
4
4
  getConfig,
5
- getPosts,
6
- getPost,
7
- getPostByPreviewToken,
5
+ getEntries,
6
+ getEntry,
7
+ getEntryByPreviewToken,
8
8
  } from "./client";
@@ -1,36 +1,36 @@
1
- import { initDispatch, getPosts, getPost } from "../src/index";
1
+ import { initDispatch, getEntries, getEntry } from "../src/index";
2
2
 
3
- const mockPost = (slug: string, title: string) => ({
3
+ const mockEntry = (slug: string, title: string) => ({
4
4
  id: `id-${slug}`,
5
5
  created_at: "",
6
6
  published_at: "",
7
7
  updated_at: "",
8
8
  site_id: "",
9
+ collection: "posts",
9
10
  title,
10
11
  slug,
11
- content: null,
12
- excerpt: null,
13
- featured_image: null,
12
+ data: {},
13
+ sort_order: 0,
14
14
  published: true,
15
15
  });
16
16
 
17
17
  type FetchCall = { url: string };
18
18
  const fetchCalls: FetchCall[] = [];
19
- let listResponse: ReturnType<typeof mockPost>[] = [];
20
- let singleResponse: ReturnType<typeof mockPost> | null = null;
19
+ let listResponse: ReturnType<typeof mockEntry>[] = [];
20
+ let singleResponse: ReturnType<typeof mockEntry> | null = null;
21
21
  let singleStatus = 200;
22
22
 
23
23
  function mockFetch(url: string, _options?: RequestInit): Promise<Response> {
24
24
  fetchCalls.push({ url });
25
25
  const u = new URL(url);
26
- if (u.pathname === "/api/posts") {
26
+ if (u.pathname === "/api/content/posts") {
27
27
  return Promise.resolve({
28
28
  ok: true,
29
29
  status: 200,
30
30
  json: () => Promise.resolve(listResponse),
31
31
  } as Response);
32
32
  }
33
- const slugMatch = u.pathname.match(/^\/api\/posts\/(.+)$/);
33
+ const slugMatch = u.pathname.match(/^\/api\/content\/posts\/(.+)$/);
34
34
  if (slugMatch) {
35
35
  const slug = decodeURIComponent(slugMatch[1]);
36
36
  if (singleStatus === 404) {
@@ -42,7 +42,7 @@ function mockFetch(url: string, _options?: RequestInit): Promise<Response> {
42
42
  return Promise.resolve({
43
43
  ok: true,
44
44
  status: 200,
45
- json: () => Promise.resolve(singleResponse ?? mockPost(slug, "Single")),
45
+ json: () => Promise.resolve(singleResponse ?? mockEntry(slug, "Single")),
46
46
  } as Response);
47
47
  }
48
48
  return Promise.reject(new Error(`Unexpected URL: ${url}`));
@@ -61,40 +61,36 @@ async function run(): Promise<void> {
61
61
  initDispatch({ siteKey: "pk_test" });
62
62
 
63
63
  try {
64
- // Test 1: getPosts() fetches and returns the list (no in-memory cache)
65
64
  fetchCalls.length = 0;
66
- listResponse = [mockPost("a", "Post A"), mockPost("b", "Post B")];
67
- const list1 = await getPosts("pk_1");
68
- assert(fetchCalls.length === 1, "getPosts triggers one fetch");
65
+ listResponse = [mockEntry("a", "Post A"), mockEntry("b", "Post B")];
66
+ const list1 = await getEntries("posts", "pk_1");
67
+ assert(fetchCalls.length === 1, "getEntries triggers one fetch");
69
68
  assert(list1.length === 2 && list1[0].slug === "a", "returns correct list");
70
- const list2 = await getPosts("pk_1");
71
- assert(fetchCalls.length === 2, "second getPosts triggers another fetch (no cache)");
69
+ const list2 = await getEntries("posts", "pk_1");
70
+ assert(fetchCalls.length === 2, "second getEntries triggers another fetch (no cache)");
72
71
  assert(list2.length === 2, "second call returns same list");
73
72
 
74
- // Test 2: getPost(slug) fetches and returns the post
75
73
  fetchCalls.length = 0;
76
- singleResponse = mockPost("my-slug", "My Post");
77
- const post = await getPost("my-slug", "pk_2");
78
- assert(fetchCalls.length === 1, "getPost triggers one fetch");
79
- assert(post !== null && post.slug === "my-slug" && post.title === "My Post", "returns correct post");
74
+ singleResponse = mockEntry("my-slug", "My Post");
75
+ const entry = await getEntry("posts", "my-slug", "pk_2");
76
+ assert(fetchCalls.length === 1, "getEntry triggers one fetch");
77
+ assert(entry !== null && entry.slug === "my-slug" && entry.title === "My Post", "returns correct entry");
80
78
 
81
- // Test 3: getPost(slug) for missing slug returns null
82
79
  fetchCalls.length = 0;
83
80
  singleStatus = 404;
84
- const missing = await getPost("nonexistent", "pk_3");
85
- assert(fetchCalls.length === 1, "getPost triggers one fetch");
81
+ const missing = await getEntry("posts", "nonexistent", "pk_3");
82
+ assert(fetchCalls.length === 1, "getEntry triggers one fetch");
86
83
  assert(missing === null, "returns null for 404");
87
84
  singleStatus = 200;
88
85
 
89
- // Test 4: getPost(slug) then getPosts() each trigger their own fetch
90
86
  fetchCalls.length = 0;
91
- singleResponse = mockPost("first", "First Post");
92
- listResponse = [mockPost("first", "First Post")];
93
- const singleFirst = await getPost("first", "pk_4");
94
- assert(fetchCalls.length === 1 && singleFirst?.slug === "first", "getPost fetches");
95
- const listAfter = await getPosts("pk_4");
96
- assert(fetchCalls.length === 2, "getPosts triggers second fetch");
97
- assert(listAfter.length === 1 && listAfter[0].slug === "first", "getPosts returns correct list");
87
+ singleResponse = mockEntry("first", "First Post");
88
+ listResponse = [mockEntry("first", "First Post")];
89
+ const singleFirst = await getEntry("posts", "first", "pk_4");
90
+ assert(fetchCalls.length === 1 && singleFirst?.slug === "first", "getEntry fetches");
91
+ const listAfter = await getEntries("posts", "pk_4");
92
+ assert(fetchCalls.length === 2, "getEntries triggers second fetch");
93
+ assert(listAfter.length === 1 && listAfter[0].slug === "first", "getEntries returns correct list");
98
94
  } finally {
99
95
  globalThis.fetch = originalFetch;
100
96
  }
@@ -1,10 +1,10 @@
1
- import { initDispatch, getPosts } from "../src/index";
1
+ import { initDispatch, getEntries } from "../src/index";
2
2
 
3
3
  async function main() {
4
4
  initDispatch({
5
5
  siteKey: process.env.NEXT_PUBLIC_DISPATCH_SITE_KEY ?? "pk_test",
6
6
  });
7
- const result = await getPosts();
7
+ const result = await getEntries("posts");
8
8
  console.log(result);
9
9
  }
10
10