@vibecuting/component-project-helper 0.1.15 → 0.1.17

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/src/index.ts CHANGED
@@ -1,26 +1,65 @@
1
+ export {
2
+ VIDEO_RESOURCE_METADATA_KEY,
3
+ BaseVideoResourceMetadataSchema,
4
+ ScenePluginMetadataSchema,
5
+ SceneSlotsSchema,
6
+ SceneSlotHintSchema,
7
+ ThemePluginMetadataSchema,
8
+ TransitionPluginMetadataSchema,
9
+ VideoResourceKindSchema,
10
+ VideoResourceMetadataSchema,
11
+ createVideoResourceAnnotation,
12
+ getVideoResourceMetadata,
13
+ type ScenePluginMetadata,
14
+ type SceneSlotHint,
15
+ type ThemePluginMetadata,
16
+ type TransitionPluginMetadata,
17
+ type VideoResourceAnnotation,
18
+ type VideoResourceDescriptor,
19
+ type VideoResourceKind,
20
+ type VideoResourceMetadata,
21
+ } from './resources/video-resource'
22
+ export {
23
+ sceneResourceDescriptor,
24
+ themeResourceDescriptor,
25
+ transitionResourceDescriptor,
26
+ videoResourceDescriptorDefinitions,
27
+ videoResourceDescriptors,
28
+ } from './resources/resource-descriptors'
29
+ export {
30
+ discoverComponentProjectComponents,
31
+ discoverVideoResources,
32
+ type DiscoveredVideoResource,
33
+ } from './discovery'
34
+ export {
35
+ generateVideoResourceMeta,
36
+ writeVideoResourceMeta,
37
+ } from './meta/generate-video-resource-meta'
38
+ export {
39
+ updateComponentMeta,
40
+ } from './meta/update-component-meta'
1
41
  export {
2
42
  VideoComponent,
3
43
  defineComponentProjectComponentMetadata,
44
+ defineScenePluginMetadata,
45
+ defineThemePluginMetadata,
46
+ defineTransitionPluginMetadata,
47
+ getScenePluginMetadata,
4
48
  getComponentProjectComponentMetadata,
49
+ getThemePluginMetadata,
50
+ getTransitionPluginMetadata,
51
+ ThemeComponent,
52
+ TransitionComponent,
5
53
  } from './decorators'
6
54
  export {
55
+ type ComponentProjectComponentMetadata,
7
56
  ComponentProjectComponentMetadataSchema,
8
57
  ComponentProjectGeneratedManifestSchema,
9
58
  } from './schemas'
10
- export {
11
- discoverComponentProjectComponents,
12
- type ComponentProjectComponentDiscovery,
13
- } from './discovery'
14
- export {
15
- renderComponentProjectMarkdown,
16
- type ComponentProjectMarkdownDocument,
17
- } from './markdown'
18
59
  export {
19
60
  resolveComponentProjectGeneratedDocsDir,
20
61
  resolveComponentProjectGeneratedManifestPath,
21
62
  resolveComponentProjectInstallRoot,
22
63
  } from './runtime'
23
- export { runComponentProjectPostinstall } from './lifecycle/postinstall'
24
- export { runComponentProjectPreuninstall } from './lifecycle/preuninstall'
25
64
 
26
65
  export const componentProjectHelperPackageName = '@vibecuting/component-project-helper' as const
