@rimelight/cms 0.0.2 → 0.0.3
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/package.json +16 -14
- package/src/admin/api/media-file.ts +39 -0
- package/src/admin/api/media.ts +199 -0
- package/src/admin/layouts/CMSDashboardLayout.astro +178 -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 +9 -2
- 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 +13 -0
- package/src/index.ts +40 -8
- package/src/integration.ts +196 -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/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 +253 -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
package/src/migration-tools.ts
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import type { BaseBlock, ComponentShowcaseBlock } from "./core/types/blocks.ts"
|
|
2
|
-
|
|
3
|
-
export interface ComponentReference {
|
|
4
|
-
componentName: string
|
|
5
|
-
componentPath: string
|
|
6
|
-
framework: "astro" | "vue" | "solid"
|
|
7
|
-
propsConfig: Record<string, string[]> | undefined
|
|
8
|
-
defaultProps: Record<string, any> | undefined
|
|
9
|
-
togglingPattern: "icon" | "select-option" | "tab-segmented" | "pill-toggle" | undefined
|
|
10
|
-
codeExamples: {
|
|
11
|
-
astro?: string
|
|
12
|
-
vue?: string
|
|
13
|
-
solid?: string
|
|
14
|
-
} | undefined
|
|
15
|
-
playgroundUrl: string | undefined
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface MigrationReport {
|
|
19
|
-
totalFiles: number
|
|
20
|
-
migratedFiles: number
|
|
21
|
-
totalComponentShowcases: number
|
|
22
|
-
migratedComponentShowcases: number
|
|
23
|
-
errors: string[]
|
|
24
|
-
warnings: string[]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface ValidationResult {
|
|
28
|
-
valid: boolean
|
|
29
|
-
errors: string[]
|
|
30
|
-
warnings: string[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class MigrationTools {
|
|
34
|
-
convertMDXToBlocks(mdxContent: string): BaseBlock[] {
|
|
35
|
-
const blocks: BaseBlock[] = []
|
|
36
|
-
const lines = mdxContent.split("\n")
|
|
37
|
-
let i = 0
|
|
38
|
-
|
|
39
|
-
while (i < lines.length) {
|
|
40
|
-
const line = lines[i] || ""
|
|
41
|
-
const trimmed = line.trim()
|
|
42
|
-
|
|
43
|
-
if (!trimmed) {
|
|
44
|
-
i++
|
|
45
|
-
continue
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (trimmed.startsWith("```")) {
|
|
49
|
-
const lang = trimmed.slice(3).trim()
|
|
50
|
-
const codeLines: string[] = []
|
|
51
|
-
i++
|
|
52
|
-
while (i < lines.length && !(lines[i] || "").trim().startsWith("```")) {
|
|
53
|
-
codeLines.push(lines[i] || "")
|
|
54
|
-
i++
|
|
55
|
-
}
|
|
56
|
-
i++
|
|
57
|
-
blocks.push({
|
|
58
|
-
id: crypto.randomUUID(),
|
|
59
|
-
type: "CodeBlock",
|
|
60
|
-
props: {
|
|
61
|
-
language: lang || "text",
|
|
62
|
-
code: codeLines.join("\n"),
|
|
63
|
-
caption: ""
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
continue
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (trimmed.startsWith("#")) {
|
|
70
|
-
const match = line.match(/^(#{1,6})\s(.*)$/)
|
|
71
|
-
if (match) {
|
|
72
|
-
const level = Math.min(6, Math.max(2, match[1]!.length)) as 2 | 3 | 4 | 5 | 6
|
|
73
|
-
blocks.push({
|
|
74
|
-
id: crypto.randomUUID(),
|
|
75
|
-
type: "SectionBlock",
|
|
76
|
-
props: {
|
|
77
|
-
title: match[2] || "",
|
|
78
|
-
level,
|
|
79
|
-
description: "",
|
|
80
|
-
children: []
|
|
81
|
-
},
|
|
82
|
-
children: []
|
|
83
|
-
})
|
|
84
|
-
i++
|
|
85
|
-
continue
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (trimmed.startsWith(">")) {
|
|
90
|
-
const calloutLines: string[] = []
|
|
91
|
-
while (i < lines.length && (lines[i] || "").trim().startsWith(">")) {
|
|
92
|
-
calloutLines.push((lines[i] || "").trim().replace(/^>\s?/, ""))
|
|
93
|
-
i++
|
|
94
|
-
}
|
|
95
|
-
const firstLine = calloutLines[0] || ""
|
|
96
|
-
const variantMatch = firstLine.match(/^\[!([A-Z]+)\]/i)
|
|
97
|
-
const variant = variantMatch ? (variantMatch[1] || "info").toLowerCase() : "info"
|
|
98
|
-
|
|
99
|
-
blocks.push({
|
|
100
|
-
id: crypto.randomUUID(),
|
|
101
|
-
type: "CalloutBlock",
|
|
102
|
-
props: {
|
|
103
|
-
variant: variant as "info" | "success" | "warning" | "error" | "commentary" | "ideation" | "source",
|
|
104
|
-
children: []
|
|
105
|
-
},
|
|
106
|
-
children: []
|
|
107
|
-
})
|
|
108
|
-
continue
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (trimmed.startsWith("|") && trimmed.endsWith("|")) {
|
|
112
|
-
const tableLines: string[] = []
|
|
113
|
-
while (i < lines.length && (lines[i] || "").trim().startsWith("|") && (lines[i] || "").trim().endsWith("|")) {
|
|
114
|
-
tableLines.push(lines[i] || "")
|
|
115
|
-
i++
|
|
116
|
-
}
|
|
117
|
-
const tableBlock = this.parseMarkdownTable(tableLines.join("\n"))
|
|
118
|
-
if (tableBlock) {
|
|
119
|
-
blocks.push(tableBlock)
|
|
120
|
-
continue
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (trimmed.startsWith("!")) {
|
|
125
|
-
const imageMatch = trimmed.match(/^!\[([^\]]*)\]\(([^)]+)\)$/)
|
|
126
|
-
if (imageMatch) {
|
|
127
|
-
blocks.push({
|
|
128
|
-
id: crypto.randomUUID(),
|
|
129
|
-
type: "ImageBlock",
|
|
130
|
-
props: {
|
|
131
|
-
alt: imageMatch[1] || "",
|
|
132
|
-
src: imageMatch[2] || "",
|
|
133
|
-
caption: ""
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
i++
|
|
137
|
-
continue
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const paragraphLines: string[] = []
|
|
142
|
-
while (
|
|
143
|
-
i < lines.length &&
|
|
144
|
-
(lines[i] || "").trim() &&
|
|
145
|
-
!(lines[i] || "").trim().startsWith("```") &&
|
|
146
|
-
!(lines[i] || "").trim().startsWith("#") &&
|
|
147
|
-
!(lines[i] || "").trim().startsWith(">") &&
|
|
148
|
-
!((lines[i] || "").trim().startsWith("|") && (lines[i] || "").trim().endsWith("|"))
|
|
149
|
-
) {
|
|
150
|
-
paragraphLines.push(lines[i] || "")
|
|
151
|
-
i++
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (paragraphLines.length > 0) {
|
|
155
|
-
blocks.push({
|
|
156
|
-
id: crypto.randomUUID(),
|
|
157
|
-
type: "ParagraphBlock",
|
|
158
|
-
props: {
|
|
159
|
-
text: { en: paragraphLines.join(" ").trim() }
|
|
160
|
-
}
|
|
161
|
-
})
|
|
162
|
-
} else {
|
|
163
|
-
i++
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return blocks
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
extractComponentReferences(mdxContent: string): ComponentReference[] {
|
|
171
|
-
const references: ComponentReference[] = []
|
|
172
|
-
const componentRegex = /<ComponentShowcase[^>]*>/g
|
|
173
|
-
let match: RegExpExecArray | null
|
|
174
|
-
|
|
175
|
-
while ((match = componentRegex.exec(mdxContent)) !== null) {
|
|
176
|
-
const tag = match[0]
|
|
177
|
-
const componentNameMatch = tag.match(/componentName=["']([^"']+)["']/)
|
|
178
|
-
const componentPathMatch = tag.match(/componentPath=["']([^"']+)["']/)
|
|
179
|
-
const frameworkMatch = tag.match(/framework=["']([^"']+)["']/)
|
|
180
|
-
const propsConfigMatch = tag.match(/propsConfig=\{\{([^}]+)\}\}/)
|
|
181
|
-
const defaultPropsMatch = tag.match(/defaultProps=\{\{([^}]+)\}\}/)
|
|
182
|
-
const togglingPatternMatch = tag.match(/togglingPattern=["']([^"']+)["']/)
|
|
183
|
-
|
|
184
|
-
references.push({
|
|
185
|
-
componentName: componentNameMatch?.[1] || "unknown",
|
|
186
|
-
componentPath: componentPathMatch?.[1] || "",
|
|
187
|
-
framework: (frameworkMatch?.[1] as "astro" | "vue" | "solid") || "astro",
|
|
188
|
-
propsConfig: propsConfigMatch?.[1] ? this.parseSimpleObject(propsConfigMatch[1]) : undefined,
|
|
189
|
-
defaultProps: defaultPropsMatch?.[1] ? this.parseSimpleObject(defaultPropsMatch[1]) : undefined,
|
|
190
|
-
togglingPattern: togglingPatternMatch?.[1] as "icon" | "select-option" | "tab-segmented" | "pill-toggle" || "tab-segmented",
|
|
191
|
-
codeExamples: undefined,
|
|
192
|
-
playgroundUrl: undefined
|
|
193
|
-
})
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return references
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
validateMigratedContent(blocks: BaseBlock[]): ValidationResult {
|
|
200
|
-
const errors: string[] = []
|
|
201
|
-
const warnings: string[] = []
|
|
202
|
-
|
|
203
|
-
const seenIds = new Set<string>()
|
|
204
|
-
const blockCounts: Record<string, number> = {}
|
|
205
|
-
|
|
206
|
-
function checkBlocks(nodes: BaseBlock[]) {
|
|
207
|
-
for (const b of nodes) {
|
|
208
|
-
if (!b.id || typeof b.id !== "string") {
|
|
209
|
-
errors.push(`Block of type '${b.type}' missing id`)
|
|
210
|
-
} else if (seenIds.has(b.id)) {
|
|
211
|
-
warnings.push(`Duplicate block id: ${b.id}`)
|
|
212
|
-
} else {
|
|
213
|
-
seenIds.add(b.id)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!b.type || typeof b.type !== "string") {
|
|
217
|
-
errors.push("Block missing valid 'type' field")
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
blockCounts[b.type] = (blockCounts[b.type] || 0) + 1
|
|
221
|
-
|
|
222
|
-
if (b.children && Array.isArray(b.children)) {
|
|
223
|
-
checkBlocks(b.children)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
checkBlocks(blocks)
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
valid: errors.length === 0,
|
|
232
|
-
errors,
|
|
233
|
-
warnings
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
generateMigrationReport(original: string, migrated: BaseBlock[]): MigrationReport {
|
|
238
|
-
const componentRefs = this.extractComponentReferences(original)
|
|
239
|
-
const validation = this.validateMigratedContent(migrated)
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
totalFiles: 1,
|
|
243
|
-
migratedFiles: validation.valid ? 1 : 0,
|
|
244
|
-
totalComponentShowcases: componentRefs.length,
|
|
245
|
-
migratedComponentShowcases: componentRefs.length,
|
|
246
|
-
errors: validation.errors,
|
|
247
|
-
warnings: validation.warnings
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
convertComponentShowcaseToBlock(ref: ComponentReference): ComponentShowcaseBlock {
|
|
252
|
-
return {
|
|
253
|
-
id: crypto.randomUUID(),
|
|
254
|
-
type: "ComponentShowcaseBlock",
|
|
255
|
-
props: {
|
|
256
|
-
componentName: ref.componentName,
|
|
257
|
-
componentPath: ref.componentPath,
|
|
258
|
-
framework: ref.framework,
|
|
259
|
-
defaultProps: ref.defaultProps || {},
|
|
260
|
-
propsConfig: ref.propsConfig || {},
|
|
261
|
-
togglingPattern: ref.togglingPattern || "tab-segmented",
|
|
262
|
-
codeExamples: ref.codeExamples || {},
|
|
263
|
-
playgroundUrl: ref.playgroundUrl
|
|
264
|
-
},
|
|
265
|
-
children: []
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private parseSimpleObject(str: string): Record<string, any> {
|
|
270
|
-
try {
|
|
271
|
-
return new Function(`return ${str}`)()
|
|
272
|
-
} catch {
|
|
273
|
-
return {}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
private parseMarkdownTable(tableText: string): BaseBlock | null {
|
|
278
|
-
const lines = tableText
|
|
279
|
-
.trim()
|
|
280
|
-
.split("\n")
|
|
281
|
-
.map((l) => l.trim())
|
|
282
|
-
.filter(Boolean)
|
|
283
|
-
if (lines.length < 2) return null
|
|
284
|
-
|
|
285
|
-
const parseRow = (line: string) =>
|
|
286
|
-
line
|
|
287
|
-
.replace(/^\|/, "")
|
|
288
|
-
.replace(/\|$/, "")
|
|
289
|
-
.split("|")
|
|
290
|
-
.map((c) => c.trim())
|
|
291
|
-
|
|
292
|
-
const headerCells = parseRow(lines[0] || "")
|
|
293
|
-
const alignCells = parseRow(lines[1] || "")
|
|
294
|
-
|
|
295
|
-
if (!alignCells.every((c) => /^[:-]+$/.test(c))) {
|
|
296
|
-
return null
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const columns = headerCells.map((header, idx) => {
|
|
300
|
-
const alignStr = alignCells[idx] || ""
|
|
301
|
-
let align: "left" | "center" | "right" = "left"
|
|
302
|
-
if (alignStr.startsWith(":") && alignStr.endsWith(":")) {
|
|
303
|
-
align = "center"
|
|
304
|
-
} else if (alignStr.endsWith(":")) {
|
|
305
|
-
align = "right"
|
|
306
|
-
}
|
|
307
|
-
return {
|
|
308
|
-
key: `col_${idx}`,
|
|
309
|
-
header,
|
|
310
|
-
align
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
const rows: Record<string, string>[] = []
|
|
315
|
-
for (let i = 2; i < lines.length; i++) {
|
|
316
|
-
const cells = parseRow(lines[i] || "")
|
|
317
|
-
const rowObj: Record<string, string> = {}
|
|
318
|
-
columns.forEach((col, idx) => {
|
|
319
|
-
rowObj[col.key] = cells[idx] || ""
|
|
320
|
-
})
|
|
321
|
-
rows.push(rowObj)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
id: crypto.randomUUID(),
|
|
326
|
-
type: "TableBlock",
|
|
327
|
-
props: {
|
|
328
|
-
columns,
|
|
329
|
-
rows,
|
|
330
|
-
caption: ""
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
package/src/migration.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { sql } from "drizzle-orm"
|
|
2
|
-
import { markdownToBlocks } from "./markdown/parser.ts"
|
|
3
|
-
import type { BaseBlock } from "./core/types/blocks"
|
|
4
|
-
|
|
5
|
-
export interface DocMigrationInput {
|
|
6
|
-
slug: string
|
|
7
|
-
title: string | Record<string, string>
|
|
8
|
-
description?: string | Record<string, string>
|
|
9
|
-
markdown: string
|
|
10
|
-
properties?: Record<string, any>
|
|
11
|
-
postedAt?: Date | null
|
|
12
|
-
authorIds?: string[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function parseMarkdownWithFrontmatter(raw: string): {
|
|
16
|
-
frontmatter: Record<string, any>
|
|
17
|
-
body: string
|
|
18
|
-
} {
|
|
19
|
-
const frontmatter: Record<string, any> = {}
|
|
20
|
-
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/)
|
|
21
|
-
|
|
22
|
-
if (!match) {
|
|
23
|
-
return { frontmatter, body: raw.trim() }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const yamlBlock = match[1] || ""
|
|
27
|
-
const body = (match[2] || "").trim()
|
|
28
|
-
|
|
29
|
-
const lines = yamlBlock.split(/\r?\n/)
|
|
30
|
-
for (const line of lines) {
|
|
31
|
-
const trimmed = line.trim()
|
|
32
|
-
if (!trimmed || trimmed.startsWith("#")) continue
|
|
33
|
-
const colonIndex = trimmed.indexOf(":")
|
|
34
|
-
if (colonIndex !== -1) {
|
|
35
|
-
const key = trimmed.slice(0, colonIndex).trim()
|
|
36
|
-
let value: any = trimmed.slice(colonIndex + 1).trim()
|
|
37
|
-
|
|
38
|
-
if (value === "true") value = true
|
|
39
|
-
else if (value === "false") value = false
|
|
40
|
-
else if (value === "null") value = null
|
|
41
|
-
else if (!isNaN(Number(value)) && value !== "") value = Number(value)
|
|
42
|
-
else if (
|
|
43
|
-
(value.startsWith('"') && value.endsWith('"')) ||
|
|
44
|
-
(value.startsWith("'") && value.endsWith("'"))
|
|
45
|
-
) {
|
|
46
|
-
value = value.slice(1, -1)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
frontmatter[key] = value
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return { frontmatter, body }
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function convertMarkdownDocToPageRecord(input: DocMigrationInput) {
|
|
57
|
-
const blocks: BaseBlock[] = markdownToBlocks(input.markdown)
|
|
58
|
-
const titleObj =
|
|
59
|
-
typeof input.title === "string" ? { en: input.title } : input.title || { en: input.slug }
|
|
60
|
-
const descObj =
|
|
61
|
-
typeof input.description === "string"
|
|
62
|
-
? { en: input.description }
|
|
63
|
-
: input.description || undefined
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
id: crypto.randomUUID(),
|
|
67
|
-
slug: input.slug,
|
|
68
|
-
type: "doc" as const,
|
|
69
|
-
title: titleObj,
|
|
70
|
-
description: descObj,
|
|
71
|
-
tags: [],
|
|
72
|
-
authorIds: input.authorIds || [],
|
|
73
|
-
postedAt: input.postedAt !== undefined ? input.postedAt : new Date(),
|
|
74
|
-
createdAt: new Date(),
|
|
75
|
-
updatedAt: new Date(),
|
|
76
|
-
content: {
|
|
77
|
-
blocks,
|
|
78
|
-
properties: input.properties || {}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export async function migrateLegacyPagesToCMSv2(db: any) {
|
|
84
|
-
console.log("[Migration] Applying DDL schema updates...")
|
|
85
|
-
|
|
86
|
-
await db.execute(sql`
|
|
87
|
-
ALTER TABLE pages ADD COLUMN IF NOT EXISTS template_id UUID;
|
|
88
|
-
ALTER TABLE pages ADD COLUMN IF NOT EXISTS template_version INTEGER DEFAULT 1 NOT NULL;
|
|
89
|
-
ALTER TABLE page_versions ADD COLUMN IF NOT EXISTS version_number INTEGER DEFAULT 1 NOT NULL;
|
|
90
|
-
|
|
91
|
-
CREATE UNIQUE INDEX IF NOT EXISTS page_versions_page_id_version_idx
|
|
92
|
-
ON page_versions (page_id, version_number);
|
|
93
|
-
`)
|
|
94
|
-
|
|
95
|
-
console.log("[Migration] Transforming legacy page content in chunked batches...")
|
|
96
|
-
|
|
97
|
-
const BATCH_SIZE = 100
|
|
98
|
-
let offset = 0
|
|
99
|
-
|
|
100
|
-
async function fetchBatch(currentOffset: number) {
|
|
101
|
-
const batch = await db.execute(sql`
|
|
102
|
-
SELECT id, content FROM pages ORDER BY id LIMIT ${BATCH_SIZE} OFFSET ${currentOffset};
|
|
103
|
-
`)
|
|
104
|
-
|
|
105
|
-
if (!batch || !batch.rows || batch.rows.length === 0) {
|
|
106
|
-
return null
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const rows: Array<{ id: string; content: unknown }> = batch.rows
|
|
110
|
-
return rows
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function processBatch(pagesList: Array<{ id: string; content: unknown }>) {
|
|
114
|
-
const updatePromises = pagesList.map((page) => {
|
|
115
|
-
const transformed = transformLegacyContent(page.content)
|
|
116
|
-
return db.execute(sql`
|
|
117
|
-
UPDATE pages SET content = ${JSON.stringify(transformed)}::jsonb WHERE id = ${page.id}::uuid;
|
|
118
|
-
`)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
await Promise.all(updatePromises)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async function processAllBatches(currentOffset: number): Promise<void> {
|
|
125
|
-
const pagesList = await fetchBatch(currentOffset)
|
|
126
|
-
if (!pagesList) return
|
|
127
|
-
await processBatch(pagesList)
|
|
128
|
-
console.log(`[Migration] Migrated ${currentOffset + BATCH_SIZE} records...`)
|
|
129
|
-
return processAllBatches(currentOffset + BATCH_SIZE)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
await processAllBatches(offset)
|
|
133
|
-
|
|
134
|
-
console.log("[Migration] Migration completed successfully.")
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function transformLegacyContent(raw: unknown) {
|
|
138
|
-
if (typeof raw === "object" && raw !== null && "blocks" in raw) return raw
|
|
139
|
-
let textContent = ""
|
|
140
|
-
try {
|
|
141
|
-
textContent = typeof raw === "string" ? raw : JSON.stringify(raw ?? "")
|
|
142
|
-
} catch {
|
|
143
|
-
textContent = ""
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
blocks: [
|
|
147
|
-
{
|
|
148
|
-
id: crypto.randomUUID(),
|
|
149
|
-
type: "ParagraphBlock",
|
|
150
|
-
props: { text: { en: textContent } }
|
|
151
|
-
}
|
|
152
|
-
],
|
|
153
|
-
properties: {}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { pgTable, text, uuid, uniqueIndex } from "drizzle-orm/pg-core"
|
|
2
|
-
|
|
3
|
-
export const componentDependencies = pgTable(
|
|
4
|
-
"component_dependencies",
|
|
5
|
-
{
|
|
6
|
-
id: uuid("id").defaultRandom().notNull().primaryKey(),
|
|
7
|
-
sourceComponent: text("source_component").notNull(),
|
|
8
|
-
targetComponent: text("target_component").notNull(),
|
|
9
|
-
dependencyType: text("dependency_type").notNull()
|
|
10
|
-
},
|
|
11
|
-
(table) => [
|
|
12
|
-
uniqueIndex("component_dependencies_unique_idx").on(
|
|
13
|
-
table.sourceComponent,
|
|
14
|
-
table.targetComponent,
|
|
15
|
-
table.dependencyType
|
|
16
|
-
)
|
|
17
|
-
]
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
export type ComponentDependency = typeof componentDependencies.$inferSelect
|
|
21
|
-
export type NewComponentDependency = typeof componentDependencies.$inferInsert
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
pgTable,
|
|
3
|
-
text,
|
|
4
|
-
timestamp,
|
|
5
|
-
uuid,
|
|
6
|
-
uniqueIndex,
|
|
7
|
-
jsonb
|
|
8
|
-
} from "drizzle-orm/pg-core"
|
|
9
|
-
|
|
10
|
-
export const componentMetadata = pgTable(
|
|
11
|
-
"component_metadata",
|
|
12
|
-
{
|
|
13
|
-
id: uuid("id").defaultRandom().notNull().primaryKey(),
|
|
14
|
-
componentName: text("component_name").notNull(),
|
|
15
|
-
version: text("version").notNull(),
|
|
16
|
-
framework: text("framework").notNull(),
|
|
17
|
-
filePath: text("file_path").notNull(),
|
|
18
|
-
props: jsonb("props").$type<Record<string, any>>().notNull(),
|
|
19
|
-
slots: jsonb("slots").$type<Record<string, any>>().notNull(),
|
|
20
|
-
emits: jsonb("emits").$type<Record<string, any>>().notNull(),
|
|
21
|
-
theme: jsonb("theme").$type<Record<string, any>>(),
|
|
22
|
-
extractedAt: timestamp("extracted_at", { withTimezone: true }).defaultNow().notNull(),
|
|
23
|
-
updatedAt: timestamp("updated_at", { withTimezone: true }).$onUpdate(() => new Date())
|
|
24
|
-
},
|
|
25
|
-
(table) => [
|
|
26
|
-
uniqueIndex("component_metadata_name_version_idx").on(table.componentName, table.version)
|
|
27
|
-
]
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
export type ComponentMetadata = typeof componentMetadata.$inferSelect
|
|
31
|
-
export type NewComponentMetadata = typeof componentMetadata.$inferInsert
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { pgTable, text, timestamp, jsonb, index } from "drizzle-orm/pg-core"
|
|
2
|
-
|
|
3
|
-
export const componentMetadataCache = pgTable(
|
|
4
|
-
"component_metadata_cache",
|
|
5
|
-
{
|
|
6
|
-
componentName: text("component_name").primaryKey(),
|
|
7
|
-
metadata: jsonb("metadata").$type<Record<string, any>>().notNull(),
|
|
8
|
-
cacheKey: text("cache_key").notNull(),
|
|
9
|
-
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull()
|
|
10
|
-
},
|
|
11
|
-
(table) => [index("component_metadata_cache_expires_idx").on(table.expiresAt)]
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
export type ComponentMetadataCache = typeof componentMetadataCache.$inferSelect
|
|
15
|
-
export type NewComponentMetadataCache = typeof componentMetadataCache.$inferInsert
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { pgTable, text, timestamp, uuid, uniqueIndex } from "drizzle-orm/pg-core"
|
|
2
|
-
|
|
3
|
-
export const componentVersions = pgTable(
|
|
4
|
-
"component_versions",
|
|
5
|
-
{
|
|
6
|
-
id: uuid("id").defaultRandom().notNull().primaryKey(),
|
|
7
|
-
componentName: text("component_name").notNull(),
|
|
8
|
-
version: text("version").notNull(),
|
|
9
|
-
filePath: text("file_path").notNull(),
|
|
10
|
-
gitCommit: text("git_commit"),
|
|
11
|
-
extractedAt: timestamp("extracted_at", { withTimezone: true }).defaultNow().notNull()
|
|
12
|
-
},
|
|
13
|
-
(table) => [
|
|
14
|
-
uniqueIndex("component_versions_name_version_idx").on(table.componentName, table.version)
|
|
15
|
-
]
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
export type ComponentVersion = typeof componentVersions.$inferSelect
|
|
19
|
-
export type NewComponentVersion = typeof componentVersions.$inferInsert
|
package/src/search/sync.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { BaseBlock } from "../core/types/blocks"
|
|
2
|
-
|
|
3
|
-
export function extractPlainTextFromBlocks(blocks: BaseBlock[], locale = "en"): string {
|
|
4
|
-
const parts: string[] = []
|
|
5
|
-
|
|
6
|
-
function walk(nodes: BaseBlock[]) {
|
|
7
|
-
for (const node of nodes) {
|
|
8
|
-
if (node.props) {
|
|
9
|
-
if (typeof node.props.text === "object" && node.props.text !== null) {
|
|
10
|
-
const locText = node.props.text[locale] || node.props.text.en
|
|
11
|
-
if (locText) parts.push(String(locText))
|
|
12
|
-
} else if (typeof node.props.text === "string") {
|
|
13
|
-
parts.push(node.props.text)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (typeof node.props.title === "object" && node.props.title !== null) {
|
|
17
|
-
const locTitle = node.props.title[locale] || node.props.title.en
|
|
18
|
-
if (locTitle) parts.push(String(locTitle))
|
|
19
|
-
} else if (typeof node.props.title === "string") {
|
|
20
|
-
parts.push(node.props.title)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (node.children?.length) {
|
|
25
|
-
walk(node.children)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
walk(blocks)
|
|
31
|
-
return parts.join(" ")
|
|
32
|
-
}
|