@cyguin/docs 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.
@@ -0,0 +1,52 @@
1
+ interface DocArticle {
2
+ id: string;
3
+ title: string;
4
+ body_md: string;
5
+ section: string;
6
+ article_order: number;
7
+ published_at: number | null;
8
+ }
9
+ interface CreateArticleInput {
10
+ title: string;
11
+ body_md: string;
12
+ section: string;
13
+ article_order: number;
14
+ published_at?: number | null;
15
+ }
16
+ interface UpdateArticleInput {
17
+ title?: string;
18
+ body_md?: string;
19
+ }
20
+ interface DocsAdapter {
21
+ list(params?: {
22
+ section?: string;
23
+ published?: boolean;
24
+ }): Promise<DocArticle[]>;
25
+ get(id: string): Promise<DocArticle | null>;
26
+ create(data: CreateArticleInput): Promise<DocArticle>;
27
+ update(id: string, data: Partial<UpdateArticleInput>): Promise<DocArticle>;
28
+ delete(id: string): Promise<void>;
29
+ reorder(id: string, newOrder: number): Promise<void>;
30
+ moveSection(id: string, newSection: string): Promise<void>;
31
+ }
32
+ interface DocsWidgetProps {
33
+ apiUrl?: string;
34
+ mode?: 'modal' | 'sidebar';
35
+ triggerLabel?: string;
36
+ defaultOpen?: boolean;
37
+ className?: string;
38
+ }
39
+ declare const defaultCssVars: {
40
+ readonly '--cyguin-docs-bg': "var(--cyguin-surface, #ffffff)";
41
+ readonly '--cyguin-docs-text': "var(--cyguin-text, #1a1a1a)";
42
+ readonly '--cyguin-docs-border': "var(--cyguin-border, #e5e5e5)";
43
+ readonly '--cyguin-docs-accent': "var(--cyguin-primary, #6366f1)";
44
+ readonly '--cyguin-docs-muted': "var(--cyguin-muted, #737373)";
45
+ readonly '--cyguin-docs-backdrop-opacity': "0.5";
46
+ readonly '--cyguin-docs-radius': "var(--cyguin-radius, 8px)";
47
+ readonly '--cyguin-docs-shadow': "var(--cyguin-shadow, 0 4px 24px rgba(0,0,0,0.12))";
48
+ readonly '--cyguin-docs-trigger-size': "48px";
49
+ readonly '--cyguin-docs-font': "var(--cyguin-font, system-ui, sans-serif)";
50
+ };
51
+
52
+ export { type DocsWidgetProps as D, type DocArticle as a, type DocsAdapter as b, defaultCssVars as d };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@cyguin/docs",
3
+ "version": "0.1.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/cyguin/docs"
7
+ },
8
+ "main": "dist/index.js",
9
+ "module": "dist/index.mjs",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./next": {
16
+ "types": "./dist/next.d.ts",
17
+ "default": "./dist/next.js"
18
+ },
19
+ "./styles.css": "./dist/styles.css"
20
+ },
21
+ "types": "dist/index.d.ts",
22
+ "peerDependencies": {
23
+ "next": ">=14",
24
+ "react": ">=18"
25
+ },
26
+ "dependencies": {
27
+ "better-sqlite3": "^11.0.0",
28
+ "marked": "^12.0.0",
29
+ "nanoid": "^5.0.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup && cp src/styles.css dist/styles.css",
33
+ "dev": "tsup --watch"
34
+ },
35
+ "devDependencies": {
36
+ "@types/better-sqlite3": "^7.6.0",
37
+ "@types/node": "^22.0.0",
38
+ "@types/react": "^18.3.0",
39
+ "@types/react-dom": "^18.3.0",
40
+ "next": "^14.2.0",
41
+ "react": "^18.3.0",
42
+ "react-dom": "^18.3.0",
43
+ "tsup": "^8.3.0",
44
+ "typescript": "^5.5.0"
45
+ }
46
+ }
@@ -0,0 +1 @@
1
+ export { createSQLiteAdapter } from './sqlite'
@@ -0,0 +1,95 @@
1
+ import Database from 'better-sqlite3'
2
+ import { nanoid } from 'nanoid'
3
+ import type { DocsAdapter, DocArticle, CreateArticleInput, UpdateArticleInput } from '../types'
4
+
5
+ interface SQLiteAdapterOptions {
6
+ dbPath?: string
7
+ }
8
+
9
+ export function createSQLiteAdapter(options: SQLiteAdapterOptions = {}): DocsAdapter {
10
+ const db = new Database(options.dbPath ?? ':memory:')
11
+ db.pragma('journal_mode = WAL')
12
+
13
+ db.exec(`
14
+ CREATE TABLE IF NOT EXISTS doc_articles (
15
+ id TEXT PRIMARY KEY,
16
+ title TEXT NOT NULL,
17
+ body_md TEXT NOT NULL,
18
+ section TEXT NOT NULL,
19
+ article_order INTEGER NOT NULL,
20
+ published_at INTEGER
21
+ );
22
+ CREATE INDEX IF NOT EXISTS idx_doc_articles_section_order ON doc_articles (section, article_order);
23
+ `)
24
+
25
+ function slugify(title: string): string {
26
+ return title.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '')
27
+ }
28
+
29
+ return {
30
+ async list({ section, published } = {}) {
31
+ let sql = 'SELECT * FROM doc_articles WHERE 1=1'
32
+ const params: unknown[] = []
33
+
34
+ if (section !== undefined) {
35
+ sql += ' AND section = ?'
36
+ params.push(section)
37
+ }
38
+
39
+ if (published === true) {
40
+ sql += ' AND published_at IS NOT NULL'
41
+ } else if (published === false) {
42
+ sql += ' AND published_at IS NULL'
43
+ }
44
+
45
+ sql += ' ORDER BY section, article_order'
46
+
47
+ const rows = db.prepare(sql).all(...params) as DocArticle[]
48
+ return rows
49
+ },
50
+
51
+ async get(id: string): Promise<DocArticle | null> {
52
+ const row = db.prepare('SELECT * FROM doc_articles WHERE id = ?').get(id) as DocArticle | undefined
53
+ return row ?? null
54
+ },
55
+
56
+ async create(data: CreateArticleInput): Promise<DocArticle> {
57
+ const id = nanoid()
58
+ const published_at = data.published_at ?? null
59
+
60
+ db.prepare(`
61
+ INSERT INTO doc_articles (id, title, body_md, section, article_order, published_at)
62
+ VALUES (?, ?, ?, ?, ?, ?)
63
+ `).run(id, data.title, data.body_md, data.section, data.article_order, published_at)
64
+
65
+ return { id, ...data, published_at }
66
+ },
67
+
68
+ async update(id: string, data: Partial<UpdateArticleInput>): Promise<DocArticle> {
69
+ const existing = await this.get(id)
70
+ if (!existing) {
71
+ throw new Error(`Article not found: ${id}`)
72
+ }
73
+
74
+ const updated = { ...existing, ...data }
75
+
76
+ db.prepare(`
77
+ UPDATE doc_articles SET title = ?, body_md = ? WHERE id = ?
78
+ `).run(updated.title, updated.body_md, id)
79
+
80
+ return updated
81
+ },
82
+
83
+ async delete(id: string): Promise<void> {
84
+ db.prepare('DELETE FROM doc_articles WHERE id = ?').run(id)
85
+ },
86
+
87
+ async reorder(id: string, newOrder: number): Promise<void> {
88
+ db.prepare('UPDATE doc_articles SET article_order = ? WHERE id = ?').run(newOrder, id)
89
+ },
90
+
91
+ async moveSection(id: string, newSection: string): Promise<void> {
92
+ db.prepare('UPDATE doc_articles SET section = ? WHERE id = ?').run(newSection, id)
93
+ },
94
+ }
95
+ }
@@ -0,0 +1,31 @@
1
+ import { createAdminHandler } from '../../../../handlers/admin'
2
+
3
+ const handler = createAdminHandler({})
4
+
5
+ export async function POST(
6
+ request: Request,
7
+ context: { params?: Record<string, string | string[]> }
8
+ ) {
9
+ return handler(request, context)
10
+ }
11
+
12
+ export async function PUT(
13
+ request: Request,
14
+ context: { params?: Record<string, string | string[]> }
15
+ ) {
16
+ return handler(request, context)
17
+ }
18
+
19
+ export async function PATCH(
20
+ request: Request,
21
+ context: { params?: Record<string, string | string[]> }
22
+ ) {
23
+ return handler(request, context)
24
+ }
25
+
26
+ export async function DELETE(
27
+ request: Request,
28
+ context: { params?: Record<string, string | string[]> }
29
+ ) {
30
+ return handler(request, context)
31
+ }
@@ -0,0 +1,10 @@
1
+ import { createDocsHandler } from '../../../../handlers/route'
2
+
3
+ const handler = createDocsHandler({})
4
+
5
+ export async function GET(
6
+ request: Request,
7
+ context: { params?: Record<string, string | string[]> }
8
+ ) {
9
+ return handler(request, context)
10
+ }