refactor(plugins): name shared catalog and manifest schema parts (#30612)

* plugins: name the shared catalog and manifest schema parts

Pure refactor. Marketplace entry/header fields and the duplicate-category
and duplicate-id checks become named pieces, the bounded catalog JSON read
is split from schema parsing, and the v1 manifest object is named apart
from its refinement, so a second catalog file and manifest version can
reuse them.

* plugins: share the plugin tree-identity check

Pure refactor. Install integrity and instructional-content checks each
inlined the full-hash-or-legacy-prefix comparison; both now call
pluginTreeHashMatches. The tree limits are exported for reuse.
This commit is contained in:
Neil
2026-10-19 00:34:42 -07:00
committed by GitHub
parent 36ed236394
commit 5be871e537
6 changed files with 132 additions and 113 deletions
+55 -56
View File
@@ -73,62 +73,61 @@ const eventContributionSchema = z.object({
on: z.enum(PLUGIN_EVENT_NAMES)
})
export const pluginManifestSchema = z
.object({
manifestVersion: z.literal(1),
id: pluginIdSchema,
/** Publisher slug; canonical identity is `<publisher>.<id>` — bare-id
* global uniqueness is unverifiable without a registry. */
publisher: pluginIdSchema,
name: z.string().min(1).max(256),
version: z.string().regex(SEMVER_RE, 'must be semver'),
description: z.string().max(4096).optional(),
author: z
.object({ name: z.string().min(1).max(256), url: z.string().max(2048).optional() })
.optional(),
repository: z.string().max(2048).optional(),
icon: pluginRelativePathSchema.optional(),
/** Minimum host version gate; the host refuses to load below it. */
engines: z.object({ orca: orcaEngineRangeSchema }),
/** Host-API major version this plugin targets. */
pluginApi: z.literal(1),
/** Node entry executed inside the out-of-process plugin worker. */
main: pluginRelativePathSchema.optional(),
contributes: z
.object({
panels: z.array(panelContributionSchema).max(PLUGIN_PANEL_LIMIT).default([]),
commands: z.array(commandContributionSchema).max(PLUGIN_COMMAND_LIMIT).default([]),
events: z.array(eventContributionSchema).max(PLUGIN_EVENT_SUBSCRIPTION_LIMIT).default([]),
languagePacks: z
.array(pluginLanguagePackContributionSchema)
.max(PLUGIN_LANGUAGE_PACK_LIMIT)
.default([]),
keybindings: z
.array(pluginKeybindingContributionSchema)
.max(PLUGIN_KEYBINDING_LIMIT)
.default([]),
vmRecipes: z
.array(pluginVmRecipeContributionSchema)
.max(PLUGIN_VM_RECIPE_LIMIT)
.default([]),
agents: z
.array(pluginAgentProfileContributionSchema)
.max(PLUGIN_AGENT_PROFILE_LIMIT)
.default([])
})
.strict()
.default(() => ({
panels: [],
commands: [],
events: [],
languagePacks: [],
keybindings: [],
vmRecipes: [],
agents: []
})),
capabilities: z.array(pluginCapabilitySchema).max(32).default([])
})
.superRefine(validatePluginManifestContributions)
const pluginManifestV1Object = z.object({
manifestVersion: z.literal(1),
id: pluginIdSchema,
/** Publisher slug; canonical identity is `<publisher>.<id>` — bare-id
* global uniqueness is unverifiable without a registry. */
publisher: pluginIdSchema,
name: z.string().min(1).max(256),
version: z.string().regex(SEMVER_RE, 'must be semver'),
description: z.string().max(4096).optional(),
author: z
.object({ name: z.string().min(1).max(256), url: z.string().max(2048).optional() })
.optional(),
repository: z.string().max(2048).optional(),
icon: pluginRelativePathSchema.optional(),
/** Minimum host version gate; the host refuses to load below it. */
engines: z.object({ orca: orcaEngineRangeSchema }),
/** Host-API major version this plugin targets. */
pluginApi: z.literal(1),
/** Node entry executed inside the out-of-process plugin worker. */
main: pluginRelativePathSchema.optional(),
contributes: z
.object({
panels: z.array(panelContributionSchema).max(PLUGIN_PANEL_LIMIT).default([]),
commands: z.array(commandContributionSchema).max(PLUGIN_COMMAND_LIMIT).default([]),
events: z.array(eventContributionSchema).max(PLUGIN_EVENT_SUBSCRIPTION_LIMIT).default([]),
languagePacks: z
.array(pluginLanguagePackContributionSchema)
.max(PLUGIN_LANGUAGE_PACK_LIMIT)
.default([]),
keybindings: z
.array(pluginKeybindingContributionSchema)
.max(PLUGIN_KEYBINDING_LIMIT)
.default([]),
vmRecipes: z.array(pluginVmRecipeContributionSchema).max(PLUGIN_VM_RECIPE_LIMIT).default([]),
agents: z
.array(pluginAgentProfileContributionSchema)
.max(PLUGIN_AGENT_PROFILE_LIMIT)
.default([])
})
.strict()
.default(() => ({
panels: [],
commands: [],
events: [],
languagePacks: [],
keybindings: [],
vmRecipes: [],
agents: []
})),
capabilities: z.array(pluginCapabilitySchema).max(32).default([])
})
export const pluginManifestSchema = pluginManifestV1Object.superRefine(
validatePluginManifestContributions
)
export type PluginManifest = z.infer<typeof pluginManifestSchema>
export type PluginPanelContribution = z.infer<typeof panelContributionSchema>
+51 -38
View File
@@ -53,50 +53,63 @@ export const pluginMarketplaceGitSourceSchema = z.strictObject({
ref: z.string().trim().min(1).max(4_096)
})
export const pluginMarketplaceEntrySchema = z
.strictObject({
/** Canonical `<publisher>.<id>` identity expected in the source manifest. */
id: z.string().refine(isQualifiedPluginKey, 'invalid qualified plugin key'),
source: pluginMarketplaceGitSourceSchema,
description: z.string().min(1).max(4_096).optional(),
categories: z
.array(marketplaceCategorySchema)
.max(PLUGIN_MARKETPLACE_CATEGORY_LIMIT)
.default([])
})
.superRefine((entry, context) => {
const seen = new Set<string>()
for (const [index, category] of entry.categories.entries()) {
if (seen.has(category)) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ['categories', index],
message: `duplicate category: ${category}`
})
}
seen.add(category)
const marketplaceEntryFields = {
/** Canonical `<publisher>.<id>` identity expected in the source manifest. */
id: z.string().refine(isQualifiedPluginKey, 'invalid qualified plugin key'),
source: pluginMarketplaceGitSourceSchema,
description: z.string().min(1).max(4_096).optional(),
categories: z.array(marketplaceCategorySchema).max(PLUGIN_MARKETPLACE_CATEGORY_LIMIT).default([])
}
function rejectDuplicateCategories(
entry: { categories: string[] },
context: z.RefinementCtx
): void {
const seen = new Set<string>()
for (const [index, category] of entry.categories.entries()) {
if (seen.has(category)) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ['categories', index],
message: `duplicate category: ${category}`
})
}
})
seen.add(category)
}
}
function rejectDuplicatePluginIds(
marketplace: { plugins: { id: string }[] },
context: z.RefinementCtx
): void {
const seen = new Set<string>()
for (const [index, plugin] of marketplace.plugins.entries()) {
if (seen.has(plugin.id)) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ['plugins', index, 'id'],
message: `duplicate plugin id: ${plugin.id}`
})
}
seen.add(plugin.id)
}
}
export const pluginMarketplaceEntrySchema = z
.strictObject(marketplaceEntryFields)
.superRefine(rejectDuplicateCategories)
const marketplaceHeaderFields = {
name: z.string().min(1).max(256),
owner: marketplaceOwnerSchema
}
export const pluginMarketplaceSchema = z
.strictObject({
name: z.string().min(1).max(256),
owner: marketplaceOwnerSchema,
...marketplaceHeaderFields,
plugins: z.array(pluginMarketplaceEntrySchema).max(PLUGIN_MARKETPLACE_ENTRY_LIMIT)
})
.superRefine((marketplace, context) => {
const seen = new Set<string>()
for (const [index, plugin] of marketplace.plugins.entries()) {
if (seen.has(plugin.id)) {
context.addIssue({
code: z.ZodIssueCode.custom,
path: ['plugins', index, 'id'],
message: `duplicate plugin id: ${plugin.id}`
})
}
seen.add(plugin.id)
}
})
.superRefine(rejectDuplicatePluginIds)
/** Host-derived trust metadata. Marketplace JSON cannot self-award either bit. */
export const pluginMarketplaceTrustMetadataSchema = z