Skip to content

Commit e65edb0

Browse files
committed
winston/content (#247)
* feat(content): happy path for doc content * feat(content): Add ability to automatically link typed data for typescript SDKs * feat(content): add blog page and blog content * feat(content): blog rss * feat(content): Add last modified time, add reading time
1 parent b3a72a4 commit e65edb0

55 files changed

Lines changed: 3571 additions & 332 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,5 @@ sst-env.d.ts
103103
.aider*
104104
.cursorrules
105105

106+
# content-collections
107+
.content-collections

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
"arktype",
5252
"Creds",
5353
"dotenvx",
54+
"fumadocs",
55+
"metas",
5456
"millis",
5557
"oklch",
5658
"orpc",

apps/www/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"dependencies": {
2222
"@rectangular-labs/api": "workspace:*",
2323
"@rectangular-labs/auth": "workspace:*",
24+
"@rectangular-labs/content": "workspace:*",
2425
"@rectangular-labs/ui": "workspace:*",
2526
"@simplewebauthn/browser": "^13.1.2",
2627
"@t3-oss/env-core": "^0.13.8",

apps/www/src/routeTree.gen.ts

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ import { createServerRootRoute } from '@tanstack/react-start/server'
1212

1313
import { Route as rootRouteImport } from './routes/__root'
1414
import { Route as LoginRouteImport } from './routes/login'
15+
import { Route as DocsRouteRouteImport } from './routes/docs/route'
16+
import { Route as BlogRouteRouteImport } from './routes/blog/route'
1517
import { Route as AuthedRouteRouteImport } from './routes/_authed/route'
1618
import { Route as IndexRouteImport } from './routes/index'
19+
import { Route as DocsIndexRouteImport } from './routes/docs/index'
20+
import { Route as BlogIndexRouteImport } from './routes/blog/index'
21+
import { Route as DocsSplatRouteImport } from './routes/docs/$'
22+
import { Route as BlogSplatRouteImport } from './routes/blog/$'
1723
import { Route as AuthedOrpcRouteImport } from './routes/_authed/orpc'
24+
import { ServerRoute as BlogRssDotxmlServerRouteImport } from './routes/blog/rss[.]xml'
1825
import { ServerRoute as ApiSplatServerRouteImport } from './routes/api/$'
1926
import { ServerRoute as ApiRpcSplatServerRouteImport } from './routes/api/rpc.$'
2027

@@ -25,6 +32,16 @@ const LoginRoute = LoginRouteImport.update({
2532
path: '/login',
2633
getParentRoute: () => rootRouteImport,
2734
} as any)
35+
const DocsRouteRoute = DocsRouteRouteImport.update({
36+
id: '/docs',
37+
path: '/docs',
38+
getParentRoute: () => rootRouteImport,
39+
} as any)
40+
const BlogRouteRoute = BlogRouteRouteImport.update({
41+
id: '/blog',
42+
path: '/blog',
43+
getParentRoute: () => rootRouteImport,
44+
} as any)
2845
const AuthedRouteRoute = AuthedRouteRouteImport.update({
2946
id: '/_authed',
3047
getParentRoute: () => rootRouteImport,
@@ -34,11 +51,36 @@ const IndexRoute = IndexRouteImport.update({
3451
path: '/',
3552
getParentRoute: () => rootRouteImport,
3653
} as any)
54+
const DocsIndexRoute = DocsIndexRouteImport.update({
55+
id: '/',
56+
path: '/',
57+
getParentRoute: () => DocsRouteRoute,
58+
} as any)
59+
const BlogIndexRoute = BlogIndexRouteImport.update({
60+
id: '/',
61+
path: '/',
62+
getParentRoute: () => BlogRouteRoute,
63+
} as any)
64+
const DocsSplatRoute = DocsSplatRouteImport.update({
65+
id: '/$',
66+
path: '/$',
67+
getParentRoute: () => DocsRouteRoute,
68+
} as any)
69+
const BlogSplatRoute = BlogSplatRouteImport.update({
70+
id: '/$',
71+
path: '/$',
72+
getParentRoute: () => BlogRouteRoute,
73+
} as any)
3774
const AuthedOrpcRoute = AuthedOrpcRouteImport.update({
3875
id: '/orpc',
3976
path: '/orpc',
4077
getParentRoute: () => AuthedRouteRoute,
4178
} as any)
79+
const BlogRssDotxmlServerRoute = BlogRssDotxmlServerRouteImport.update({
80+
id: '/blog/rss.xml',
81+
path: '/blog/rss.xml',
82+
getParentRoute: () => rootServerRouteImport,
83+
} as any)
4284
const ApiSplatServerRoute = ApiSplatServerRouteImport.update({
4385
id: '/api/$',
4486
path: '/api/$',
@@ -52,57 +94,99 @@ const ApiRpcSplatServerRoute = ApiRpcSplatServerRouteImport.update({
5294

5395
export interface FileRoutesByFullPath {
5496
'/': typeof IndexRoute
97+
'/blog': typeof BlogRouteRouteWithChildren
98+
'/docs': typeof DocsRouteRouteWithChildren
5599
'/login': typeof LoginRoute
56100
'/orpc': typeof AuthedOrpcRoute
101+
'/blog/$': typeof BlogSplatRoute
102+
'/docs/$': typeof DocsSplatRoute
103+
'/blog/': typeof BlogIndexRoute
104+
'/docs/': typeof DocsIndexRoute
57105
}
58106
export interface FileRoutesByTo {
59107
'/': typeof IndexRoute
60108
'/login': typeof LoginRoute
61109
'/orpc': typeof AuthedOrpcRoute
110+
'/blog/$': typeof BlogSplatRoute
111+
'/docs/$': typeof DocsSplatRoute
112+
'/blog': typeof BlogIndexRoute
113+
'/docs': typeof DocsIndexRoute
62114
}
63115
export interface FileRoutesById {
64116
__root__: typeof rootRouteImport
65117
'/': typeof IndexRoute
66118
'/_authed': typeof AuthedRouteRouteWithChildren
119+
'/blog': typeof BlogRouteRouteWithChildren
120+
'/docs': typeof DocsRouteRouteWithChildren
67121
'/login': typeof LoginRoute
68122
'/_authed/orpc': typeof AuthedOrpcRoute
123+
'/blog/$': typeof BlogSplatRoute
124+
'/docs/$': typeof DocsSplatRoute
125+
'/blog/': typeof BlogIndexRoute
126+
'/docs/': typeof DocsIndexRoute
69127
}
70128
export interface FileRouteTypes {
71129
fileRoutesByFullPath: FileRoutesByFullPath
72-
fullPaths: '/' | '/login' | '/orpc'
130+
fullPaths:
131+
| '/'
132+
| '/blog'
133+
| '/docs'
134+
| '/login'
135+
| '/orpc'
136+
| '/blog/$'
137+
| '/docs/$'
138+
| '/blog/'
139+
| '/docs/'
73140
fileRoutesByTo: FileRoutesByTo
74-
to: '/' | '/login' | '/orpc'
75-
id: '__root__' | '/' | '/_authed' | '/login' | '/_authed/orpc'
141+
to: '/' | '/login' | '/orpc' | '/blog/$' | '/docs/$' | '/blog' | '/docs'
142+
id:
143+
| '__root__'
144+
| '/'
145+
| '/_authed'
146+
| '/blog'
147+
| '/docs'
148+
| '/login'
149+
| '/_authed/orpc'
150+
| '/blog/$'
151+
| '/docs/$'
152+
| '/blog/'
153+
| '/docs/'
76154
fileRoutesById: FileRoutesById
77155
}
78156
export interface RootRouteChildren {
79157
IndexRoute: typeof IndexRoute
80158
AuthedRouteRoute: typeof AuthedRouteRouteWithChildren
159+
BlogRouteRoute: typeof BlogRouteRouteWithChildren
160+
DocsRouteRoute: typeof DocsRouteRouteWithChildren
81161
LoginRoute: typeof LoginRoute
82162
}
83163
export interface FileServerRoutesByFullPath {
84164
'/api/$': typeof ApiSplatServerRoute
165+
'/blog/rss.xml': typeof BlogRssDotxmlServerRoute
85166
'/api/rpc/$': typeof ApiRpcSplatServerRoute
86167
}
87168
export interface FileServerRoutesByTo {
88169
'/api/$': typeof ApiSplatServerRoute
170+
'/blog/rss.xml': typeof BlogRssDotxmlServerRoute
89171
'/api/rpc/$': typeof ApiRpcSplatServerRoute
90172
}
91173
export interface FileServerRoutesById {
92174
__root__: typeof rootServerRouteImport
93175
'/api/$': typeof ApiSplatServerRoute
176+
'/blog/rss.xml': typeof BlogRssDotxmlServerRoute
94177
'/api/rpc/$': typeof ApiRpcSplatServerRoute
95178
}
96179
export interface FileServerRouteTypes {
97180
fileServerRoutesByFullPath: FileServerRoutesByFullPath
98-
fullPaths: '/api/$' | '/api/rpc/$'
181+
fullPaths: '/api/$' | '/blog/rss.xml' | '/api/rpc/$'
99182
fileServerRoutesByTo: FileServerRoutesByTo
100-
to: '/api/$' | '/api/rpc/$'
101-
id: '__root__' | '/api/$' | '/api/rpc/$'
183+
to: '/api/$' | '/blog/rss.xml' | '/api/rpc/$'
184+
id: '__root__' | '/api/$' | '/blog/rss.xml' | '/api/rpc/$'
102185
fileServerRoutesById: FileServerRoutesById
103186
}
104187
export interface RootServerRouteChildren {
105188
ApiSplatServerRoute: typeof ApiSplatServerRoute
189+
BlogRssDotxmlServerRoute: typeof BlogRssDotxmlServerRoute
106190
ApiRpcSplatServerRoute: typeof ApiRpcSplatServerRoute
107191
}
108192

@@ -115,6 +199,20 @@ declare module '@tanstack/react-router' {
115199
preLoaderRoute: typeof LoginRouteImport
116200
parentRoute: typeof rootRouteImport
117201
}
202+
'/docs': {
203+
id: '/docs'
204+
path: '/docs'
205+
fullPath: '/docs'
206+
preLoaderRoute: typeof DocsRouteRouteImport
207+
parentRoute: typeof rootRouteImport
208+
}
209+
'/blog': {
210+
id: '/blog'
211+
path: '/blog'
212+
fullPath: '/blog'
213+
preLoaderRoute: typeof BlogRouteRouteImport
214+
parentRoute: typeof rootRouteImport
215+
}
118216
'/_authed': {
119217
id: '/_authed'
120218
path: ''
@@ -129,6 +227,34 @@ declare module '@tanstack/react-router' {
129227
preLoaderRoute: typeof IndexRouteImport
130228
parentRoute: typeof rootRouteImport
131229
}
230+
'/docs/': {
231+
id: '/docs/'
232+
path: '/'
233+
fullPath: '/docs/'
234+
preLoaderRoute: typeof DocsIndexRouteImport
235+
parentRoute: typeof DocsRouteRoute
236+
}
237+
'/blog/': {
238+
id: '/blog/'
239+
path: '/'
240+
fullPath: '/blog/'
241+
preLoaderRoute: typeof BlogIndexRouteImport
242+
parentRoute: typeof BlogRouteRoute
243+
}
244+
'/docs/$': {
245+
id: '/docs/$'
246+
path: '/$'
247+
fullPath: '/docs/$'
248+
preLoaderRoute: typeof DocsSplatRouteImport
249+
parentRoute: typeof DocsRouteRoute
250+
}
251+
'/blog/$': {
252+
id: '/blog/$'
253+
path: '/$'
254+
fullPath: '/blog/$'
255+
preLoaderRoute: typeof BlogSplatRouteImport
256+
parentRoute: typeof BlogRouteRoute
257+
}
132258
'/_authed/orpc': {
133259
id: '/_authed/orpc'
134260
path: '/orpc'
@@ -140,6 +266,13 @@ declare module '@tanstack/react-router' {
140266
}
141267
declare module '@tanstack/react-start/server' {
142268
interface ServerFileRoutesByPath {
269+
'/blog/rss.xml': {
270+
id: '/blog/rss.xml'
271+
path: '/blog/rss.xml'
272+
fullPath: '/blog/rss.xml'
273+
preLoaderRoute: typeof BlogRssDotxmlServerRouteImport
274+
parentRoute: typeof rootServerRouteImport
275+
}
143276
'/api/$': {
144277
id: '/api/$'
145278
path: '/api/$'
@@ -169,16 +302,47 @@ const AuthedRouteRouteWithChildren = AuthedRouteRoute._addFileChildren(
169302
AuthedRouteRouteChildren,
170303
)
171304

305+
interface BlogRouteRouteChildren {
306+
BlogSplatRoute: typeof BlogSplatRoute
307+
BlogIndexRoute: typeof BlogIndexRoute
308+
}
309+
310+
const BlogRouteRouteChildren: BlogRouteRouteChildren = {
311+
BlogSplatRoute: BlogSplatRoute,
312+
BlogIndexRoute: BlogIndexRoute,
313+
}
314+
315+
const BlogRouteRouteWithChildren = BlogRouteRoute._addFileChildren(
316+
BlogRouteRouteChildren,
317+
)
318+
319+
interface DocsRouteRouteChildren {
320+
DocsSplatRoute: typeof DocsSplatRoute
321+
DocsIndexRoute: typeof DocsIndexRoute
322+
}
323+
324+
const DocsRouteRouteChildren: DocsRouteRouteChildren = {
325+
DocsSplatRoute: DocsSplatRoute,
326+
DocsIndexRoute: DocsIndexRoute,
327+
}
328+
329+
const DocsRouteRouteWithChildren = DocsRouteRoute._addFileChildren(
330+
DocsRouteRouteChildren,
331+
)
332+
172333
const rootRouteChildren: RootRouteChildren = {
173334
IndexRoute: IndexRoute,
174335
AuthedRouteRoute: AuthedRouteRouteWithChildren,
336+
BlogRouteRoute: BlogRouteRouteWithChildren,
337+
DocsRouteRoute: DocsRouteRouteWithChildren,
175338
LoginRoute: LoginRoute,
176339
}
177340
export const routeTree = rootRouteImport
178341
._addFileChildren(rootRouteChildren)
179342
._addFileTypes<FileRouteTypes>()
180343
const rootServerRouteChildren: RootServerRouteChildren = {
181344
ApiSplatServerRoute: ApiSplatServerRoute,
345+
BlogRssDotxmlServerRoute: BlogRssDotxmlServerRoute,
182346
ApiRpcSplatServerRoute: ApiRpcSplatServerRoute,
183347
}
184348
export const serverRouteTree = rootServerRouteImport

apps/www/src/routes/__root.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "@tanstack/react-router";
1111
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
1212
import { seo } from "~/lib/seo";
13-
import appCss from "../style.css?url";
13+
import appCss from "../styles.css?url";
1414

1515
export const Route = createRootRouteWithContext<{
1616
queryClient: QueryClient;
@@ -29,6 +29,12 @@ export const Route = createRootRouteWithContext<{
2929
}),
3030
],
3131
links: [
32+
{
33+
rel: "alternate",
34+
type: "application/rss+xml",
35+
href: "/blog/rss.xml",
36+
title: "Blog RSS",
37+
},
3238
{ rel: "stylesheet", href: appCss },
3339
{
3440
rel: "apple-touch-icon",

apps/www/src/routes/api/$.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { createApiContext } from "@rectangular-labs/api/context";
22
import { openAPIHandler } from "@rectangular-labs/api/server";
33
import { initAuthHandler } from "@rectangular-labs/auth";
4+
import {
5+
createBlogSearchServer,
6+
createDocsSearchServer,
7+
} from "@rectangular-labs/content/search";
48
import { createServerFileRoute } from "@tanstack/react-start/server";
59
import { serverEnv } from "~/lib/env";
610

@@ -10,6 +14,21 @@ async function handle({ request }: { request: Request }) {
1014
return await auth.handler(request);
1115
}
1216

17+
if (new URL(request.url).pathname.startsWith("/api/docs/search")) {
18+
if (request.method !== "GET") {
19+
return new Response("Method not allowed", { status: 405 });
20+
}
21+
const search = createDocsSearchServer();
22+
return await search.GET(request);
23+
}
24+
if (new URL(request.url).pathname.startsWith("/api/blog/search")) {
25+
if (request.method !== "GET") {
26+
return new Response("Method not allowed", { status: 405 });
27+
}
28+
const search = createBlogSearchServer();
29+
return await search.GET(request);
30+
}
31+
1332
const env = serverEnv();
1433
const context = createApiContext({
1534
dbUrl: env.DATABASE_URL,

0 commit comments

Comments
 (0)