Barazo AppView backend
barazo.forum
1import { z } from 'zod/v4'
2
3// ---------------------------------------------------------------------------
4// Plugin source enum
5// ---------------------------------------------------------------------------
6
7const pluginSourceSchema = z.enum(['core', 'official', 'community', 'experimental'])
8
9export type PluginSource = z.infer<typeof pluginSourceSchema>
10
11// ---------------------------------------------------------------------------
12// Plugin setting schemas (discriminated union on `type`)
13// ---------------------------------------------------------------------------
14
15const booleanSettingSchema = z.object({
16 type: z.literal('boolean'),
17 label: z.string().min(1),
18 description: z.string().optional(),
19 default: z.boolean(),
20})
21
22const stringSettingSchema = z.object({
23 type: z.literal('string'),
24 label: z.string().min(1),
25 description: z.string().optional(),
26 default: z.string(),
27 placeholder: z.string().optional(),
28})
29
30const numberSettingSchema = z.object({
31 type: z.literal('number'),
32 label: z.string().min(1),
33 description: z.string().optional(),
34 default: z.number(),
35 min: z.number().optional(),
36 max: z.number().optional(),
37})
38
39const selectSettingSchema = z.object({
40 type: z.literal('select'),
41 label: z.string().min(1),
42 description: z.string().optional(),
43 default: z.string(),
44 options: z.array(z.string()).min(1),
45})
46
47const pluginSettingSchema = z.discriminatedUnion('type', [
48 booleanSettingSchema,
49 stringSettingSchema,
50 numberSettingSchema,
51 selectSettingSchema,
52])
53
54export type PluginSettingSchema = z.infer<typeof pluginSettingSchema>
55
56// ---------------------------------------------------------------------------
57// Plugin manifest schema
58// ---------------------------------------------------------------------------
59
60/** Name must be scoped @barazo/plugin-* or unscoped barazo-plugin-*. */
61const pluginNamePattern = /^(@barazo\/plugin-[\w-]+|barazo-plugin-[\w-]+)$/
62
63/** Strict semver: major.minor.patch with optional pre-release and build metadata. */
64const semverPattern = /^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/
65
66/** Semver range expression (^, ~, >=, <, ||, *, x, etc.). */
67const semverRangePattern = /^[\^~>=<|*\s\d.x-]+$/
68
69export const pluginManifestSchema = z.object({
70 // Required fields
71 name: z
72 .string()
73 .regex(pluginNamePattern, 'Plugin name must match @barazo/plugin-* or barazo-plugin-*'),
74 displayName: z.string().min(1).max(100),
75 version: z.string().regex(semverPattern, 'Version must be valid semver (e.g. 1.0.0)'),
76 description: z.string().min(1).max(500),
77 barazoVersion: z.string().regex(semverRangePattern, 'barazoVersion must be a valid semver range'),
78 source: pluginSourceSchema,
79 category: z.string().min(1).max(50),
80 author: z.object({
81 name: z.string().min(1),
82 url: z.string().optional(),
83 }),
84 license: z.string().min(1),
85 permissions: z.object({
86 backend: z.array(z.string()),
87 frontend: z.array(z.string()),
88 }),
89
90 // Optional fields
91 lexicons: z.array(z.string()).optional(),
92 dependencies: z.array(z.string()).optional(),
93 settings: z.record(z.string(), pluginSettingSchema).optional(),
94 hooks: z
95 .object({
96 onInstall: z.string().optional(),
97 onUninstall: z.string().optional(),
98 onEnable: z.string().optional(),
99 onDisable: z.string().optional(),
100 onProfileSync: z.string().optional(),
101 })
102 .optional(),
103 backend: z
104 .object({
105 routes: z.string().optional(),
106 migrations: z.string().optional(),
107 })
108 .optional(),
109 frontend: z
110 .object({
111 register: z.string().optional(),
112 })
113 .optional(),
114})
115
116export type PluginManifest = z.infer<typeof pluginManifestSchema>