@@ -0,0 +1,198 @@
1
+ import crypto from 'node:crypto'
2
+ import fs from 'node:fs/promises'
3
+ import path from 'node:path'
4
+
5
+ import type { DiscoveredVideoResource } from '../discovery/index.ts'
6
+
7
+ type ResourceEntry = {
8
+ packageName: string
9
+ packageVersion: string
10
+ }
11
+
12
+ type GeneratedResourceRecord = {
13
+ packageName: string
14
+ exportName: string
15
+ metadata: DiscoveredVideoResource['metadata']
16
+ }
17
+
18
+ type GeneratedModuleInput = {
19
+ projectRoot: string
20
+ resources: DiscoveredVideoResource[]
21
+ }
22
+
23
+ function groupResourcesByPackage(resources: DiscoveredVideoResource[]): Map<string, DiscoveredVideoResource[]> {
24
+ const result = new Map<string, DiscoveredVideoResource[]>()
25
+
26
+ for (const resource of resources) {
27
+ const items = result.get(resource.packageName) ?? []
28
+ items.push(resource)
29
+ result.set(resource.packageName, items)
30
+ }
31
+
32
+ for (const [packageName, items] of result.entries()) {
33
+ result.set(
34
+ packageName,
35
+ items.sort((left, right) => left.exportName.localeCompare(right.exportName)),
36
+ )
37
+ }
38
+
39
+ return result
40
+ }
41
+
42
+ function stableJson(value: unknown): string {
43
+ return JSON.stringify(
44
+ value,
45
+ (_key, nestedValue) => {
46
+ if (nestedValue && typeof nestedValue === 'object' && !Array.isArray(nestedValue)) {
47
+ return Object.fromEntries(
48
+ Object.entries(nestedValue as Record<string, unknown>).sort(([left], [right]) =>
49
+ left.localeCompare(right),
50
+ ),
51
+ )
52
+ }
53
+
54
+ return nestedValue
55
+ },
56
+ 2,
57
+ )
58
+ }
59
+
60
+ function createFingerprint(resources: DiscoveredVideoResource[]): string {
61
+ const hash = crypto.createHash('sha256')
62
+ hash.update(
63
+ stableJson(
64
+ resources.map((resource) => ({
65
+ packageName: resource.packageName,
66
+ packageVersion: resource.packageVersion,
67
+ exportName: resource.exportName,
68
+ annotationName: resource.annotationName,
69
+ metadata: resource.metadata,
70
+ sourceDigest: resource.sourceDigest,
71
+ })),
72
+ ),
73
+ )
74
+ return `sha256:${hash.digest('hex')}`
75
+ }
76
+
77
+ function buildImportSpecifier(projectRoot: string, resource: DiscoveredVideoResource): string {
78
+ if (resource.packageRoot !== projectRoot) {
79
+ return resource.packageName
80
+ }
81
+
82
+ const relativePath = path
83
+ .relative(path.join(projectRoot, '.vibecuting'), path.join(projectRoot, resource.sourceFile))
84
+ .replaceAll(path.sep, '/')
85
+ .replace(/\.(tsx?|jsx?|mjs|cjs)$/, '')
86
+
87
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`
88
+ }
89
+
90
+ function buildImports(projectRoot: string, resources: DiscoveredVideoResource[]): string {
91
+ const grouped = new Map<string, DiscoveredVideoResource[]>()
92
+
93
+ for (const resource of resources) {
94
+ const specifier = buildImportSpecifier(projectRoot, resource)
95
+ const items = grouped.get(specifier) ?? []
96
+ items.push(resource)
97
+ grouped.set(specifier, items)
98
+ }
99
+
100
+ return Array.from(grouped.entries())
101
+ .map(([specifier, packageResources]) => {
102
+ const names = packageResources.map((resource) => resource.exportName).join(', ')
103
+ return `import { ${names} } from '${specifier}'`
104
+ })
105
+ .join('\n')
106
+ }
107
+
108
+ function buildRegistryArray(
109
+ name: string,
110
+ kind: DiscoveredVideoResource['metadata']['resourceKind'],
111
+ resources: DiscoveredVideoResource[],
112
+ ): string {
113
+ const imports = resources
114
+ .filter((resource) => resource.metadata.resourceKind === kind)
115
+ .map((resource) => resource.exportName)
116
+
117
+ const factoryName =
118
+ kind === 'scene'
119
+ ? 'createScenePluginRegistry'
120
+ : kind === 'transition'
121
+ ? 'createTransitionPluginRegistry'
122
+ : 'createSceneThemeRegistry'
123
+
124
+ return `export const ${name} = ${factoryName}([${imports.join(', ')}])`
125
+ }
126
+
127
+ function buildMetadataRecords(resources: DiscoveredVideoResource[]): GeneratedResourceRecord[] {
128
+ return resources.map((resource) => ({
129
+ packageName: resource.packageName,
130
+ exportName: resource.exportName,
131
+ metadata: resource.metadata,
132
+ }))
133
+ }
134
+
135
+ export async function generateVideoResourceMeta({
136
+ projectRoot,
137
+ resources,
138
+ }: GeneratedModuleInput): Promise<{ filePath: string; contents: string }> {
139
+ const sortedResources = [...resources].sort((left, right) => {
140
+ return (
141
+ left.packageName.localeCompare(right.packageName) ||
142
+ left.metadata.resourceKind.localeCompare(right.metadata.resourceKind) ||
143
+ left.metadata.pluginKey.localeCompare(right.metadata.pluginKey) ||
144
+ left.exportName.localeCompare(right.exportName)
145
+ )
146
+ })
147
+
148
+ const filePath = path.join(projectRoot, '.vibecuting', 'video-resource-meta.generated.ts')
149
+ const fingerprint = createFingerprint(sortedResources)
150
+ const packageEntries: ResourceEntry[] = []
151
+ const seenPackages = new Set<string>()
152
+
153
+ for (const resource of sortedResources) {
154
+ if (seenPackages.has(resource.packageName)) {
155
+ continue
156
+ }
157
+
158
+ seenPackages.add(resource.packageName)
159
+ packageEntries.push({
160
+ packageName: resource.packageName,
161
+ packageVersion: resource.packageVersion,
162
+ })
163
+ }
164
+
165
+ const contents = [
166
+ '// Generated by update-component-meta. Do not edit.',
167
+ '',
168
+ buildImports(projectRoot, sortedResources),
169
+ "import { createScenePluginRegistry, createSceneThemeRegistry, createTransitionPluginRegistry } from '@vibecuting/video-project-core'",
170
+ '',
171
+ `export const videoResourceMeta = ${stableJson({
172
+ schemaVersion: 1,
173
+ packageFingerprint: fingerprint,
174
+ packages: packageEntries,
175
+ resources: buildMetadataRecords(sortedResources),
176
+ })} as const`,
177
+ '',
178
+ buildRegistryArray('installedScenePluginRegistry', 'scene', sortedResources),
179
+ buildRegistryArray('installedTransitionPluginRegistry', 'transition', sortedResources),
180
+ buildRegistryArray('installedSceneThemeRegistry', 'theme', sortedResources),
181
+ '',
182
+ ]
183
+ .filter(Boolean)
184
+ .join('\n')
185
+
186
+ return { filePath, contents }
187
+ }
188
+
189
+ export async function writeVideoResourceMeta(
190
+ input: GeneratedModuleInput,
191
+ ): Promise<{ filePath: string; contents: string }> {
192
+ const generated = await generateVideoResourceMeta(input)
193
+ await fs.mkdir(path.dirname(generated.filePath), { recursive: true })
194
+ const tempPath = `${generated.filePath}.tmp`
195
+ await fs.writeFile(tempPath, `${generated.contents}\n`, 'utf8')
196
+ await fs.rename(tempPath, generated.filePath)
197
+ return generated
198
+ }
@@ -0,0 +1,64 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import { tmpdir } from 'node:os'
4
+
5
+ import { expect, test } from 'vitest'
6
+
7
+ import { updateComponentMeta } from './update-component-meta'
8
+
9
+ test('writes and checks the unified video resource metadata file', async () => {
10
+ const projectRoot = await fs.mkdtemp(path.join(tmpdir(), 'component-project-helper-meta-'))
11
+ const packageRoot = path.join(projectRoot, 'node_modules', '@vibecuting', 'demo-pack')
12
+ const srcRoot = path.join(packageRoot, 'src')
13
+
14
+ await fs.mkdir(srcRoot, { recursive: true })
15
+ await fs.writeFile(
16
+ path.join(packageRoot, 'package.json'),
17
+ JSON.stringify({
18
+ name: '@vibecuting/demo-pack',
19
+ version: '0.1.0',
20
+ main: './src/index.ts',
21
+ types: './src/index.ts',
22
+ }),
23
+ 'utf8',
24
+ )
25
+
26
+ await fs.writeFile(
27
+ path.join(srcRoot, 'index.ts'),
28
+ `
29
+ export const sceneMetadata = defineScenePluginMetadata({
30
+ resourceKind: 'scene',
31
+ name: 'DemoScene',
32
+ description: 'Demo scene resource',
33
+ sourceFile: 'src/index.ts',
34
+ pluginKey: 'demo.scene',
35
+ tags: ['demo'],
36
+ aspectRatio: '16:9',
37
+ sceneType: 'scene',
38
+ sceneFamily: 'custom',
39
+ rootLayout: 'absolute-fill',
40
+ })
41
+
42
+ export const DemoScene = VideoComponent(sceneMetadata)(
43
+ defineSceneComponent({
44
+ family: 'custom',
45
+ propsSchema: null,
46
+ component: function DemoScene() {
47
+ return null
48
+ },
49
+ }),
50
+ )
51
+ `,
52
+ 'utf8',
53
+ )
54
+
55
+ await updateComponentMeta(projectRoot)
56
+
57
+ const generatedFilePath = path.join(projectRoot, '.vibecuting', 'video-resource-meta.generated.ts')
58
+ const generatedContents = await fs.readFile(generatedFilePath, 'utf8')
59
+
60
+ expect(generatedContents).toContain('installedScenePluginRegistry')
61
+ expect(generatedContents).toContain('DemoScene')
62
+
63
+ await expect(updateComponentMeta(projectRoot, true)).resolves.toBeUndefined()
64
+ })
@@ -0,0 +1,47 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+
4
+ import { discoverVideoResources } from '../discovery/index.ts'
5
+ import { generateVideoResourceMeta } from './generate-video-resource-meta.ts'
6
+
7
+ export async function updateComponentMeta(projectRoot: string, checkOnly = false): Promise<void> {
8
+ const resources = await discoverVideoResources(projectRoot)
9
+ const generated = await generateVideoResourceMeta({ projectRoot, resources })
10
+ const currentContents = await readOptionalFile(generated.filePath)
11
+
12
+ if (checkOnly) {
13
+ if (currentContents?.trimEnd() !== generated.contents.trimEnd()) {
14
+ throw new Error('Generated video resource metadata is stale. Run pnpm run update-component-meta')
15
+ }
16
+
17
+ return
18
+ }
19
+
20
+ await fs.mkdir(path.dirname(generated.filePath), { recursive: true })
21
+ const tempFilePath = `${generated.filePath}.tmp`
22
+ await fs.writeFile(tempFilePath, `${generated.contents}\n`, 'utf8')
23
+ await fs.rename(tempFilePath, generated.filePath)
24
+ }
25
+
26
+ async function readOptionalFile(filePath: string): Promise<string | undefined> {
27
+ try {
28
+ return await fs.readFile(filePath, 'utf8')
29
+ } catch (error) {
30
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
31
+ return undefined
32
+ }
33
+
34
+ throw error
35
+ }
36
+ }
37
+
38
+ if (import.meta.url === `file://${process.argv[1]}`) {
39
+ const checkOnly = process.argv.includes('--check')
40
+ const cwd = process.cwd()
41
+
42
+ updateComponentMeta(cwd, checkOnly).catch((error: unknown) => {
43
+ const message = error instanceof Error ? error.message : String(error)
44
+ console.error(message)
45
+ process.exitCode = 1
46
+ })
47
+ }
@@ -0,0 +1,47 @@
1
+ import { createRequire } from 'node:module'
2
+ import type {
3
+ ScenePluginMetadata,
4
+ ThemePluginMetadata,
5
+ TransitionPluginMetadata,
6
+ VideoResourceDescriptor,
7
+ VideoResourceMetadata,
8
+ } from './video-resource.ts'
9
+ import {
10
+ ScenePluginMetadataSchema,
11
+ ThemePluginMetadataSchema,
12
+ TransitionPluginMetadataSchema,
13
+ } from './video-resource.ts'
14
+
15
+ const require = createRequire(import.meta.url)
16
+ const videoResourceDescriptorDefinitionRows = require('./video-resource-descriptors.json')
17
+
18
+ export const videoResourceDescriptorDefinitions = videoResourceDescriptorDefinitionRows
19
+
20
+ export type VideoResourceDescriptorDefinition = (typeof videoResourceDescriptorDefinitions)[number]
21
+
22
+ export const sceneResourceDescriptor: VideoResourceDescriptor<ScenePluginMetadata> = {
23
+ kind: 'scene',
24
+ annotationName: 'VideoComponent',
25
+ metadataFactoryName: 'defineScenePluginMetadata',
26
+ schema: ScenePluginMetadataSchema,
27
+ }
28
+
29
+ export const transitionResourceDescriptor: VideoResourceDescriptor<TransitionPluginMetadata> = {
30
+ kind: 'transition',
31
+ annotationName: 'TransitionComponent',
32
+ metadataFactoryName: 'defineTransitionPluginMetadata',
33
+ schema: TransitionPluginMetadataSchema,
34
+ }
35
+
36
+ export const themeResourceDescriptor: VideoResourceDescriptor<ThemePluginMetadata> = {
37
+ kind: 'theme',
38
+ annotationName: 'ThemeComponent',
39
+ metadataFactoryName: 'defineThemePluginMetadata',
40
+ schema: ThemePluginMetadataSchema,
41
+ }
42
+
43
+ export const videoResourceDescriptors = [
44
+ sceneResourceDescriptor,
45
+ transitionResourceDescriptor,
46
+ themeResourceDescriptor,
47
+ ] as const satisfies readonly VideoResourceDescriptor<VideoResourceMetadata>[]
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "kind": "scene",
4
+ "annotationName": "VideoComponent",
5
+ "metadataFactoryName": "defineScenePluginMetadata"
6
+ },
7
+ {
8
+ "kind": "transition",
9
+ "annotationName": "TransitionComponent",
10
+ "metadataFactoryName": "defineTransitionPluginMetadata"
11
+ },
12
+ {
13
+ "kind": "theme",
14
+ "annotationName": "ThemeComponent",
15
+ "metadataFactoryName": "defineThemePluginMetadata"
16
+ }
17
+ ]
@@ -0,0 +1,143 @@
1
+ import 'reflect-metadata'
2
+
3
+ import { z } from 'zod'
4
+
5
+ export const VideoResourceKindSchema = z.enum(['scene', 'transition', 'theme'])
6
+
7
+ export type VideoResourceKind = z.infer<typeof VideoResourceKindSchema>
8
+
9
+ export const BaseVideoResourceMetadataSchema = z.object({
10
+ resourceKind: VideoResourceKindSchema,
11
+ name: z.string().min(1),
12
+ description: z.string().min(1),
13
+ sourceFile: z.string().min(1),
14
+ pluginKey: z.string().min(1),
15
+ tags: z.array(z.string().min(1)).default([]),
16
+ })
17
+
18
+ export const SceneSlotHintSchema = z.object({
19
+ name: z.string().min(1),
20
+ description: z.string().min(1).optional(),
21
+ required: z.boolean().optional(),
22
+ accepts: z.enum(['text', 'image', 'video', 'component', 'any']).optional(),
23
+ })
24
+
25
+ export const SceneSlotsSchema = z.union([
26
+ z.array(z.string().min(1)),
27
+ z.array(SceneSlotHintSchema),
28
+ ])
29
+
30
+ const SceneFamilySchema = z.enum(['slide', 'canvas-2d', 'game-2d', 'three-3d', 'custom'])
31
+
32
+ export const ScenePluginMetadataSchema = BaseVideoResourceMetadataSchema.extend({
33
+ resourceKind: z.literal('scene'),
34
+ aspectRatio: z.enum(['9:16', '16:9']),
35
+ sceneType: z.enum(['intro', 'scene', 'outro']).optional(),
36
+ sceneFamily: SceneFamilySchema.default('custom'),
37
+ themePreset: z.string().min(1).optional(),
38
+ slots: SceneSlotsSchema.optional(),
39
+ propsTypeName: z.string().min(1).optional(),
40
+ rootLayout: z.literal('absolute-fill').default('absolute-fill'),
41
+ })
42
+
43
+ export const TransitionPluginMetadataSchema = BaseVideoResourceMetadataSchema.extend({
44
+ resourceKind: z.literal('transition'),
45
+ transitionKind: z.enum(['transition', 'overlay']),
46
+ })
47
+
48
+ export const ThemePluginMetadataSchema = BaseVideoResourceMetadataSchema.extend({
49
+ resourceKind: z.literal('theme'),
50
+ supportedSceneFamilies: z.array(SceneFamilySchema).default([]),
51
+ cssVariables: z.array(z.string().regex(/^--scene-/)).default([]),
52
+ })
53
+
54
+ export const VideoResourceMetadataSchema = z.discriminatedUnion('resourceKind', [
55
+ ScenePluginMetadataSchema,
56
+ TransitionPluginMetadataSchema,
57
+ ThemePluginMetadataSchema,
58
+ ])
59
+
60
+ export type ScenePluginMetadata = z.infer<typeof ScenePluginMetadataSchema>
61
+ export type SceneSlotHint = z.infer<typeof SceneSlotHintSchema>
62
+ export type TransitionPluginMetadata = z.infer<typeof TransitionPluginMetadataSchema>
63
+ export type ThemePluginMetadata = z.infer<typeof ThemePluginMetadataSchema>
64
+ export type VideoResourceMetadata = z.infer<typeof VideoResourceMetadataSchema>
65
+
66
+ export const VIDEO_RESOURCE_METADATA_KEY = Symbol.for(
67
+ '@vibecuting/component-project-helper/video-resource-metadata',
68
+ )
69
+
70
+ export type VideoResourceDescriptor<TMetadata extends VideoResourceMetadata> = {
71
+ kind: TMetadata['resourceKind']
72
+ annotationName: string
73
+ metadataFactoryName: string
74
+ schema: z.ZodType<TMetadata>
75
+ }
76
+
77
+ export type VideoResourceAnnotation<TMetadata extends VideoResourceMetadata> = {
78
+ defineMetadata(input: TMetadata): TMetadata
79
+ annotate(metadata: TMetadata): <T extends object>(target: T) => T
80
+ getMetadata(target: unknown): TMetadata | undefined
81
+ }
82
+
83
+ export function createVideoResourceAnnotation<TMetadata extends VideoResourceMetadata>(
84
+ descriptor: VideoResourceDescriptor<TMetadata>,
85
+ ): VideoResourceAnnotation<TMetadata> {
86
+ return {
87
+ defineMetadata(input) {
88
+ return descriptor.schema.parse(input)
89
+ },
90
+ annotate(metadata) {
91
+ const normalized = descriptor.schema.parse(metadata)
92
+
93
+ return function annotateTarget<T extends object>(target: T): T {
94
+ Reflect.defineMetadata(VIDEO_RESOURCE_METADATA_KEY, normalized, target)
95
+ return target
96
+ }
97
+ },
98
+ getMetadata(target) {
99
+ if (!target || (typeof target !== 'object' && typeof target !== 'function')) {
100
+ return undefined
101
+ }
102
+
103
+ const metadata = Reflect.getMetadata(VIDEO_RESOURCE_METADATA_KEY, target)
104
+ const parsed = descriptor.schema.safeParse(metadata)
105
+ return parsed.success ? parsed.data : undefined
106
+ },
107
+ }
108
+ }
109
+
110
+ export function getVideoResourceMetadata<TMetadata extends VideoResourceMetadata>(
111
+ target: unknown,
112
+ expectedKind?: TMetadata['resourceKind'],
113
+ ): TMetadata | undefined {
114
+ if (!target || (typeof target !== 'object' && typeof target !== 'function')) {
115
+ return undefined
116
+ }
117
+
118
+ const metadata = Reflect.getMetadata(VIDEO_RESOURCE_METADATA_KEY, target)
119
+ const parsed = VideoResourceMetadataSchema.safeParse(metadata)
120
+ if (!parsed.success) {
121
+ return undefined
122
+ }
123
+
124
+ if (expectedKind && parsed.data.resourceKind !== expectedKind) {
125
+ return undefined
126
+ }
127
+
128
+ return parsed.data as TMetadata
129
+ }
130
+
131
+ export function defineScenePluginMetadata(input: ScenePluginMetadata): ScenePluginMetadata {
132
+ return ScenePluginMetadataSchema.parse(input)
133
+ }
134
+
135
+ export function defineTransitionPluginMetadata(
136
+ input: TransitionPluginMetadata,
137
+ ): TransitionPluginMetadata {
138
+ return TransitionPluginMetadataSchema.parse(input)
139
+ }
140
+
141
+ export function defineThemePluginMetadata(input: ThemePluginMetadata): ThemePluginMetadata {
142
+ return ThemePluginMetadataSchema.parse(input)
143
+ }
@@ -1,6 +1,25 @@
1
1
  import { z } from 'zod'
