@rimelight/cms 0.0.2 → 0.0.4
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 +21 -0
- package/dist/bylines-Csggkl-g.d.mts +405 -0
- package/dist/index.d.mts +724 -0
- package/dist/index.mjs +2382 -0
- package/dist/integration.d.mts +33 -0
- package/dist/integration.mjs +161 -0
- package/dist/schema/index.d.mts +2063 -0
- package/dist/schema/index.mjs +176 -0
- package/dist/schema/sqlite.d.mts +2132 -0
- package/dist/schema/sqlite.mjs +137 -0
- package/dist/storage/index.d.mts +59 -0
- package/dist/storage/index.mjs +133 -0
- package/package.json +36 -18
- package/src/admin/api/media-file.ts +39 -0
- package/src/admin/api/media.ts +224 -0
- package/src/admin/layouts/CMSDashboardLayout.astro +191 -0
- package/src/admin/layouts/DefaultParentLayout.astro +50 -0
- package/src/admin/pages/assets.astro +424 -0
- package/src/admin/pages/byline-schema.astro +62 -0
- package/src/admin/pages/bylines.astro +16 -0
- package/src/admin/pages/categories.astro +21 -0
- package/src/admin/pages/content-types.astro +55 -0
- package/src/admin/pages/dashboard.astro +61 -0
- package/src/admin/pages/pages-edit.astro +595 -0
- package/src/admin/pages/pages-index.astro +146 -0
- package/src/admin/pages/pages-new.astro +130 -0
- package/src/admin/pages/pages-preview.astro +111 -0
- package/src/admin/pages/pages-review.astro +236 -0
- package/src/admin/pages/settings.astro +203 -0
- package/src/admin/pages/tags.astro +21 -0
- package/src/admin/pages/templates-edit.astro +234 -0
- package/src/admin/pages/templates-index.astro +95 -0
- package/src/admin/pages/templates-new.astro +193 -0
- package/src/admin/pages/users.astro +74 -0
- package/src/admin/pages/versions.astro +74 -0
- package/src/astro/BlockRenderer.astro +44 -102
- package/src/astro/PageRenderer.astro +12 -5
- package/src/astro/blocks/CalloutBlock.astro +7 -7
- package/src/astro/blocks/CardBlock.astro +5 -5
- package/src/astro/blocks/CodeBlock.astro +2 -2
- package/src/astro/blocks/FileTreeBlock.astro +7 -7
- package/src/astro/blocks/ImageBlock.astro +2 -2
- package/src/astro/blocks/OrderedListBlock.astro +2 -2
- package/src/astro/blocks/ParagraphBlock.astro +3 -3
- package/src/astro/blocks/SectionBlock.astro +6 -6
- package/src/astro/blocks/StepItemBlock.astro +3 -3
- package/src/astro/blocks/StepsBlock.astro +1 -1
- package/src/astro/blocks/TableBlock.astro +6 -6
- package/src/astro/blocks/TabsBlock.astro +9 -9
- package/src/astro/blocks/UnorderedListBlock.astro +2 -2
- package/src/astro/blocks/index.ts +35 -0
- package/src/client/components/RimelightBlock.tsx +19 -19
- package/src/client/components/RimelightBlockPicker.tsx +9 -13
- package/src/client/components/RimelightBylinesManager.tsx +438 -0
- package/src/client/components/RimelightEditor.tsx +3 -3
- package/src/client/components/RimelightPreview.tsx +37 -45
- package/src/client/components/RimelightPropertiesPanel.tsx +368 -26
- package/src/client/components/RimelightTaxonomyManager.tsx +369 -0
- package/src/client/components/RimelightTaxonomyMasterDetail.tsx +412 -0
- package/src/client/components/RimelightTemplateEditor.tsx +6 -8
- package/src/client/components/editors/CalloutEditor.tsx +2 -4
- package/src/client/components/editors/CardsEditor.tsx +2 -4
- package/src/client/components/editors/CodeEditor.tsx +1 -3
- package/src/client/components/editors/FileTreeEditor.tsx +2 -4
- package/src/client/components/editors/ImageEditor.tsx +3 -3
- package/src/client/components/editors/ParagraphEditor.tsx +2 -2
- package/src/client/components/editors/SceneEditor.tsx +6 -12
- package/src/client/components/editors/ScriptEditor.tsx +8 -16
- package/src/client/components/editors/SectionEditor.tsx +4 -4
- package/src/client/components/editors/StepItemEditor.tsx +2 -2
- package/src/client/components/editors/StepsEditor.tsx +2 -4
- package/src/client/components/editors/TabItemEditor.tsx +3 -3
- package/src/client/components/editors/TableEditor.tsx +5 -5
- package/src/client/components/editors/TabsEditor.tsx +2 -4
- package/src/client/components/index.ts +3 -0
- package/src/client/loader.ts +3 -0
- package/src/core/diff.ts +145 -0
- package/src/core/excerpt.ts +159 -0
- package/src/core/preview.ts +87 -0
- package/src/core/registry.ts +0 -86
- package/src/core/types/blocks.ts +6 -1
- package/src/core/types/bylines.ts +14 -0
- package/src/core/types/media.ts +13 -0
- package/src/core/types/taxonomies.ts +20 -0
- package/src/cron/scheduled.ts +57 -0
- package/src/docs/agent.ts +116 -0
- package/src/docs/config.ts +25 -0
- package/src/docs/index.ts +5 -0
- package/src/docs/routing.ts +104 -0
- package/src/docs/sidebar.ts +251 -0
- package/src/docs/toc.ts +47 -0
- package/src/env.d.ts +19 -0
- package/src/index.ts +57 -12
- package/src/integration.ts +250 -0
- package/src/loader/live.ts +254 -0
- package/src/markdown/index.ts +0 -1
- package/src/mcp/index.ts +228 -0
- package/src/schema/bylines.ts +23 -0
- package/src/schema/index.ts +3 -4
- package/src/schema/page_versions.ts +2 -0
- package/src/schema/pages.ts +2 -0
- package/src/schema/site_settings.ts +41 -0
- package/src/schema/sqlite.ts +344 -0
- package/src/schema/taxonomies.ts +49 -0
- package/src/services/bylines.ts +74 -0
- package/src/services/search-indexer.ts +61 -0
- package/src/services/site-settings.ts +108 -0
- package/src/services/taxonomies.ts +276 -0
- package/src/storage/index.ts +256 -0
- package/src/astro/blocks/ApiDocumentationBlock.astro +0 -350
- package/src/astro/blocks/DialogueBlock.astro +0 -23
- package/src/astro/blocks/LivePreviewBlock.astro +0 -27
- package/src/astro/blocks/SceneBlock.astro +0 -65
- package/src/astro/blocks/ScriptBlock.astro +0 -59
- package/src/core/template-sync.ts +0 -75
- package/src/markdown/parser.ts +0 -347
- package/src/migration-tools.ts +0 -334
- package/src/migration.ts +0 -155
- package/src/schema/component_dependencies.ts +0 -21
- package/src/schema/component_metadata.ts +0 -31
- package/src/schema/component_metadata_cache.ts +0 -15
- package/src/schema/component_versions.ts +0 -19
- package/src/search/sync.ts +0 -32
- package/src/services/component-metadata.ts +0 -134
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import type { DocsConfig, SidebarItem, SidebarScope, BadgeProps } from "./config"
|
|
2
|
+
|
|
3
|
+
export interface ResolvedSidebarItem {
|
|
4
|
+
label: string
|
|
5
|
+
href?: string | undefined
|
|
6
|
+
icon?: string | undefined
|
|
7
|
+
badge?: string | BadgeProps | undefined
|
|
8
|
+
active?: boolean | undefined
|
|
9
|
+
children?: ResolvedSidebarItem[] | undefined
|
|
10
|
+
collapsed?: boolean | undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ResolvedScope {
|
|
14
|
+
id: string
|
|
15
|
+
label: string
|
|
16
|
+
href: string
|
|
17
|
+
icon?: string | undefined
|
|
18
|
+
active?: boolean | undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatDocsUrl(
|
|
22
|
+
href: string,
|
|
23
|
+
locale: string,
|
|
24
|
+
activeVersion?: string,
|
|
25
|
+
includeVersionInUrl?: boolean
|
|
26
|
+
): string {
|
|
27
|
+
let url = href.replace("{locale}", locale)
|
|
28
|
+
if (activeVersion && includeVersionInUrl) {
|
|
29
|
+
if (url.includes(`/${activeVersion}/`) || url.endsWith(`/${activeVersion}`)) {
|
|
30
|
+
return url
|
|
31
|
+
}
|
|
32
|
+
const docsPattern = new RegExp(`(/${locale}/docs)(/|$)`)
|
|
33
|
+
const companyDocsPattern = new RegExp(`(/${locale}/company/docs)(/|$)`)
|
|
34
|
+
|
|
35
|
+
if (companyDocsPattern.test(url)) {
|
|
36
|
+
url = url.replace(companyDocsPattern, `$1/${activeVersion}$2`)
|
|
37
|
+
} else if (docsPattern.test(url)) {
|
|
38
|
+
url = url.replace(docsPattern, `$1/${activeVersion}$2`)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
url = url.replace(/\/{2,}/g, "/")
|
|
42
|
+
}
|
|
43
|
+
return url
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function findFirstHref(
|
|
47
|
+
items: SidebarItem[],
|
|
48
|
+
locale: string,
|
|
49
|
+
activeVersion?: string,
|
|
50
|
+
includeVersionInUrl?: boolean
|
|
51
|
+
): string | undefined {
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
if (item.href) return formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl)
|
|
54
|
+
const childItems = item.children || item.items
|
|
55
|
+
if (childItems) {
|
|
56
|
+
const found = findFirstHref(childItems, locale, activeVersion, includeVersionInUrl)
|
|
57
|
+
if (found) return found
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getScopePrefix(
|
|
64
|
+
items: SidebarItem[],
|
|
65
|
+
locale: string,
|
|
66
|
+
activeVersion?: string,
|
|
67
|
+
includeVersionInUrl?: boolean
|
|
68
|
+
): string {
|
|
69
|
+
const allHrefs: string[] = []
|
|
70
|
+
|
|
71
|
+
function collect(item: SidebarItem) {
|
|
72
|
+
if (item.href)
|
|
73
|
+
allHrefs.push(formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl))
|
|
74
|
+
const childItems = item.children || item.items
|
|
75
|
+
if (childItems) childItems.forEach(collect)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
items.forEach(collect)
|
|
79
|
+
if (allHrefs.length === 0) return ""
|
|
80
|
+
|
|
81
|
+
const parts = allHrefs.map((h) => h.split("/").filter(Boolean))
|
|
82
|
+
let commonLen = 0
|
|
83
|
+
const firstPart = parts[0]
|
|
84
|
+
if (!firstPart) return ""
|
|
85
|
+
for (let i = 0; i < firstPart.length; i++) {
|
|
86
|
+
if (parts.every((p) => p[i] === firstPart[i])) commonLen++
|
|
87
|
+
else break
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return "/" + firstPart.slice(0, commonLen).join("/") + "/"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function hasPath(
|
|
94
|
+
items: SidebarItem[],
|
|
95
|
+
locale: string,
|
|
96
|
+
normPath: string,
|
|
97
|
+
activeVersion?: string,
|
|
98
|
+
includeVersionInUrl?: boolean
|
|
99
|
+
): boolean {
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
if (
|
|
102
|
+
item.href &&
|
|
103
|
+
formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl)
|
|
104
|
+
.replace(/\/$/, "")
|
|
105
|
+
.toLowerCase() === normPath
|
|
106
|
+
) {
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
const childItems = item.children || item.items
|
|
110
|
+
if (childItems && hasPath(childItems, locale, normPath, activeVersion, includeVersionInUrl)) {
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function getDocsSidebar(
|
|
118
|
+
config: DocsConfig,
|
|
119
|
+
locale: string,
|
|
120
|
+
currentPath: string,
|
|
121
|
+
activeVersion?: string,
|
|
122
|
+
includeVersionInUrl?: boolean
|
|
123
|
+
): Promise<{
|
|
124
|
+
items: ResolvedSidebarItem[]
|
|
125
|
+
scopes: ResolvedScope[]
|
|
126
|
+
activeScope?: ResolvedScope | undefined
|
|
127
|
+
}> {
|
|
128
|
+
const normPath = currentPath.replace(/\/$/, "").toLowerCase()
|
|
129
|
+
|
|
130
|
+
const sidebarScopes: SidebarScope[] = Array.isArray(config.sidebar)
|
|
131
|
+
? config.sidebar.filter((item): item is SidebarScope => "id" in item || "scopes" in item)
|
|
132
|
+
: config.sidebar.scopes || []
|
|
133
|
+
|
|
134
|
+
const scopes: ResolvedScope[] = sidebarScopes.map((scope) => {
|
|
135
|
+
const id = scope.id || scope.label
|
|
136
|
+
let href = scope.href || ""
|
|
137
|
+
if (!href && scope.items && scope.items.length > 0) {
|
|
138
|
+
href = findFirstHref(scope.items, locale, activeVersion, includeVersionInUrl) || ""
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
label: scope.label,
|
|
143
|
+
href: formatDocsUrl(href, locale, activeVersion, includeVersionInUrl),
|
|
144
|
+
icon: scope.icon
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const scopePrefixes: string[] = sidebarScopes.map((scope) =>
|
|
149
|
+
scope.items ? getScopePrefix(scope.items, locale, activeVersion, includeVersionInUrl) : ""
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
let activeScope = scopes.find((s) => normPath === s.href.replace(/\/$/, "").toLowerCase())
|
|
153
|
+
if (!activeScope) {
|
|
154
|
+
for (let i = 0; i < sidebarScopes.length; i++) {
|
|
155
|
+
const scope = sidebarScopes[i]
|
|
156
|
+
if (
|
|
157
|
+
scope &&
|
|
158
|
+
scope.items &&
|
|
159
|
+
hasPath(scope.items, locale, normPath, activeVersion, includeVersionInUrl)
|
|
160
|
+
) {
|
|
161
|
+
activeScope = scopes[i]
|
|
162
|
+
break
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!activeScope) {
|
|
167
|
+
const sorted = [...scopePrefixes.entries()]
|
|
168
|
+
.filter(([, p]) => p)
|
|
169
|
+
.toSorted(([, a], [, b]) => b.length - a.length)
|
|
170
|
+
for (const [i] of sorted) {
|
|
171
|
+
const prefix = scopePrefixes[i]
|
|
172
|
+
if (prefix && normPath.startsWith(prefix.toLowerCase())) {
|
|
173
|
+
activeScope = scopes[i]
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!activeScope && scopes.length > 0) {
|
|
179
|
+
activeScope = scopes[0]
|
|
180
|
+
}
|
|
181
|
+
if (activeScope) activeScope.active = true
|
|
182
|
+
|
|
183
|
+
const sidebarConfig = sidebarScopes.find(
|
|
184
|
+
(item) => activeScope && (item.id === activeScope.id || item.label === activeScope.id)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const items = sidebarConfig?.items || []
|
|
188
|
+
|
|
189
|
+
async function resolveItem(item: SidebarItem): Promise<ResolvedSidebarItem[]> {
|
|
190
|
+
const resolved: ResolvedSidebarItem = {
|
|
191
|
+
label: item.label || "",
|
|
192
|
+
href: item.href
|
|
193
|
+
? formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl)
|
|
194
|
+
: undefined,
|
|
195
|
+
icon: item.icon,
|
|
196
|
+
badge: item.badge,
|
|
197
|
+
collapsed: item.collapsed
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (resolved.href) {
|
|
201
|
+
resolved.active = normPath === resolved.href.replace(/\/$/, "").toLowerCase()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const childItems = item.children || item.items
|
|
205
|
+
if (childItems) {
|
|
206
|
+
const children: ResolvedSidebarItem[] = []
|
|
207
|
+
const resolvedChildren = await Promise.all(childItems.map((child) => resolveItem(child)))
|
|
208
|
+
for (const res of resolvedChildren) {
|
|
209
|
+
children.push(...res)
|
|
210
|
+
}
|
|
211
|
+
resolved.children = children
|
|
212
|
+
if (!resolved.active) {
|
|
213
|
+
resolved.active = children.some((c) => c.active)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return [resolved]
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const resolvedItems: ResolvedSidebarItem[] = []
|
|
221
|
+
const resolvedAllItems = await Promise.all(items.map((item) => resolveItem(item)))
|
|
222
|
+
for (const res of resolvedAllItems) {
|
|
223
|
+
resolvedItems.push(...res)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { items: resolvedItems, scopes, activeScope }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function getSurroundItems(sidebar: ResolvedSidebarItem[], currentPath: string) {
|
|
230
|
+
const flatItems: { label: string; href: string }[] = []
|
|
231
|
+
|
|
232
|
+
function flatten(items: ResolvedSidebarItem[]) {
|
|
233
|
+
for (const item of items) {
|
|
234
|
+
if (item.href) flatItems.push({ label: item.label, href: item.href })
|
|
235
|
+
if (item.children) flatten(item.children)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
flatten(sidebar)
|
|
240
|
+
|
|
241
|
+
const normPath = currentPath.replace(/\/$/, "").toLowerCase()
|
|
242
|
+
const currentIndex = flatItems.findIndex(
|
|
243
|
+
(item) => item.href.replace(/\/$/, "").toLowerCase() === normPath
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
prev: currentIndex > 0 ? flatItems[currentIndex - 1] : null,
|
|
248
|
+
next:
|
|
249
|
+
currentIndex >= 0 && currentIndex < flatItems.length - 1 ? flatItems[currentIndex + 1] : null
|
|
250
|
+
}
|
|
251
|
+
}
|
package/src/docs/toc.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface TableOfContentsHeading {
|
|
2
|
+
depth: number
|
|
3
|
+
slug: string
|
|
4
|
+
text: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function slugifyHeading(text: string): string {
|
|
8
|
+
return text
|
|
9
|
+
.toString()
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.normalize("NFD")
|
|
12
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
13
|
+
.replace(/\s+/g, "-")
|
|
14
|
+
.replace(/[^\w-]+/g, "")
|
|
15
|
+
.replace(/-{2,}/g, "-")
|
|
16
|
+
.replace(/^-+/, "")
|
|
17
|
+
.replace(/-+$/, "")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function extractHeadingsFromBlocks(
|
|
21
|
+
blocks: any[] = [],
|
|
22
|
+
currentDepth = 2
|
|
23
|
+
): TableOfContentsHeading[] {
|
|
24
|
+
if (!Array.isArray(blocks)) return []
|
|
25
|
+
const headings: TableOfContentsHeading[] = []
|
|
26
|
+
|
|
27
|
+
for (const block of blocks) {
|
|
28
|
+
if (!block) continue
|
|
29
|
+
if (block.type === "SectionBlock") {
|
|
30
|
+
const title = block.props?.title || ""
|
|
31
|
+
const level = Number(block.props?.level) || currentDepth
|
|
32
|
+
if (title) {
|
|
33
|
+
headings.push({
|
|
34
|
+
depth: level,
|
|
35
|
+
slug: slugifyHeading(title),
|
|
36
|
+
text: title
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
const children = block.children || block.props?.children || []
|
|
40
|
+
if (Array.isArray(children) && children.length > 0) {
|
|
41
|
+
headings.push(...extractHeadingsFromBlocks(children, Math.min(6, level + 1)))
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return headings
|
|
47
|
+
}
|
package/src/env.d.ts
CHANGED
|
@@ -2,3 +2,22 @@ declare module "*.astro" {
|
|
|
2
2
|
const component: any
|
|
3
3
|
export default component
|
|
4
4
|
}
|
|
5
|
+
|
|
6
|
+
declare module "virtual:rimelight-cms/parent-layout" {
|
|
7
|
+
const layout: any
|
|
8
|
+
export default layout
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module "virtual:rimelight-cms/db" {
|
|
12
|
+
export const db: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module "virtual:rimelight-cms/storage" {
|
|
16
|
+
export const storageConfig: any
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare module "virtual:rimelight-cms/auth" {
|
|
20
|
+
import type { AuthAdapter } from "@rimelight/auth"
|
|
21
|
+
export const authAdapter: AuthAdapter
|
|
22
|
+
export default authAdapter
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,9 @@ export * from "./core/types/templates.ts"
|
|
|
6
6
|
export * from "./core/types/versioning.ts"
|
|
7
7
|
export * from "./core/page-definitions.ts"
|
|
8
8
|
|
|
9
|
+
// Markdown Serialization & Parsing
|
|
10
|
+
export * from "./markdown/index.ts"
|
|
11
|
+
|
|
9
12
|
// Re-export specific types for convenience
|
|
10
13
|
export type {
|
|
11
14
|
TimeOfDay,
|
|
@@ -19,31 +22,73 @@ export type {
|
|
|
19
22
|
// Core Services & Registries
|
|
20
23
|
export * from "./core/registry.ts"
|
|
21
24
|
export * from "./core/validator.ts"
|
|
22
|
-
export * from "./core/template-sync.ts"
|
|
23
|
-
export * from "./services/component-metadata.ts"
|
|
24
25
|
|
|
25
|
-
// Auth &
|
|
26
|
+
// Universal Auth & Pluggable Adapters (RFC-0004 & RFC-0006)
|
|
26
27
|
export * from "./auth/permissions.ts"
|
|
27
28
|
export * from "./auth/filter-blocks.ts"
|
|
28
29
|
export * from "./auth/editor-guards.ts"
|
|
29
30
|
export * from "./auth/filter-templates.ts"
|
|
31
|
+
export {
|
|
32
|
+
cfAccessAuth,
|
|
33
|
+
auth0Auth,
|
|
34
|
+
mockAuth,
|
|
35
|
+
hasRole,
|
|
36
|
+
hasPermission,
|
|
37
|
+
evaluateAccess
|
|
38
|
+
} from "@rimelight/auth"
|
|
39
|
+
export type {
|
|
40
|
+
AuthAdapter as CmsAuthAdapter,
|
|
41
|
+
AuthAdapter,
|
|
42
|
+
UserSessionContext,
|
|
43
|
+
UserType
|
|
44
|
+
} from "@rimelight/auth"
|
|
30
45
|
|
|
31
46
|
// Edge Cache & Search
|
|
32
47
|
export * from "./cache/purge.ts"
|
|
33
48
|
export * from "./search/query.ts"
|
|
34
|
-
export * from "./search/sync.ts"
|
|
35
49
|
|
|
36
50
|
// Database Schemas & Migration
|
|
37
51
|
export * from "./schema/index.ts"
|
|
38
|
-
export * from "./migration.ts"
|
|
39
52
|
|
|
40
|
-
//
|
|
41
|
-
export
|
|
42
|
-
export * from "./migration-tools.ts"
|
|
43
|
-
|
|
44
|
-
// Astro Components
|
|
45
|
-
export { default as PageRenderer } from "./astro/PageRenderer.astro"
|
|
46
|
-
export { default as BlockRenderer } from "./astro/BlockRenderer.astro"
|
|
53
|
+
// Astro Integration & Components
|
|
54
|
+
export { rimelightCms, type RimelightCmsOptions } from "./integration.ts"
|
|
47
55
|
|
|
48
56
|
// Toast API
|
|
49
57
|
export * from "./client/toast.ts"
|
|
58
|
+
|
|
59
|
+
// Excerpts & Summaries
|
|
60
|
+
export * from "./core/excerpt.ts"
|
|
61
|
+
|
|
62
|
+
// Dynamic Site Settings
|
|
63
|
+
export * from "./services/site-settings.ts"
|
|
64
|
+
|
|
65
|
+
// Astro Live Loader & Collections
|
|
66
|
+
export * from "./loader/live.ts"
|
|
67
|
+
|
|
68
|
+
// Cloudflare Worker Cron Dispatcher
|
|
69
|
+
export * from "./cron/scheduled.ts"
|
|
70
|
+
|
|
71
|
+
// Model Context Protocol (MCP) Tools
|
|
72
|
+
export * from "./mcp/index.ts"
|
|
73
|
+
|
|
74
|
+
// Media & Bylines
|
|
75
|
+
export * from "./core/types/media.ts"
|
|
76
|
+
export * from "./core/types/bylines.ts"
|
|
77
|
+
export * from "./services/bylines.ts"
|
|
78
|
+
|
|
79
|
+
// Preview Tokens & Block Diffs
|
|
80
|
+
export * from "./core/preview.ts"
|
|
81
|
+
export * from "./core/diff.ts"
|
|
82
|
+
|
|
83
|
+
// Search Indexer
|
|
84
|
+
export * from "./services/search-indexer.ts"
|
|
85
|
+
|
|
86
|
+
// Taxonomies Engine
|
|
87
|
+
export * from "./core/types/taxonomies.ts"
|
|
88
|
+
export * from "./services/taxonomies.ts"
|
|
89
|
+
|
|
90
|
+
// Documentation Engine & Navigation
|
|
91
|
+
export * from "./docs/index.ts"
|
|
92
|
+
|
|
93
|
+
// Storage Adapters (R2, etc.)
|
|
94
|
+
export * from "./storage/index.ts"
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import type { AstroIntegration } from "astro"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import fs from "node:fs"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
import type { StorageConfig } from "./storage/index"
|
|
6
|
+
import type { AuthAdapter } from "@rimelight/auth"
|
|
7
|
+
|
|
8
|
+
export interface RimelightCmsOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Route prefix for CMS admin pages. Defaults to "/[locale]/cms" (or "/cms" if no locale routing).
|
|
11
|
+
*/
|
|
12
|
+
routePrefix?: string | undefined
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Optional outer layout to wrap the CMS dashboard in (e.g. "./src/layouts/AppLayout.astro"). When
|
|
16
|
+
* omitted, renders as a dedicated standalone full-viewport dashboard.
|
|
17
|
+
*/
|
|
18
|
+
parentLayout?: string | undefined
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Path to the app database instance module exporting `db`. Defaults to "./src/db/index.ts" or
|
|
22
|
+
* "./src/db".
|
|
23
|
+
*/
|
|
24
|
+
db?: string | undefined
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Storage backend configuration (e.g. r2({ binding: "BLOB", publicUrl:
|
|
28
|
+
* "https://media.example.com" })).
|
|
29
|
+
*/
|
|
30
|
+
storage?: StorageConfig | undefined
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pluggable Auth Adapter instance or module path (RFC-0004 / RFC-0006). E.g. cfAccessAuth({ ...
|
|
34
|
+
* }), auth0Auth({ ... }), or mockAuth().
|
|
35
|
+
*/
|
|
36
|
+
auth?: AuthAdapter | string | undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function rimelightCms(options: RimelightCmsOptions = {}): AstroIntegration {
|
|
40
|
+
const routePrefix = options.routePrefix ?? "/[locale]/cms"
|
|
41
|
+
const storageConfig = {
|
|
42
|
+
provider: options.storage?.provider ?? "r2",
|
|
43
|
+
binding: options.storage?.binding ?? "BLOB",
|
|
44
|
+
publicUrl: options.storage?.publicUrl,
|
|
45
|
+
prefix: options.storage?.prefix ?? "media/"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
name: "@rimelight/cms",
|
|
50
|
+
hooks: {
|
|
51
|
+
"astro:config:setup": ({ injectRoute, updateConfig, config }) => {
|
|
52
|
+
const rootDir = fileURLToPath(config.root)
|
|
53
|
+
|
|
54
|
+
// Resolve parent layout if provided
|
|
55
|
+
let resolvedParentLayout: string | null = null
|
|
56
|
+
if (options.parentLayout) {
|
|
57
|
+
resolvedParentLayout = path.isAbsolute(options.parentLayout)
|
|
58
|
+
? options.parentLayout
|
|
59
|
+
: path.resolve(rootDir, options.parentLayout)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Resolve db module if exists
|
|
63
|
+
let resolvedDb: string | null = null
|
|
64
|
+
if (options.db) {
|
|
65
|
+
resolvedDb = path.isAbsolute(options.db) ? options.db : path.resolve(rootDir, options.db)
|
|
66
|
+
} else {
|
|
67
|
+
const defaultDbTs = path.resolve(rootDir, "src/db/index.ts")
|
|
68
|
+
const defaultDbJs = path.resolve(rootDir, "src/db/index.js")
|
|
69
|
+
if (fs.existsSync(defaultDbTs)) {
|
|
70
|
+
resolvedDb = defaultDbTs
|
|
71
|
+
} else if (fs.existsSync(defaultDbJs)) {
|
|
72
|
+
resolvedDb = defaultDbJs
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Resolve auth module / adapter code
|
|
77
|
+
let resolvedAuthCode: string
|
|
78
|
+
if (typeof options.auth === "string") {
|
|
79
|
+
const resolvedAuthPath = path.isAbsolute(options.auth)
|
|
80
|
+
? options.auth
|
|
81
|
+
: path.resolve(rootDir, options.auth)
|
|
82
|
+
resolvedAuthCode = `export { authAdapter, authAdapter as default } from ${JSON.stringify(resolvedAuthPath)};`
|
|
83
|
+
} else if (options.auth && typeof options.auth === "object") {
|
|
84
|
+
const authObj = options.auth as any
|
|
85
|
+
const adapterName = authObj.name || "mock"
|
|
86
|
+
const adapterOpts = authObj.options || {}
|
|
87
|
+
|
|
88
|
+
if (adapterName === "cf-access") {
|
|
89
|
+
resolvedAuthCode = `import { cfAccessAuth } from "@rimelight/auth/cf-access";
|
|
90
|
+
export const authAdapter = cfAccessAuth(${JSON.stringify(adapterOpts)});
|
|
91
|
+
export default authAdapter;`
|
|
92
|
+
} else if (adapterName === "auth0") {
|
|
93
|
+
resolvedAuthCode = `import { auth0Auth } from "@rimelight/auth/auth0";
|
|
94
|
+
export const authAdapter = auth0Auth(${JSON.stringify(adapterOpts)});
|
|
95
|
+
export default authAdapter;`
|
|
96
|
+
} else {
|
|
97
|
+
resolvedAuthCode = `import { mockAuth } from "@rimelight/auth/mock";
|
|
98
|
+
export const authAdapter = mockAuth(${JSON.stringify(adapterOpts)});
|
|
99
|
+
export default authAdapter;`
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// Default fallback mock admin adapter for development
|
|
103
|
+
resolvedAuthCode = `import { mockAuth } from "@rimelight/auth/mock";
|
|
104
|
+
export const authAdapter = mockAuth({
|
|
105
|
+
session: {
|
|
106
|
+
userId: "cms-admin",
|
|
107
|
+
name: "CMS Administrator",
|
|
108
|
+
email: "admin@rimelight.com",
|
|
109
|
+
roles: ["admin", "editor"],
|
|
110
|
+
permissions: ["*"],
|
|
111
|
+
userType: "employee"
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
export default authAdapter;`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 1. Inject CMS admin routes
|
|
118
|
+
injectRoute({
|
|
119
|
+
pattern: `${routePrefix}`,
|
|
120
|
+
entrypoint: "@rimelight/cms/admin/pages/dashboard.astro"
|
|
121
|
+
})
|
|
122
|
+
injectRoute({
|
|
123
|
+
pattern: `${routePrefix}/pages`,
|
|
124
|
+
entrypoint: "@rimelight/cms/admin/pages/pages-index.astro"
|
|
125
|
+
})
|
|
126
|
+
injectRoute({
|
|
127
|
+
pattern: `${routePrefix}/pages/new`,
|
|
128
|
+
entrypoint: "@rimelight/cms/admin/pages/pages-new.astro"
|
|
129
|
+
})
|
|
130
|
+
injectRoute({
|
|
131
|
+
pattern: `${routePrefix}/pages/[id]/edit`,
|
|
132
|
+
entrypoint: "@rimelight/cms/admin/pages/pages-edit.astro"
|
|
133
|
+
})
|
|
134
|
+
injectRoute({
|
|
135
|
+
pattern: `${routePrefix}/pages/[id]/preview`,
|
|
136
|
+
entrypoint: "@rimelight/cms/admin/pages/pages-preview.astro"
|
|
137
|
+
})
|
|
138
|
+
injectRoute({
|
|
139
|
+
pattern: `${routePrefix}/pages/[id]/review`,
|
|
140
|
+
entrypoint: "@rimelight/cms/admin/pages/pages-review.astro"
|
|
141
|
+
})
|
|
142
|
+
injectRoute({
|
|
143
|
+
pattern: `${routePrefix}/templates`,
|
|
144
|
+
entrypoint: "@rimelight/cms/admin/pages/templates-index.astro"
|
|
145
|
+
})
|
|
146
|
+
injectRoute({
|
|
147
|
+
pattern: `${routePrefix}/templates/new`,
|
|
148
|
+
entrypoint: "@rimelight/cms/admin/pages/templates-new.astro"
|
|
149
|
+
})
|
|
150
|
+
injectRoute({
|
|
151
|
+
pattern: `${routePrefix}/templates/[id]/edit`,
|
|
152
|
+
entrypoint: "@rimelight/cms/admin/pages/templates-edit.astro"
|
|
153
|
+
})
|
|
154
|
+
injectRoute({
|
|
155
|
+
pattern: `${routePrefix}/assets`,
|
|
156
|
+
entrypoint: "@rimelight/cms/admin/pages/assets.astro"
|
|
157
|
+
})
|
|
158
|
+
injectRoute({
|
|
159
|
+
pattern: `${routePrefix}/categories`,
|
|
160
|
+
entrypoint: "@rimelight/cms/admin/pages/categories.astro"
|
|
161
|
+
})
|
|
162
|
+
injectRoute({
|
|
163
|
+
pattern: `${routePrefix}/tags`,
|
|
164
|
+
entrypoint: "@rimelight/cms/admin/pages/tags.astro"
|
|
165
|
+
})
|
|
166
|
+
injectRoute({
|
|
167
|
+
pattern: `${routePrefix}/bylines`,
|
|
168
|
+
entrypoint: "@rimelight/cms/admin/pages/bylines.astro"
|
|
169
|
+
})
|
|
170
|
+
injectRoute({
|
|
171
|
+
pattern: `${routePrefix}/versions`,
|
|
172
|
+
entrypoint: "@rimelight/cms/admin/pages/versions.astro"
|
|
173
|
+
})
|
|
174
|
+
injectRoute({
|
|
175
|
+
pattern: `${routePrefix}/content-types`,
|
|
176
|
+
entrypoint: "@rimelight/cms/admin/pages/content-types.astro"
|
|
177
|
+
})
|
|
178
|
+
injectRoute({
|
|
179
|
+
pattern: `${routePrefix}/byline-schema`,
|
|
180
|
+
entrypoint: "@rimelight/cms/admin/pages/byline-schema.astro"
|
|
181
|
+
})
|
|
182
|
+
injectRoute({
|
|
183
|
+
pattern: `${routePrefix}/users`,
|
|
184
|
+
entrypoint: "@rimelight/cms/admin/pages/users.astro"
|
|
185
|
+
})
|
|
186
|
+
injectRoute({
|
|
187
|
+
pattern: `${routePrefix}/settings`,
|
|
188
|
+
entrypoint: "@rimelight/cms/admin/pages/settings.astro"
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// Media API Routes
|
|
192
|
+
injectRoute({
|
|
193
|
+
pattern: `${routePrefix}/api/media`,
|
|
194
|
+
entrypoint: "@rimelight/cms/admin/api/media.ts"
|
|
195
|
+
})
|
|
196
|
+
injectRoute({
|
|
197
|
+
pattern: `${routePrefix}/api/media/file/[...key]`,
|
|
198
|
+
entrypoint: "@rimelight/cms/admin/api/media-file.ts"
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// 2. Vite plugin for virtual modules
|
|
202
|
+
updateConfig({
|
|
203
|
+
vite: {
|
|
204
|
+
plugins: [
|
|
205
|
+
{
|
|
206
|
+
name: "vite-plugin-rimelight-cms-virtuals",
|
|
207
|
+
resolveId(id) {
|
|
208
|
+
if (id === "virtual:rimelight-cms/parent-layout") {
|
|
209
|
+
return "\0virtual:rimelight-cms/parent-layout"
|
|
210
|
+
}
|
|
211
|
+
if (id === "virtual:rimelight-cms/db") {
|
|
212
|
+
return "\0virtual:rimelight-cms/db"
|
|
213
|
+
}
|
|
214
|
+
if (id === "virtual:rimelight-cms/storage") {
|
|
215
|
+
return "\0virtual:rimelight-cms/storage"
|
|
216
|
+
}
|
|
217
|
+
if (id === "virtual:rimelight-cms/auth") {
|
|
218
|
+
return "\0virtual:rimelight-cms/auth"
|
|
219
|
+
}
|
|
220
|
+
return undefined
|
|
221
|
+
},
|
|
222
|
+
load(id) {
|
|
223
|
+
if (id === "\0virtual:rimelight-cms/parent-layout") {
|
|
224
|
+
if (resolvedParentLayout && fs.existsSync(resolvedParentLayout)) {
|
|
225
|
+
return `export { default } from ${JSON.stringify(resolvedParentLayout)};`
|
|
226
|
+
}
|
|
227
|
+
return `export { default } from "@rimelight/cms/admin/layouts/DefaultParentLayout.astro";`
|
|
228
|
+
}
|
|
229
|
+
if (id === "\0virtual:rimelight-cms/db") {
|
|
230
|
+
if (resolvedDb && fs.existsSync(resolvedDb)) {
|
|
231
|
+
return `export * from ${JSON.stringify(resolvedDb)};`
|
|
232
|
+
}
|
|
233
|
+
return `export const db = null;`
|
|
234
|
+
}
|
|
235
|
+
if (id === "\0virtual:rimelight-cms/storage") {
|
|
236
|
+
return `export const storageConfig = ${JSON.stringify(storageConfig)};`
|
|
237
|
+
}
|
|
238
|
+
if (id === "\0virtual:rimelight-cms/auth") {
|
|
239
|
+
return resolvedAuthCode
|
|
240
|
+
}
|
|
241
|
+
return undefined
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|