notionpress 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vivillies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # notionpress
2
+
3
+ ![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)
4
+ ![Runtime: Bun](https://img.shields.io/badge/runtime-bun-000000?logo=bun)
5
+ ![TypeScript](https://img.shields.io/badge/language-TypeScript-3178c6?logo=typescript&logoColor=white)
6
+
7
+ **Turn a Notion page into a headless CMS for your blog.**
8
+
9
+ `notionpress` is a lightweight TypeScript client that provisions a blog database on any Notion page (with a ready-made schema for title, slug, status, publish date, and cover image), and gives you a typed API to fetch posts and their content as markdown — no manual database setup required.
10
+
11
+ ## Features
12
+
13
+ - 🗄️ **Zero-config provisioning** — point it at a Notion page and it creates a database with the right schema automatically.
14
+ - 📝 **Typed posts** — `getPosts()` returns clean, typed `Post` objects instead of raw Notion API responses.
15
+ - 📄 **Markdown content** — `getPostContent(id)` returns a post's body as ready-to-render markdown.
16
+ - 💾 **Local caching** — database/data source IDs are cached on disk so you're not re-creating a database on every run.
17
+ - 🔌 **Bring your own database** — already have a Notion database? Pass its `datasource_id` and skip auto-provisioning entirely.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ bun add notionpress
23
+ # or
24
+ npm install notionpress
25
+ # or
26
+ pnpm add notionpress
27
+ ```
28
+
29
+ ## Prerequisites
30
+
31
+ 1. **Create a Notion integration** at [notion.so/my-integrations](https://www.notion.so/my-integrations) and copy its **Internal Integration Secret** — this is your `api_key`.
32
+ 2. **Share a Notion page** with that integration (`•••` menu → *Connections* → select your integration). This page is where your blog database will live — copy its **page ID** from the URL.
33
+
34
+ ## Quick Start
35
+
36
+ ```ts
37
+ import { Notionpress } from "notionpress";
38
+
39
+ const blog = new Notionpress({
40
+ api_key: process.env.NOTION_API!,
41
+ page_id: process.env.NOTION_PAGE_ID!,
42
+ });
43
+
44
+ // Fetch all posts
45
+ const posts = await blog.getPosts();
46
+ console.log(posts);
47
+
48
+ // Fetch the markdown body of a specific post
49
+ if (posts[0]) {
50
+ const markdown = await blog.getPostContent(posts[0].id);
51
+ console.log(markdown);
52
+ }
53
+ ```
54
+
55
+ On first run, `notionpress` creates a new database titled **"blog_database"** on the given page and caches its IDs in `.notionpress-cache.json` (add this to your `.gitignore`) so subsequent runs reuse the same database instead of creating a new one.
56
+
57
+ ## Environment variables
58
+
59
+ Create a `.env` file (see `.env.example`):
60
+
61
+ ```bash
62
+ NOTION_API=secret_xxx # your integration's internal secret
63
+ NOTION_PAGE_ID=xxxxxxxxxxxx # the page to host/host your blog database
64
+ ```
65
+
66
+ ## API Reference
67
+
68
+ ### `new Notionpress(params)`
69
+
70
+ | Param | Type | Required | Description |
71
+ | --------------- | -------- | -------- | ----------------------------------------------------------------------------|
72
+ | `api_key` | `string` | ✅ | Your Notion integration secret. |
73
+ | `page_id` | `string` | ✅ | ID of the Notion page to provision (or that already hosts) the blog database. |
74
+
75
+ ### `getPosts(): Promise<Post[]>`
76
+
77
+ Returns every post in the blog database, fully paginated.
78
+
79
+ ```ts
80
+ type Post = {
81
+ id: string;
82
+ url: string;
83
+ title: string;
84
+ slug: string;
85
+ status: "Published" | "Editing" | "Deprived" | null;
86
+ published_date: string | null;
87
+ cover_image: string | null;
88
+ };
89
+ ```
90
+
91
+ ### `getPostContent(page_id: string): Promise<string>`
92
+
93
+ Returns the markdown body of a single post page.
94
+
95
+ ## Fetching a post by slug
96
+
97
+ `notionpress` doesn't expose a dedicated "get by slug" method — instead, pull the full list from `getPosts()`, find the matching slug, then fetch its body with `getPostContent()`. A tiny shared helper keeps this logic in one place:
98
+
99
+ ```ts
100
+ // lib/getPostBySlug.ts
101
+ import { blog } from "./notionpress"; // your Notionpress instance
102
+
103
+ export async function getPostBySlug(slug: string) {
104
+ const posts = await blog.getPosts();
105
+ const post = posts.find((p) => p.slug === slug);
106
+
107
+ if (!post) return null;
108
+
109
+ const content = await blog.getPostContent(post.id);
110
+ return { ...post, content };
111
+ }
112
+ ```
113
+
114
+ ### Next.js (App Router)
115
+
116
+ ```tsx
117
+ // app/blog/[slug]/page.tsx
118
+ import { notFound } from "next/navigation";
119
+ import Markdown from "react-markdown";
120
+ import { getPostBySlug } from "@/lib/getPostBySlug";
121
+ import { blog } from "@/lib/notionpress";
122
+
123
+ export default async function BlogPostPage({ params }: { params: { slug: string } }) {
124
+ const post = await getPostBySlug(params.slug);
125
+
126
+ if (!post) notFound();
127
+
128
+ return (
129
+ <article>
130
+ <h1>{post.title}</h1>
131
+ <Markdown>{post.content}</Markdown>
132
+ </article>
133
+ );
134
+ }
135
+
136
+ // Optional: pre-render every known slug at build time
137
+ export async function generateStaticParams() {
138
+ const posts = await blog.getPosts();
139
+ return posts.map((post) => ({ slug: post.slug }));
140
+ }
141
+ ```
142
+
143
+ ### SvelteKit
144
+
145
+ ```ts
146
+ // src/routes/blog/[slug]/+page.server.ts
147
+ import { error } from "@sveltejs/kit";
148
+ import { getPostBySlug } from "$lib/getPostBySlug";
149
+ import type { PageServerLoad } from "./$types";
150
+
151
+ export const load: PageServerLoad = async ({ params }) => {
152
+ const post = await getPostBySlug(params.slug);
153
+
154
+ if (!post) throw error(404, "Post not found");
155
+
156
+ return { post };
157
+ };
158
+ ```
159
+
160
+ ```svelte
161
+ <!-- src/routes/blog/[slug]/+page.svelte -->
162
+ <script lang="ts">
163
+ import { marked } from "marked";
164
+ export let data;
165
+ </script>
166
+
167
+ <article>
168
+ <h1>{data.post.title}</h1>
169
+ {@html marked(data.post.content)}
170
+ </article>
171
+ ```
172
+
173
+ ### SolidStart
174
+
175
+ ```tsx
176
+ // src/routes/blog/[slug].tsx
177
+ import { createAsync, query } from "@solidjs/router";
178
+ import { marked } from "marked";
179
+ import { getPostBySlug } from "~/lib/getPostBySlug";
180
+
181
+ const findPost = query(async (slug: string) => {
182
+ "use server";
183
+ return getPostBySlug(slug);
184
+ }, "post-by-slug");
185
+
186
+ export default function BlogPostPage(props: { params: { slug: string } }) {
187
+ const post = createAsync(() => findPost(props.params.slug));
188
+
189
+ return (
190
+ <article>
191
+ <h1>{post()?.title}</h1>
192
+ <div innerHTML={marked(post()?.content ?? "")} />
193
+ </article>
194
+ );
195
+ }
196
+ ```
197
+
198
+ ### Astro
199
+
200
+ ```astro
201
+ ---
202
+ // src/pages/blog/[slug].astro
203
+ import { getPostBySlug } from "../../lib/getPostBySlug";
204
+ import { marked } from "marked";
205
+
206
+ const { slug } = Astro.params;
207
+ const post = await getPostBySlug(slug!);
208
+
209
+ if (!post) return Astro.redirect("/404");
210
+
211
+ const html = marked(post.content);
212
+ ---
213
+
214
+ <article>
215
+ <h1>{post.title}</h1>
216
+ <Fragment set:html={html} />
217
+ </article>
218
+ ```
219
+
220
+ > `getPostContent()` returns raw markdown, so pick whichever markdown-to-HTML renderer fits your stack (`react-markdown`, `marked`, `markdown-it`, etc.) to turn it into rendered output.
221
+
222
+ Full, typed-out, copy-pasteable versions of each of these live in [`examples/`](./examples).
223
+
224
+ ## Default schema
225
+
226
+ When `notionpress` provisions a database for you, it creates the following properties:
227
+
228
+ | Property | Notion type |
229
+ | ----------------- | ------------- |
230
+ | `Title` | Title |
231
+ | `Slug` | Rich text |
232
+ | `Status` | Select (`Published`, `Editing`, `Deprived`) |
233
+ | `Published Date` | Date |
234
+ | `Cover Image` | Files |
235
+
236
+ ## Development
237
+
238
+ ```bash
239
+ bun install # install dependencies
240
+ bun run test # run tests
241
+ bun run build # compile to dist/
242
+ ```
243
+
244
+ ## Contributing
245
+
246
+ Issues and pull requests are welcome! If you're proposing a larger change, please open an issue first to discuss what you'd like to change.
247
+
248
+ ## License
249
+
250
+ [MIT](./LICENSE)
@@ -0,0 +1,42 @@
1
+ import type { Post, RootClientProps } from "./types.js";
2
+ export declare class Notionpress {
3
+ private api_key;
4
+ private page_id;
5
+ private database_id;
6
+ private notionClient;
7
+ private dataSourceId;
8
+ private ready;
9
+ /**
10
+ * Creates a Notionpress client bound to a single Notion page.
11
+ * Kicks off async setup (finding or creating the underlying database/data source)
12
+ * in the background; awaited internally by any public method via `this.ready`.
13
+ */
14
+ constructor(params: RootClientProps);
15
+ /**
16
+ * Runs the async initialization steps required before the client is usable.
17
+ */
18
+ private initiateTable;
19
+ /**
20
+ * Resolves `this.database_id` / `this.dataSourceId` for `this.page_id`, in priority order:
21
+ * 1. Use the caller-supplied `datasource_id` as-is.
22
+ * 2. Reuse a previously cached database/data source for this page.
23
+ * 3. Otherwise, create a new database on the page with the default schema.
24
+ * Any newly resolved IDs are written back to the cache file.
25
+ */
26
+ private createDataSources;
27
+ /**
28
+ * Maps a raw Notion page object (from the blog database) to our `Post` shape,
29
+ * pulling out title, slug, status, published date, and cover image.
30
+ */
31
+ private toPost;
32
+ /**
33
+ * Fetches all posts from the configured data source, paging through
34
+ * results until every entry has been collected.
35
+ *
36
+ * @throws if no `dataSourceId` is available (must be set manually after
37
+ * a table/database is created).
38
+ */
39
+ getPosts(): Promise<Post[]>;
40
+ getPostContent(page_id: string): Promise<string>;
41
+ }
42
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAc,eAAe,EAAE,MAAM,YAAY,CAAC;AAkCpE,qBAAa,WAAW;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,KAAK,CAAgB;IAE7B;;;;OAIG;gBACS,MAAM,EAAE,eAAe;IAYnC;;OAEG;YACW,aAAa;IAI3B;;;;;;OAMG;YACW,iBAAiB;IAyC/B;;;OAGG;IACH,OAAO,CAAC,MAAM;IAyCd;;;;;;OAMG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IA4B3B,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAShE"}
package/dist/client.js ADDED
@@ -0,0 +1,167 @@
1
+ import { Client as NotionClient } from "@notionhq/client";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { defaultDatasource } from "./schema.js";
4
+ const CACHE_FILE = ".notionpress-cache.json";
5
+ /**
6
+ * Reads the on-disk cache file mapping page IDs to their database/data source IDs.
7
+ * Returns an empty object if the file doesn't exist or fails to parse.
8
+ */
9
+ function readCache() {
10
+ if (!existsSync(CACHE_FILE)) {
11
+ return {};
12
+ }
13
+ try {
14
+ return JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
15
+ }
16
+ catch {
17
+ return {};
18
+ }
19
+ }
20
+ /**
21
+ * Persists (or overwrites) the cache entry for a given page ID.
22
+ */
23
+ function writeCacheEntry(page_id, entry) {
24
+ const cache = readCache();
25
+ cache[page_id] = entry;
26
+ writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
27
+ }
28
+ export class Notionpress {
29
+ api_key;
30
+ page_id;
31
+ database_id;
32
+ notionClient;
33
+ dataSourceId;
34
+ ready;
35
+ /**
36
+ * Creates a Notionpress client bound to a single Notion page.
37
+ * Kicks off async setup (finding or creating the underlying database/data source)
38
+ * in the background; awaited internally by any public method via `this.ready`.
39
+ */
40
+ constructor(params) {
41
+ this.api_key = params.api_key;
42
+ this.page_id = params.page_id;
43
+ this.notionClient = new NotionClient({ auth: this.api_key });
44
+ this.ready = this.initiateTable().catch((error) => {
45
+ console.error("Notionpress failed to initialize:", error);
46
+ throw error;
47
+ });
48
+ }
49
+ /**
50
+ * Runs the async initialization steps required before the client is usable.
51
+ */
52
+ async initiateTable() {
53
+ await this.createDataSources();
54
+ }
55
+ /**
56
+ * Resolves `this.database_id` / `this.dataSourceId` for `this.page_id`, in priority order:
57
+ * 1. Use the caller-supplied `datasource_id` as-is.
58
+ * 2. Reuse a previously cached database/data source for this page.
59
+ * 3. Otherwise, create a new database on the page with the default schema.
60
+ * Any newly resolved IDs are written back to the cache file.
61
+ */
62
+ async createDataSources() {
63
+ // Caller already told us exactly which data source to use.
64
+ if (this.dataSourceId) {
65
+ writeCacheEntry(this.page_id, { database_id: this.database_id, datasource_id: this.dataSourceId });
66
+ return;
67
+ }
68
+ // We've already set this exact page up before; reuse it instead of
69
+ // creating a new database on it every time.
70
+ const cached = readCache()[this.page_id];
71
+ if (cached) {
72
+ this.database_id = cached.database_id;
73
+ this.dataSourceId = cached.datasource_id;
74
+ return;
75
+ }
76
+ // Brand new page: create a database on it with our schema baked in.
77
+ const database = await this.notionClient.databases.create({
78
+ parent: {
79
+ page_id: this.page_id,
80
+ type: "page_id",
81
+ },
82
+ title: [{ text: { content: "blog_database" } }],
83
+ initial_data_source: {
84
+ // @ts-expect-error - Old and New SDK and API calls collide, will fix later
85
+ properties: { ...defaultDatasource }
86
+ }
87
+ });
88
+ this.database_id = database.id;
89
+ if ("data_sources" in database) {
90
+ this.dataSourceId = database.data_sources[0]?.id;
91
+ }
92
+ if (this.dataSourceId) {
93
+ writeCacheEntry(this.page_id, { database_id: this.database_id, datasource_id: this.dataSourceId });
94
+ }
95
+ }
96
+ /**
97
+ * Maps a raw Notion page object (from the blog database) to our `Post` shape,
98
+ * pulling out title, slug, status, published date, and cover image.
99
+ */
100
+ toPost(page) {
101
+ const properties = page.properties;
102
+ const title = properties.title;
103
+ const slug = properties.slug;
104
+ const status = properties.status;
105
+ const published_date = properties.published_date;
106
+ const cover_image = properties.cover_image;
107
+ const titleText = title?.type === "title"
108
+ ? title.title.map((item) => item.plain_text).join("")
109
+ : "";
110
+ const slugText = slug?.type === "rich_text"
111
+ ? slug.rich_text.map((item) => item.plain_text).join("")
112
+ : "";
113
+ const statusName = status?.type === "select"
114
+ ? status.select?.name ?? null
115
+ : null;
116
+ const publishedDateValue = published_date?.type === "date"
117
+ ? published_date.date?.start ?? null
118
+ : null;
119
+ const coverImageFile = cover_image?.type === "files" ? cover_image.files[0] : undefined;
120
+ const coverImageUrl = coverImageFile
121
+ ? (coverImageFile.type === "file" ? coverImageFile.file.url : coverImageFile.external.url)
122
+ : null;
123
+ return {
124
+ id: page.id,
125
+ url: page.url,
126
+ title: titleText,
127
+ slug: slugText,
128
+ status: statusName,
129
+ published_date: publishedDateValue,
130
+ cover_image: coverImageUrl
131
+ };
132
+ }
133
+ /**
134
+ * Fetches all posts from the configured data source, paging through
135
+ * results until every entry has been collected.
136
+ *
137
+ * @throws if no `dataSourceId` is available (must be set manually after
138
+ * a table/database is created).
139
+ */
140
+ async getPosts() {
141
+ await this.ready;
142
+ if (!this.dataSourceId) {
143
+ throw new Error("Once a table/database is created you have to manually add the datasource_id to the variable initiator");
144
+ }
145
+ const posts = [];
146
+ let cursor = undefined;
147
+ do {
148
+ const response = await this.notionClient.dataSources.query({
149
+ data_source_id: this.dataSourceId,
150
+ start_cursor: cursor
151
+ });
152
+ for (const result of response.results) {
153
+ if (result.object === "page" && "properties" in result) {
154
+ posts.push(this.toPost(result));
155
+ }
156
+ }
157
+ cursor = response.next_cursor ?? undefined;
158
+ } while (cursor);
159
+ return posts;
160
+ }
161
+ async getPostContent(page_id) {
162
+ await this.ready;
163
+ const { markdown } = await this.notionClient.pages.retrieveMarkdown({ page_id });
164
+ return markdown;
165
+ }
166
+ }
167
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { Notionpress } from "./client.js";
2
+ export type { Post, PostStatus, RootClientProps } from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { Notionpress } from "./client.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,33 @@
1
+ export declare const defaultDatasource: {
2
+ cover_image: {
3
+ name: string;
4
+ type: string;
5
+ files: {};
6
+ };
7
+ published_date: {
8
+ name: string;
9
+ type: string;
10
+ date: {};
11
+ };
12
+ slug: {
13
+ name: string;
14
+ type: string;
15
+ rich_text: {};
16
+ };
17
+ title: {
18
+ name: string;
19
+ type: string;
20
+ title: {};
21
+ };
22
+ status: {
23
+ name: string;
24
+ type: string;
25
+ select: {
26
+ options: {
27
+ name: string;
28
+ color: string;
29
+ }[];
30
+ };
31
+ };
32
+ };
33
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC7B,CAAA"}
package/dist/schema.js ADDED
@@ -0,0 +1,34 @@
1
+ export const defaultDatasource = {
2
+ cover_image: {
3
+ name: "Cover Image",
4
+ type: "files",
5
+ files: {}
6
+ },
7
+ published_date: {
8
+ name: "Published Date",
9
+ type: "date",
10
+ date: {}
11
+ },
12
+ slug: {
13
+ name: "Slug",
14
+ type: "rich_text",
15
+ rich_text: {}
16
+ },
17
+ title: {
18
+ name: "Title",
19
+ type: "title",
20
+ title: {}
21
+ },
22
+ status: {
23
+ name: "Status",
24
+ type: "select",
25
+ select: {
26
+ options: [
27
+ { name: "Published", color: "green" },
28
+ { name: "Editing", color: "blue" },
29
+ { name: "Deprived", color: "red" },
30
+ ]
31
+ }
32
+ }
33
+ };
34
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC7B,WAAW,EAAE;QACT,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,EAAE;KACZ;IACD,cAAc,EAAE;QACZ,IAAI,EAAE,gBAAgB;QACtB,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,EAAE;KACX;IACD,IAAI,EAAE;QACF,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,EAAE;KAChB;IACD,KAAK,EAAE;QACH,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,EAAE;KACZ;IACD,MAAM,EAAE;QACJ,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE;YACJ,OAAO,EAAE;gBACL,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE;gBACrC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE;gBAClC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE;aACrC;SACJ;KACJ;CACJ,CAAA"}
@@ -0,0 +1,16 @@
1
+ export interface RootClientProps {
2
+ api_key: string;
3
+ page_id: string;
4
+ datasource_id?: string;
5
+ }
6
+ export type PostStatus = "Published" | "Editing" | "Deprived";
7
+ export interface Post {
8
+ id: string;
9
+ url: string;
10
+ title: string;
11
+ slug: string;
12
+ status: PostStatus | null;
13
+ published_date: string | null;
14
+ cover_image: string | null;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,IAAI;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "notionpress",
3
+ "version": "0.1.0",
4
+ "description": "Turn a Notion page into a headless CMS for your blog — typed post fetching and markdown content, no manual database setup required.",
5
+ "keywords": [
6
+ "notion",
7
+ "cms",
8
+ "headless-cms",
9
+ "blog",
10
+ "markdown",
11
+ "typescript"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Vivillies",
15
+ "homepage": "https://github.com/Vivillies/notionpress#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Vivillies/notionpress.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/Vivillies/notionpress/issues"
22
+ },
23
+ "type": "module",
24
+ "module": "src/index.ts",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.build.json",
38
+ "test": "bun run test/client.test.ts",
39
+ "prepublishOnly": "bun run build"
40
+ },
41
+ "devDependencies": {
42
+ "@types/bun": "latest",
43
+ "@types/node": "^26.6.2"
44
+ },
45
+ "peerDependencies": {
46
+ "typescript": "^5"
47
+ },
48
+ "dependencies": {
49
+ "@notionhq/client": "^5.26.0",
50
+ "zod": "^4.5.4"
51
+ }
52
+ }