2
2
 
3
- export const ComponentProjectComponentMetadataSchema = z.object({
3
+ export {
4
+ BaseVideoResourceMetadataSchema,
5
+ ScenePluginMetadataSchema,
6
+ SceneSlotHintSchema,
7
+ SceneSlotsSchema,
8
+ ThemePluginMetadataSchema,
9
+ TransitionPluginMetadataSchema,
10
+ VideoResourceKindSchema,
11
+ VideoResourceMetadataSchema,
12
+ } from '../resources/video-resource.ts'
13
+ export type {
14
+ ScenePluginMetadata,
15
+ SceneSlotHint,
16
+ ThemePluginMetadata,
17
+ TransitionPluginMetadata,
18
+ VideoResourceKind,
19
+ VideoResourceMetadata,
20
+ } from '../resources/video-resource.ts'
21
+
22
+ const ComponentProjectComponentMetadataInputSchema = z.object({
4
23
  name: z.string().min(1),
5
24
  description: z.string().min(1),
6
25
  sourceFile: z.string().min(1),
@@ -10,6 +29,9 @@ export const ComponentProjectComponentMetadataSchema = z.object({
10
29
  propsTypeName: z.string().min(1).optional(),
11
30
  })
12
31
 
32
+ export const ComponentProjectComponentMetadataSchema =
33
+ ComponentProjectComponentMetadataInputSchema
34
+
13
35
  export const ComponentProjectGeneratedManifestSchema = z.object({
14
36
  projectRoot: z.string().min(1),
15
37
  generatedAt: z.string().min(1),
@@ -18,7 +40,7 @@ export const ComponentProjectGeneratedManifestSchema = z.object({
18
40
  })
19
41
 
20
42
  export type ComponentProjectComponentMetadata = z.infer<
21
- typeof ComponentProjectComponentMetadataSchema
43
+ typeof ComponentProjectComponentMetadataInputSchema
22
44
  >
23
45
  export type ComponentProjectGeneratedManifest = z.infer<
24
46
  typeof ComponentProjectGeneratedManifestSchema