@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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bylines-Csggkl-g.d.mts +405 -0
  3. package/dist/index.d.mts +724 -0
  4. package/dist/index.mjs +2382 -0
  5. package/dist/integration.d.mts +33 -0
  6. package/dist/integration.mjs +161 -0
  7. package/dist/schema/index.d.mts +2063 -0
  8. package/dist/schema/index.mjs +176 -0
  9. package/dist/schema/sqlite.d.mts +2132 -0
  10. package/dist/schema/sqlite.mjs +137 -0
  11. package/dist/storage/index.d.mts +59 -0
  12. package/dist/storage/index.mjs +133 -0
  13. package/package.json +36 -18
  14. package/src/admin/api/media-file.ts +39 -0
  15. package/src/admin/api/media.ts +224 -0
  16. package/src/admin/layouts/CMSDashboardLayout.astro +191 -0
  17. package/src/admin/layouts/DefaultParentLayout.astro +50 -0
  18. package/src/admin/pages/assets.astro +424 -0
  19. package/src/admin/pages/byline-schema.astro +62 -0
  20. package/src/admin/pages/bylines.astro +16 -0
  21. package/src/admin/pages/categories.astro +21 -0
  22. package/src/admin/pages/content-types.astro +55 -0
  23. package/src/admin/pages/dashboard.astro +61 -0
  24. package/src/admin/pages/pages-edit.astro +595 -0
  25. package/src/admin/pages/pages-index.astro +146 -0
  26. package/src/admin/pages/pages-new.astro +130 -0
  27. package/src/admin/pages/pages-preview.astro +111 -0
  28. package/src/admin/pages/pages-review.astro +236 -0
  29. package/src/admin/pages/settings.astro +203 -0
  30. package/src/admin/pages/tags.astro +21 -0
  31. package/src/admin/pages/templates-edit.astro +234 -0
  32. package/src/admin/pages/templates-index.astro +95 -0
  33. package/src/admin/pages/templates-new.astro +193 -0
  34. package/src/admin/pages/users.astro +74 -0
  35. package/src/admin/pages/versions.astro +74 -0
  36. package/src/astro/BlockRenderer.astro +44 -102
  37. package/src/astro/PageRenderer.astro +12 -5
  38. package/src/astro/blocks/CalloutBlock.astro +7 -7
  39. package/src/astro/blocks/CardBlock.astro +5 -5
  40. package/src/astro/blocks/CodeBlock.astro +2 -2
  41. package/src/astro/blocks/FileTreeBlock.astro +7 -7
  42. package/src/astro/blocks/ImageBlock.astro +2 -2
  43. package/src/astro/blocks/OrderedListBlock.astro +2 -2
  44. package/src/astro/blocks/ParagraphBlock.astro +3 -3
  45. package/src/astro/blocks/SectionBlock.astro +6 -6
  46. package/src/astro/blocks/StepItemBlock.astro +3 -3
  47. package/src/astro/blocks/StepsBlock.astro +1 -1
  48. package/src/astro/blocks/TableBlock.astro +6 -6
  49. package/src/astro/blocks/TabsBlock.astro +9 -9
  50. package/src/astro/blocks/UnorderedListBlock.astro +2 -2
  51. package/src/astro/blocks/index.ts +35 -0
  52. package/src/client/components/RimelightBlock.tsx +19 -19
  53. package/src/client/components/RimelightBlockPicker.tsx +9 -13
  54. package/src/client/components/RimelightBylinesManager.tsx +438 -0
  55. package/src/client/components/RimelightEditor.tsx +3 -3
  56. package/src/client/components/RimelightPreview.tsx +37 -45
  57. package/src/client/components/RimelightPropertiesPanel.tsx +368 -26
  58. package/src/client/components/RimelightTaxonomyManager.tsx +369 -0
  59. package/src/client/components/RimelightTaxonomyMasterDetail.tsx +412 -0
  60. package/src/client/components/RimelightTemplateEditor.tsx +6 -8
  61. package/src/client/components/editors/CalloutEditor.tsx +2 -4
  62. package/src/client/components/editors/CardsEditor.tsx +2 -4
  63. package/src/client/components/editors/CodeEditor.tsx +1 -3
  64. package/src/client/components/editors/FileTreeEditor.tsx +2 -4
  65. package/src/client/components/editors/ImageEditor.tsx +3 -3
  66. package/src/client/components/editors/ParagraphEditor.tsx +2 -2
  67. package/src/client/components/editors/SceneEditor.tsx +6 -12
  68. package/src/client/components/editors/ScriptEditor.tsx +8 -16
  69. package/src/client/components/editors/SectionEditor.tsx +4 -4
  70. package/src/client/components/editors/StepItemEditor.tsx +2 -2
  71. package/src/client/components/editors/StepsEditor.tsx +2 -4
  72. package/src/client/components/editors/TabItemEditor.tsx +3 -3
  73. package/src/client/components/editors/TableEditor.tsx +5 -5
  74. package/src/client/components/editors/TabsEditor.tsx +2 -4
  75. package/src/client/components/index.ts +3 -0
  76. package/src/client/loader.ts +3 -0
  77. package/src/core/diff.ts +145 -0
  78. package/src/core/excerpt.ts +159 -0
  79. package/src/core/preview.ts +87 -0
  80. package/src/core/registry.ts +0 -86
  81. package/src/core/types/blocks.ts +6 -1
  82. package/src/core/types/bylines.ts +14 -0
  83. package/src/core/types/media.ts +13 -0
  84. package/src/core/types/taxonomies.ts +20 -0
  85. package/src/cron/scheduled.ts +57 -0
  86. package/src/docs/agent.ts +116 -0
  87. package/src/docs/config.ts +25 -0
  88. package/src/docs/index.ts +5 -0
  89. package/src/docs/routing.ts +104 -0
  90. package/src/docs/sidebar.ts +251 -0
  91. package/src/docs/toc.ts +47 -0
  92. package/src/env.d.ts +19 -0
  93. package/src/index.ts +57 -12
  94. package/src/integration.ts +250 -0
  95. package/src/loader/live.ts +254 -0
  96. package/src/markdown/index.ts +0 -1
  97. package/src/mcp/index.ts +228 -0
  98. package/src/schema/bylines.ts +23 -0
  99. package/src/schema/index.ts +3 -4
  100. package/src/schema/page_versions.ts +2 -0
  101. package/src/schema/pages.ts +2 -0
  102. package/src/schema/site_settings.ts +41 -0
  103. package/src/schema/sqlite.ts +344 -0
  104. package/src/schema/taxonomies.ts +49 -0
  105. package/src/services/bylines.ts +74 -0
  106. package/src/services/search-indexer.ts +61 -0
  107. package/src/services/site-settings.ts +108 -0
  108. package/src/services/taxonomies.ts +276 -0
  109. package/src/storage/index.ts +256 -0
  110. package/src/astro/blocks/ApiDocumentationBlock.astro +0 -350
  111. package/src/astro/blocks/DialogueBlock.astro +0 -23
  112. package/src/astro/blocks/LivePreviewBlock.astro +0 -27
  113. package/src/astro/blocks/SceneBlock.astro +0 -65
  114. package/src/astro/blocks/ScriptBlock.astro +0 -59
  115. package/src/core/template-sync.ts +0 -75
  116. package/src/markdown/parser.ts +0 -347
  117. package/src/migration-tools.ts +0 -334
  118. package/src/migration.ts +0 -155
  119. package/src/schema/component_dependencies.ts +0 -21
  120. package/src/schema/component_metadata.ts +0 -31
  121. package/src/schema/component_metadata_cache.ts +0 -15
  122. package/src/schema/component_versions.ts +0 -19
  123. package/src/search/sync.ts +0 -32
  124. 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
+ }
@@ -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 & Security Guards
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
- // Markdown Serialization & Parsing
41
- export * from "./markdown/index.ts"
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
+ }