@natesena/blog-lib 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 +293 -0
- package/dist/chunk-EG563RZL.js +475 -0
- package/dist/chunk-EG563RZL.js.map +1 -0
- package/dist/chunk-OPJV2ECE.js +122 -0
- package/dist/chunk-OPJV2ECE.js.map +1 -0
- package/dist/components/index.js +447 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index-TnUz7zF0.d.ts +404 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk/index.d.ts +2 -0
- package/dist/sdk/index.js +19 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/server/index.d.ts +131 -0
- package/dist/server/index.js +132 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { PrismaClient, Author as Author$1, Tag as Tag$1 } from '@prisma/client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TypeScript types for Post
|
|
5
|
+
*
|
|
6
|
+
* Ref: docs/epic-blog-lib/02-architecture.md
|
|
7
|
+
*/
|
|
8
|
+
type Status = 'DRAFT' | 'PUBLISHED';
|
|
9
|
+
interface TiptapContent {
|
|
10
|
+
type: string;
|
|
11
|
+
content?: unknown[];
|
|
12
|
+
attrs?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
interface CreatePostInput {
|
|
15
|
+
title: string;
|
|
16
|
+
content: string;
|
|
17
|
+
excerpt?: string;
|
|
18
|
+
coverImage?: string;
|
|
19
|
+
status?: Status;
|
|
20
|
+
authorId: string;
|
|
21
|
+
slug?: string;
|
|
22
|
+
tagIds?: string[];
|
|
23
|
+
publishedAt?: Date;
|
|
24
|
+
}
|
|
25
|
+
interface UpdatePostInput {
|
|
26
|
+
title?: string;
|
|
27
|
+
content?: string;
|
|
28
|
+
excerpt?: string;
|
|
29
|
+
coverImage?: string;
|
|
30
|
+
status?: Status;
|
|
31
|
+
tagIds?: string[];
|
|
32
|
+
publishedAt?: Date;
|
|
33
|
+
}
|
|
34
|
+
interface PostListFilters {
|
|
35
|
+
status?: Status;
|
|
36
|
+
authorId?: string;
|
|
37
|
+
tag?: string;
|
|
38
|
+
publishedBefore?: Date;
|
|
39
|
+
publishedAfter?: Date;
|
|
40
|
+
limit?: number;
|
|
41
|
+
offset?: number;
|
|
42
|
+
}
|
|
43
|
+
interface Post {
|
|
44
|
+
id: string;
|
|
45
|
+
slug: string;
|
|
46
|
+
title: string;
|
|
47
|
+
content: string;
|
|
48
|
+
excerpt: string | null;
|
|
49
|
+
coverImage: string | null;
|
|
50
|
+
status: Status;
|
|
51
|
+
publishedAt: Date | null;
|
|
52
|
+
createdAt: Date;
|
|
53
|
+
updatedAt: Date;
|
|
54
|
+
authorId: string;
|
|
55
|
+
tagIds: string[];
|
|
56
|
+
}
|
|
57
|
+
declare function isValidStatus(statusValue: string): statusValue is Status;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* TypeScript types for Author
|
|
61
|
+
*
|
|
62
|
+
* Ref: docs/epic-blog-lib/02-architecture.md
|
|
63
|
+
*/
|
|
64
|
+
interface CreateAuthorInput {
|
|
65
|
+
name: string;
|
|
66
|
+
email: string;
|
|
67
|
+
bio?: string;
|
|
68
|
+
avatar?: string;
|
|
69
|
+
}
|
|
70
|
+
interface UpdateAuthorInput {
|
|
71
|
+
name?: string;
|
|
72
|
+
bio?: string;
|
|
73
|
+
avatar?: string;
|
|
74
|
+
}
|
|
75
|
+
interface Author {
|
|
76
|
+
id: string;
|
|
77
|
+
name: string;
|
|
78
|
+
email: string;
|
|
79
|
+
bio: string | null;
|
|
80
|
+
avatar: string | null;
|
|
81
|
+
createdAt: Date;
|
|
82
|
+
updatedAt: Date;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* TypeScript types for Tag
|
|
87
|
+
*
|
|
88
|
+
* Ref: docs/epic-blog-lib/02-architecture.md
|
|
89
|
+
*/
|
|
90
|
+
interface CreateTagInput {
|
|
91
|
+
name: string;
|
|
92
|
+
slug: string;
|
|
93
|
+
description?: string;
|
|
94
|
+
}
|
|
95
|
+
interface UpdateTagInput {
|
|
96
|
+
name?: string;
|
|
97
|
+
slug?: string;
|
|
98
|
+
description?: string;
|
|
99
|
+
}
|
|
100
|
+
interface Tag {
|
|
101
|
+
id: string;
|
|
102
|
+
name: string;
|
|
103
|
+
slug: string;
|
|
104
|
+
description: string | null;
|
|
105
|
+
createdAt: Date;
|
|
106
|
+
updatedAt: Date;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Database Adapter Interface
|
|
111
|
+
*
|
|
112
|
+
* Defines the contract for all database adapters.
|
|
113
|
+
* Allows swapping between different databases (Postgres, SQLite, MongoDB, etc.)
|
|
114
|
+
*
|
|
115
|
+
* Ref: docs/epic-blog-lib/02-architecture.md
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
interface DatabaseAdapter {
|
|
119
|
+
createPost(post: CreatePostInput): Promise<AdapterPost>;
|
|
120
|
+
getPost(id: string): Promise<AdapterPost | null>;
|
|
121
|
+
getPostBySlug(slug: string): Promise<AdapterPost | null>;
|
|
122
|
+
listPosts(filters?: PostListFilters): Promise<{
|
|
123
|
+
posts: AdapterPost[];
|
|
124
|
+
total: number;
|
|
125
|
+
}>;
|
|
126
|
+
updatePost(id: string, data: UpdatePostInput): Promise<AdapterPost>;
|
|
127
|
+
deletePost(id: string): Promise<void>;
|
|
128
|
+
createAuthor(author: CreateAuthorInput): Promise<Author>;
|
|
129
|
+
getAuthor(id: string): Promise<Author | null>;
|
|
130
|
+
getAuthorByEmail(email: string): Promise<Author | null>;
|
|
131
|
+
listAuthors(options?: {
|
|
132
|
+
limit?: number;
|
|
133
|
+
offset?: number;
|
|
134
|
+
}): Promise<Author[]>;
|
|
135
|
+
updateAuthor(id: string, data: UpdateAuthorInput): Promise<Author>;
|
|
136
|
+
deleteAuthor(id: string): Promise<void>;
|
|
137
|
+
createTag(tag: CreateTagInput): Promise<Tag>;
|
|
138
|
+
getTag(id: string): Promise<Tag | null>;
|
|
139
|
+
getTagBySlug(slug: string): Promise<Tag | null>;
|
|
140
|
+
listTags(options?: {
|
|
141
|
+
limit?: number;
|
|
142
|
+
offset?: number;
|
|
143
|
+
}): Promise<Tag[]>;
|
|
144
|
+
updateTag(id: string, data: UpdateTagInput): Promise<Tag>;
|
|
145
|
+
deleteTag(id: string): Promise<void>;
|
|
146
|
+
}
|
|
147
|
+
interface AdapterPost {
|
|
148
|
+
id: string;
|
|
149
|
+
slug: string;
|
|
150
|
+
title: string;
|
|
151
|
+
content: string;
|
|
152
|
+
excerpt: string | null;
|
|
153
|
+
coverImage: string | null;
|
|
154
|
+
status: 'DRAFT' | 'PUBLISHED';
|
|
155
|
+
publishedAt: Date | null;
|
|
156
|
+
createdAt: Date;
|
|
157
|
+
updatedAt: Date;
|
|
158
|
+
authorId: string;
|
|
159
|
+
tagIds: string[];
|
|
160
|
+
author?: Author | null;
|
|
161
|
+
tags?: Tag[] | null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Posts SDK
|
|
166
|
+
*
|
|
167
|
+
* CRUD operations for blog posts, including filtering, slug lookup,
|
|
168
|
+
* and automatic slug generation.
|
|
169
|
+
*
|
|
170
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 169-228)
|
|
171
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.4)
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
declare class PostsSDK {
|
|
175
|
+
private adapter;
|
|
176
|
+
constructor(adapter: DatabaseAdapter);
|
|
177
|
+
/**
|
|
178
|
+
* Create a new blog post.
|
|
179
|
+
* Generates a slug from the title if none is provided.
|
|
180
|
+
* Auto-sets publishedAt when status is PUBLISHED.
|
|
181
|
+
*/
|
|
182
|
+
create(input: CreatePostInput): Promise<AdapterPost>;
|
|
183
|
+
/** Get a post by its ID. */
|
|
184
|
+
get(id: string): Promise<AdapterPost | null>;
|
|
185
|
+
/** Get a post by its URL slug. */
|
|
186
|
+
getBySlug(slug: string): Promise<AdapterPost | null>;
|
|
187
|
+
/**
|
|
188
|
+
* List posts with optional filtering and pagination.
|
|
189
|
+
* Defaults to limit=10, offset=0.
|
|
190
|
+
*/
|
|
191
|
+
list(filters?: PostListFilters): Promise<{
|
|
192
|
+
posts: AdapterPost[];
|
|
193
|
+
total: number;
|
|
194
|
+
}>;
|
|
195
|
+
/**
|
|
196
|
+
* Update an existing post by ID.
|
|
197
|
+
* Auto-sets publishedAt when transitioning from DRAFT to PUBLISHED.
|
|
198
|
+
*/
|
|
199
|
+
update(id: string, data: UpdatePostInput): Promise<AdapterPost>;
|
|
200
|
+
/** Delete a post by ID. */
|
|
201
|
+
delete(id: string): Promise<void>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Authors SDK
|
|
206
|
+
*
|
|
207
|
+
* CRUD operations for blog authors.
|
|
208
|
+
*
|
|
209
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 213-218)
|
|
210
|
+
* Ref: Plan Phase 5, Steps 5.3 and 5.4
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
declare class AuthorsSDK {
|
|
214
|
+
private adapter;
|
|
215
|
+
constructor(adapter: DatabaseAdapter);
|
|
216
|
+
/** Create a new author. Email must be unique. */
|
|
217
|
+
create(input: CreateAuthorInput): Promise<Author>;
|
|
218
|
+
/** Get an author by their ID. */
|
|
219
|
+
get(id: string): Promise<Author | null>;
|
|
220
|
+
/** Get an author by their email address. */
|
|
221
|
+
getByEmail(email: string): Promise<Author | null>;
|
|
222
|
+
/** List authors with optional pagination. */
|
|
223
|
+
list(options?: {
|
|
224
|
+
limit?: number;
|
|
225
|
+
offset?: number;
|
|
226
|
+
}): Promise<Author[]>;
|
|
227
|
+
/** Update an author by ID. */
|
|
228
|
+
update(id: string, data: UpdateAuthorInput): Promise<Author>;
|
|
229
|
+
/** Delete an author by ID. */
|
|
230
|
+
delete(id: string): Promise<void>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Tags SDK
|
|
235
|
+
*
|
|
236
|
+
* CRUD operations for blog tags.
|
|
237
|
+
*
|
|
238
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 220-225)
|
|
239
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.14)
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
declare class TagsSDK {
|
|
243
|
+
private adapter;
|
|
244
|
+
constructor(adapter: DatabaseAdapter);
|
|
245
|
+
/** Create a new tag. Name and slug must be provided and unique. */
|
|
246
|
+
create(input: CreateTagInput): Promise<Tag>;
|
|
247
|
+
/** Get a tag by its ID. */
|
|
248
|
+
get(id: string): Promise<Tag | null>;
|
|
249
|
+
/** Get a tag by its URL slug. */
|
|
250
|
+
getBySlug(slug: string): Promise<Tag | null>;
|
|
251
|
+
/** List tags with optional pagination. */
|
|
252
|
+
list(options?: {
|
|
253
|
+
limit?: number;
|
|
254
|
+
offset?: number;
|
|
255
|
+
}): Promise<Tag[]>;
|
|
256
|
+
/** Update a tag by ID. */
|
|
257
|
+
update(id: string, data: UpdateTagInput): Promise<Tag>;
|
|
258
|
+
/** Delete a tag by ID. */
|
|
259
|
+
delete(id: string): Promise<void>;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Postgres Adapter (Prisma)
|
|
264
|
+
*
|
|
265
|
+
* Implementation of DatabaseAdapter using Prisma ORM for PostgreSQL.
|
|
266
|
+
* Handles all CRUD operations with proper error handling and validation.
|
|
267
|
+
*/
|
|
268
|
+
|
|
269
|
+
declare class PostgresAdapter implements DatabaseAdapter {
|
|
270
|
+
private prisma;
|
|
271
|
+
constructor(prisma: PrismaClient);
|
|
272
|
+
createPost(post: CreatePostInput): Promise<AdapterPost>;
|
|
273
|
+
getPost(id: string): Promise<AdapterPost | null>;
|
|
274
|
+
getPostBySlug(slug: string): Promise<AdapterPost | null>;
|
|
275
|
+
listPosts(filters?: PostListFilters): Promise<{
|
|
276
|
+
posts: AdapterPost[];
|
|
277
|
+
total: number;
|
|
278
|
+
}>;
|
|
279
|
+
updatePost(id: string, data: UpdatePostInput): Promise<AdapterPost>;
|
|
280
|
+
deletePost(id: string): Promise<void>;
|
|
281
|
+
createAuthor(author: CreateAuthorInput): Promise<Author$1>;
|
|
282
|
+
getAuthor(id: string): Promise<Author$1 | null>;
|
|
283
|
+
getAuthorByEmail(email: string): Promise<Author$1 | null>;
|
|
284
|
+
listAuthors(options?: {
|
|
285
|
+
limit?: number;
|
|
286
|
+
offset?: number;
|
|
287
|
+
}): Promise<Author$1[]>;
|
|
288
|
+
updateAuthor(id: string, data: UpdateAuthorInput): Promise<Author$1>;
|
|
289
|
+
deleteAuthor(id: string): Promise<void>;
|
|
290
|
+
createTag(tag: CreateTagInput): Promise<Tag$1>;
|
|
291
|
+
getTag(id: string): Promise<Tag$1 | null>;
|
|
292
|
+
getTagBySlug(slug: string): Promise<Tag$1 | null>;
|
|
293
|
+
listTags(options?: {
|
|
294
|
+
limit?: number;
|
|
295
|
+
offset?: number;
|
|
296
|
+
}): Promise<Tag$1[]>;
|
|
297
|
+
updateTag(id: string, data: UpdateTagInput): Promise<Tag$1>;
|
|
298
|
+
deleteTag(id: string): Promise<void>;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Google Cloud Storage Integration
|
|
303
|
+
*
|
|
304
|
+
* Handles presigned URL generation, file deletion, and existence checks
|
|
305
|
+
* for image uploads to GCS.
|
|
306
|
+
*
|
|
307
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 866-923)
|
|
308
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.9)
|
|
309
|
+
*/
|
|
310
|
+
interface GCSStorageConfig {
|
|
311
|
+
projectId: string;
|
|
312
|
+
bucket: string;
|
|
313
|
+
keyFile?: string;
|
|
314
|
+
/**
|
|
315
|
+
* 'public' (default) — files are publicly readable via direct URL.
|
|
316
|
+
* 'private' — files require a v4 signed read URL to access.
|
|
317
|
+
*
|
|
318
|
+
* Ref: Plan Phase 5, Step 5.5
|
|
319
|
+
*/
|
|
320
|
+
accessMode?: 'public' | 'private';
|
|
321
|
+
/** How long signed read URLs last in seconds (default 3600 = 1 hour). Only used when accessMode is 'private'. */
|
|
322
|
+
signedReadUrlExpiresInSeconds?: number;
|
|
323
|
+
}
|
|
324
|
+
interface PresignedUploadResult {
|
|
325
|
+
uploadUrl: string;
|
|
326
|
+
/** Direct public URL when accessMode='public', or a v4 signed read URL when accessMode='private'. */
|
|
327
|
+
publicUrl: string;
|
|
328
|
+
}
|
|
329
|
+
declare class GCSStorage {
|
|
330
|
+
private storage;
|
|
331
|
+
private storageInitPromise;
|
|
332
|
+
private bucketName;
|
|
333
|
+
private accessMode;
|
|
334
|
+
private signedReadUrlExpiresInSeconds;
|
|
335
|
+
constructor(config: GCSStorageConfig);
|
|
336
|
+
private getStorage;
|
|
337
|
+
/**
|
|
338
|
+
* Generate a presigned URL for direct browser upload to GCS.
|
|
339
|
+
*
|
|
340
|
+
* @param filename - Destination path in bucket (e.g. "blog/posts/1234-abc.jpg")
|
|
341
|
+
* @param contentType - MIME type (e.g. "image/jpeg")
|
|
342
|
+
* @param uploadUrlExpiresInSeconds - URL expiry (default 300 = 5 min)
|
|
343
|
+
*/
|
|
344
|
+
getPresignedUploadUrl(filename: string, contentType: string, uploadUrlExpiresInSeconds?: number): Promise<PresignedUploadResult>;
|
|
345
|
+
/**
|
|
346
|
+
* Get a read URL for an existing file.
|
|
347
|
+
* Returns a direct public URL or a v4 signed URL depending on accessMode.
|
|
348
|
+
*/
|
|
349
|
+
getReadUrl(filename: string): Promise<string>;
|
|
350
|
+
/** Delete a file from GCS. */
|
|
351
|
+
delete(filename: string): Promise<void>;
|
|
352
|
+
/** Check if a file exists in the bucket. */
|
|
353
|
+
exists(filename: string): Promise<boolean>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* BlogSDK — Main Entry Point
|
|
358
|
+
*
|
|
359
|
+
* Initialize with a database adapter to get full CRUD for posts, authors, and tags.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* const blog = new BlogSDK(new PostgresAdapter(prisma));
|
|
363
|
+
* const post = await blog.posts.create({ title: "Hello", content: "...", authorId: "..." });
|
|
364
|
+
*
|
|
365
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 169-228)
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
interface BlogSDKConfig {
|
|
369
|
+
adapter: DatabaseAdapter;
|
|
370
|
+
gcs?: {
|
|
371
|
+
bucket: string;
|
|
372
|
+
projectId: string;
|
|
373
|
+
keyFile?: string;
|
|
374
|
+
/** 'public' (default) or 'private' — private uses signed read URLs. */
|
|
375
|
+
accessMode?: 'public' | 'private';
|
|
376
|
+
/** Signed read URL expiry in seconds (default 3600). Only used when accessMode is 'private'. */
|
|
377
|
+
signedReadUrlExpiresInSeconds?: number;
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
declare class BlogSDK {
|
|
381
|
+
readonly posts: PostsSDK;
|
|
382
|
+
readonly authors: AuthorsSDK;
|
|
383
|
+
readonly tags: TagsSDK;
|
|
384
|
+
private adapter;
|
|
385
|
+
private gcsStorage?;
|
|
386
|
+
constructor(config: BlogSDKConfig);
|
|
387
|
+
/**
|
|
388
|
+
* Static factory method for initialization.
|
|
389
|
+
* Validates config and returns a ready-to-use BlogSDK instance.
|
|
390
|
+
*/
|
|
391
|
+
static initialize(config: BlogSDKConfig): Promise<BlogSDK>;
|
|
392
|
+
/**
|
|
393
|
+
* Upload an image to GCS. Requires gcs config in BlogSDKConfig.
|
|
394
|
+
* Returns { uploadUrl, publicUrl } — client uploads directly to uploadUrl.
|
|
395
|
+
*/
|
|
396
|
+
getImageUploadUrl(filename: string, contentType: string, options?: {
|
|
397
|
+
folder?: string;
|
|
398
|
+
}): Promise<{
|
|
399
|
+
uploadUrl: string;
|
|
400
|
+
publicUrl: string;
|
|
401
|
+
}>;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export { type AdapterPost as A, BlogSDK as B, type CreateAuthorInput as C, type DatabaseAdapter as D, GCSStorage as G, type Post as P, type Status as S, type Tag as T, type UpdateAuthorInput as U, type Author as a, type BlogSDKConfig as b, type CreatePostInput as c, type CreateTagInput as d, type GCSStorageConfig as e, type PostListFilters as f, PostgresAdapter as g, type TiptapContent as h, type UpdatePostInput as i, type UpdateTagInput as j, isValidStatus as k, AuthorsSDK as l, PostsSDK as m, TagsSDK as n };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { A as AdapterPost, a as Author, B as BlogSDK, b as BlogSDKConfig, C as CreateAuthorInput, c as CreatePostInput, d as CreateTagInput, D as DatabaseAdapter, G as GCSStorage, e as GCSStorageConfig, P as Post, f as PostListFilters, g as PostgresAdapter, S as Status, T as Tag, h as TiptapContent, U as UpdateAuthorInput, i as UpdatePostInput, j as UpdateTagInput, k as isValidStatus } from './index-TnUz7zF0.js';
|
|
2
|
+
import '@prisma/client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BlogLibError — Custom Error Class
|
|
6
|
+
*
|
|
7
|
+
* Provides structured error codes for programmatic error handling.
|
|
8
|
+
* Consumers can catch BlogLibError and switch on the `code` field.
|
|
9
|
+
*
|
|
10
|
+
* Ref: Plan Phase 5, Step 5.3
|
|
11
|
+
*/
|
|
12
|
+
type BlogLibErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'DUPLICATE' | 'RELATION_ERROR' | 'STORAGE_ERROR' | 'AUTH_ERROR';
|
|
13
|
+
declare class BlogLibError extends Error {
|
|
14
|
+
readonly code: BlogLibErrorCode;
|
|
15
|
+
constructor(code: BlogLibErrorCode, message: string);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { BlogLibError, type BlogLibErrorCode };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BlogLibError,
|
|
3
|
+
BlogSDK,
|
|
4
|
+
PostgresAdapter
|
|
5
|
+
} from "./chunk-EG563RZL.js";
|
|
6
|
+
import {
|
|
7
|
+
GCSStorage
|
|
8
|
+
} from "./chunk-OPJV2ECE.js";
|
|
9
|
+
|
|
10
|
+
// src/types/post.ts
|
|
11
|
+
function isValidStatus(statusValue) {
|
|
12
|
+
return statusValue === "DRAFT" || statusValue === "PUBLISHED";
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
BlogLibError,
|
|
16
|
+
BlogSDK,
|
|
17
|
+
GCSStorage,
|
|
18
|
+
PostgresAdapter,
|
|
19
|
+
isValidStatus
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/post.ts"],"sourcesContent":["/**\n * TypeScript types for Post\n *\n * Ref: docs/epic-blog-lib/02-architecture.md\n */\n\n// Post Status\nexport type Status = 'DRAFT' | 'PUBLISHED';\n\n// Tiptap JSON content structure\nexport interface TiptapContent {\n type: string;\n content?: unknown[];\n attrs?: Record<string, unknown>;\n}\n\n// Create Post Input\nexport interface CreatePostInput {\n title: string;\n content: string;\n excerpt?: string;\n coverImage?: string;\n status?: Status;\n authorId: string;\n slug?: string;\n tagIds?: string[];\n publishedAt?: Date;\n}\n\n// Update Post Input (all fields optional)\nexport interface UpdatePostInput {\n title?: string;\n content?: string;\n excerpt?: string;\n coverImage?: string;\n status?: Status;\n tagIds?: string[];\n publishedAt?: Date;\n}\n\n// Post List Filters\nexport interface PostListFilters {\n status?: Status;\n authorId?: string;\n tag?: string;\n publishedBefore?: Date;\n publishedAfter?: Date;\n limit?: number;\n offset?: number;\n}\n\n// Post (from database)\nexport interface Post {\n id: string;\n slug: string;\n title: string;\n content: string;\n excerpt: string | null;\n coverImage: string | null;\n status: Status;\n publishedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n authorId: string;\n tagIds: string[];\n}\n\n// Type guard for status\nexport function isValidStatus(statusValue: string): statusValue is Status {\n return statusValue === 'DRAFT' || statusValue === 'PUBLISHED';\n}\n"],"mappings":";;;;;;;;;;AAoEO,SAAS,cAAc,aAA4C;AACxE,SAAO,gBAAgB,WAAW,gBAAgB;AACpD;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthorsSDK,
|
|
3
|
+
BlogSDK,
|
|
4
|
+
PostgresAdapter,
|
|
5
|
+
PostsSDK,
|
|
6
|
+
TagsSDK
|
|
7
|
+
} from "../chunk-EG563RZL.js";
|
|
8
|
+
import {
|
|
9
|
+
GCSStorage
|
|
10
|
+
} from "../chunk-OPJV2ECE.js";
|
|
11
|
+
export {
|
|
12
|
+
AuthorsSDK,
|
|
13
|
+
BlogSDK,
|
|
14
|
+
GCSStorage,
|
|
15
|
+
PostgresAdapter,
|
|
16
|
+
PostsSDK,
|
|
17
|
+
TagsSDK
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable Auth Configuration
|
|
3
|
+
*
|
|
4
|
+
* Allows consumers to inject their own auth provider into blog-lib.
|
|
5
|
+
* Upload actions and other protected operations call getCurrentUser()
|
|
6
|
+
* and reject requests if no user is returned.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { configureBlogAuth } from '@nate/blog-lib/server';
|
|
10
|
+
*
|
|
11
|
+
* configureBlogAuth({
|
|
12
|
+
* getCurrentUser: async () => {
|
|
13
|
+
* const session = await auth(); // your auth provider
|
|
14
|
+
* return session?.user ?? null;
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* Ref: Plan Phase 3, Step 3.2
|
|
19
|
+
*/
|
|
20
|
+
interface BlogAuthUser {
|
|
21
|
+
id: string;
|
|
22
|
+
email?: string;
|
|
23
|
+
}
|
|
24
|
+
interface BlogAuthConfig {
|
|
25
|
+
/**
|
|
26
|
+
* Return the currently authenticated user, or null if unauthenticated.
|
|
27
|
+
* Called by upload actions and other protected operations.
|
|
28
|
+
*/
|
|
29
|
+
getCurrentUser: () => Promise<BlogAuthUser | null>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Configure blog-lib's auth integration. Call once during app initialization.
|
|
33
|
+
*/
|
|
34
|
+
declare function configureBlogAuth(config: BlogAuthConfig): void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Auth Permission Utilities
|
|
38
|
+
*
|
|
39
|
+
* Pluggable permission checks for CMS access.
|
|
40
|
+
* Works with any auth system — Clerk, Auth0, NextAuth.js, or custom.
|
|
41
|
+
*
|
|
42
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 308-324)
|
|
43
|
+
* Ref: docs/epic-blog-lib/04-auth-guide.md
|
|
44
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.7)
|
|
45
|
+
*/
|
|
46
|
+
interface AuthUser {
|
|
47
|
+
id: string;
|
|
48
|
+
email: string;
|
|
49
|
+
isAdmin?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a user has permission to access the CMS.
|
|
53
|
+
*
|
|
54
|
+
* Uses CMS_ADMIN_EMAILS env var (comma-separated) to restrict access.
|
|
55
|
+
* If no env var is set, all authenticated users are allowed.
|
|
56
|
+
*/
|
|
57
|
+
declare function canAccessCMS(user: AuthUser): boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Simple API Key Auth
|
|
61
|
+
*
|
|
62
|
+
* For headless CMS access or simple auth without a full auth provider.
|
|
63
|
+
* Set CMS_API_KEY env var and send it in the x-api-key header.
|
|
64
|
+
*
|
|
65
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 646-671)
|
|
66
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.7)
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Verify an API key against the CMS_API_KEY environment variable.
|
|
70
|
+
* Returns false if CMS_API_KEY is not configured.
|
|
71
|
+
*/
|
|
72
|
+
declare function verifyApiKey(providedApiKey?: string | null): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Check the incoming request's x-api-key header against CMS_API_KEY.
|
|
75
|
+
* Use in server components or API routes.
|
|
76
|
+
*/
|
|
77
|
+
declare function checkApiKeyAuth(): Promise<boolean>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Auth Middleware Helper
|
|
81
|
+
*
|
|
82
|
+
* Convenience function to enforce auth in server components.
|
|
83
|
+
* Redirects unauthenticated users and checks CMS permissions.
|
|
84
|
+
*
|
|
85
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.7)
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Require authenticated CMS access. Redirects to signInUrl if no user,
|
|
90
|
+
* throws Error if user lacks CMS permission.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const user = await getYourAuthUser();
|
|
94
|
+
* await requireCMSAuth(user, '/sign-in');
|
|
95
|
+
*/
|
|
96
|
+
declare function requireCMSAuth(user: AuthUser | null, signInRedirectUrl?: string): Promise<void>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Image Upload Server Actions
|
|
100
|
+
*
|
|
101
|
+
* Server-side actions for initiating and confirming image uploads to GCS.
|
|
102
|
+
* Frontend calls these, then uploads directly to GCS using the presigned URL.
|
|
103
|
+
*
|
|
104
|
+
* Ref: docs/epic-blog-lib/02-architecture.md (lines 927-991)
|
|
105
|
+
* Ref: docs/epic-blog-lib/03-tasks.md (Task openclaw-n1y.10)
|
|
106
|
+
*/
|
|
107
|
+
interface InitiateUploadInput {
|
|
108
|
+
fileName: string;
|
|
109
|
+
fileType: string;
|
|
110
|
+
fileSizeBytes: number;
|
|
111
|
+
folder?: string;
|
|
112
|
+
}
|
|
113
|
+
interface InitiateUploadResult {
|
|
114
|
+
uploadUrl: string;
|
|
115
|
+
publicUrl: string;
|
|
116
|
+
destinationFilename: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate the file and generate a presigned GCS upload URL.
|
|
120
|
+
* The client then uploads directly to GCS using this URL.
|
|
121
|
+
*/
|
|
122
|
+
declare function initiateImageUpload(input: InitiateUploadInput): Promise<InitiateUploadResult>;
|
|
123
|
+
/**
|
|
124
|
+
* Confirm that a file was successfully uploaded to GCS.
|
|
125
|
+
* Call after the client finishes the direct upload.
|
|
126
|
+
*/
|
|
127
|
+
declare function confirmImageUpload(destinationFilename: string): Promise<{
|
|
128
|
+
isUploadConfirmed: boolean;
|
|
129
|
+
}>;
|
|
130
|
+
|
|
131
|
+
export { type AuthUser, type BlogAuthConfig, type BlogAuthUser, type InitiateUploadInput, type InitiateUploadResult, canAccessCMS, checkApiKeyAuth, configureBlogAuth, confirmImageUpload, initiateImageUpload, requireCMSAuth, verifyApiKey };
|