@hoardodile/sdk-types 0.1.1 → 0.1.2

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { C as CoverKindUi, a as CoverKindUiMap, P as PluginManifest, b as PluginManifestId, c as PluginManifestUi, d as PluginPermissions, S as SearchKind } from './manifest-TGCp8NDj.js';
1
+ export { C as CoverKindUi, a as CoverKindUiMap, P as PluginManifest, b as PluginManifestId, c as PluginManifestUi, d as PluginPermissions, S as SearchKind } from './manifest-JMXWSfKE.js';
2
2
  import { PluginAssetErrorName } from './plugin-asset-limits.js';
3
3
  import { MediaKind } from './media-exts.js';
4
4
  import { Result } from './result.js';
@@ -161,6 +161,7 @@ declare const pluginManifest: z.ZodObject<{
161
161
  description: z.ZodString;
162
162
  icon: z.ZodOptional<z.ZodString>;
163
163
  version: z.ZodString;
164
+ minAppVersion: z.ZodOptional<z.ZodString>;
164
165
  permissions: z.ZodObject<{
165
166
  sourceMeta: z.ZodDefault<z.ZodBoolean>;
166
167
  searchMeta: z.ZodDefault<z.ZodBoolean>;
@@ -1,4 +1,4 @@
1
- import { d as PluginPermissions } from './manifest-TGCp8NDj.js';
1
+ import { d as PluginPermissions } from './manifest-JMXWSfKE.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
package/dist/schema.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- export { C as CoverKindUi, a as CoverKindUiMap, P as PluginManifest, b as PluginManifestId, c as PluginManifestUi, d as PluginPermissions, S as SearchKind, i as iconRef, l as localeString, p as pluginManifest, e as pluginManifestId, f as pluginManifestUi, g as pluginPermissions, s as searchKind } from './manifest-TGCp8NDj.js';
2
+ export { C as CoverKindUi, a as CoverKindUiMap, P as PluginManifest, b as PluginManifestId, c as PluginManifestUi, d as PluginPermissions, S as SearchKind, i as iconRef, l as localeString, p as pluginManifest, e as pluginManifestId, f as pluginManifestUi, g as pluginPermissions, s as searchKind } from './manifest-JMXWSfKE.js';
3
3
 
4
4
  /**
5
5
  * The zod schema layer of the plugin contract: the manifest schema and
package/dist/schema.js CHANGED
@@ -106,6 +106,13 @@ var pluginManifest = z.object({
106
106
  icon: iconRef.optional(),
107
107
  /** Semantic plugin version; shown to users on the plugin card. */
108
108
  version: z.string().min(1),
109
+ /**
110
+ * Minimum hoardodile app version this plugin runs on (e.g. `"0.1.1"`).
111
+ * Optional: absent means compatible with every app version. Hosts below
112
+ * the minimum refuse to install/update the plugin — the marketplace
113
+ * hides the install/update entries and the zip upload blocks it.
114
+ */
115
+ minAppVersion: z.string().min(1).optional(),
109
116
  /** Declared capabilities (see {@link pluginPermissions}). */
110
117
  permissions: pluginPermissions,
111
118
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manifest.ts","../src/schema.ts"],"names":["z"],"mappings":"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","file":"schema.js","sourcesContent":["import { z } from \"zod\"\n\n/**\n * Plugin manifest UUID (v4). Generated once when scaffolding a plugin\n * (e.g. `crypto.randomUUID()`) and never reused across plugins — the\n * server keys installed plugins by this id.\n */\nexport const pluginManifestId = z.string().uuid()\nexport type PluginManifestId = z.infer<typeof pluginManifestId>\n\n/**\n * Declared plugin capabilities. Each flag gates the corresponding API\n * surface: a plugin without `danmaku` gets no danmaku methods and the\n * host enforces the permission at the capability guard, so a manifest\n * that does not declare a capability cannot call it.\n */\nexport const pluginPermissions = z.object({\n\t/** Read/write the resource's source metadata. */\n\tsourceMeta: z.boolean().default(false),\n\t/** Produce and store search metadata facets. */\n\tsearchMeta: z.boolean().default(false),\n\t/** Create/list danmaku for resources this plugin renders. */\n\tdanmaku: z.boolean().default(false),\n\t/** Create/list messages for resources this plugin renders. */\n\tmessage: z.boolean().default(false),\n\t/** Produce content hashes for duplicate detection / image similarity. */\n\timageHashes: z.boolean().default(false),\n\t/**\n\t * List and extract archive (zip/tar/7z/…) entries. The only API\n\t * surface with a write side effect (the host's extraction cache), so\n\t * it is denied by default.\n\t */\n\tcontainer: z.boolean().default(false),\n\t/**\n\t * The plugin asset vault: user-consented downloads into the plugin's\n\t * own `vault/` directory plus the vault read/delete methods. Denied\n\t * by default — every download needs this capability AND the user's\n\t * per-request approval.\n\t */\n\tdownload: z.boolean().default(false),\n})\nexport type PluginPermissions = z.infer<typeof pluginPermissions>\n\n/**\n * Label key → locale table: `{ \"cover.open\": { \"en\": \"Open\", \"zh-CN\": \"打开\" } }`.\n * The host's template engine resolves `t('cover.open')` against the\n * resource's locale from this map.\n */\nexport const localeString = z.record(z.string(), z.string())\n\n/**\n * Icon reference, in exactly one of two static forms:\n *\n * - `<SolarGlyph>` — a Solar glyph name (kebab-case, or the legacy\n * PascalCase spelling); the host renders it from its own bundled three\n * weights (bold / boldDuotone / linear) and follows the user's icon\n * style preference. Names outside the host's Solar index render\n * nothing.\n * - `<relative/path>` — an asset inside the plugin zip (`assets/icon.svg`),\n * served from the plugin's own directory.\n *\n * `http(s):`/`data:` URIs and `..` path segments are rejected: the\n * manifest stays plain static JSON and the host never fetches or\n * executes anything an icon reference asks for.\n */\nexport const iconRef = z\n\t.string()\n\t.min(1)\n\t.max(512)\n\t.refine(\n\t\t(value) =>\n\t\t\t!value.startsWith(\"http://\") &&\n\t\t\t!value.startsWith(\"https://\") &&\n\t\t\t!value.startsWith(\"data:\") &&\n\t\t\t!value.split(\"/\").includes(\"..\"),\n\t\t{\n\t\t\tmessage:\n\t\t\t\t\"plugin icon must be a Solar glyph name or a zip asset path (no URLs, no data: URIs, no .. path segments)\",\n\t\t},\n\t)\n\n/**\n * Corner template slot: a string rendered by the host's template engine\n * over the resource scope. The engine supports `{{data.field}}` paths,\n * pipes (`bytes`, `duration`, `number`, `inc`), comparisons\n * (`eq`/`ne`/`gt`/`lt`/`gte`/`lte`), `if(cond, a, b)`, `join`,\n * `t('key')` for i18n, `icon('<SolarGlyph>')` (a Solar glyph name — see\n * {@link iconRef}), `asset('path')`,\n * `kind(...)`, and `searchKindIcons()` (the plugin's search kinds).\n * Unknown expressions render as the empty string.\n */\nconst templateValue = z.string()\n\n/**\n * Corner template slots for one content kind. Templates are rendered by\n * the host's template engine over the resource's file list; supported\n * directives include `{{data.field}}`, `{{duration(ms)}}`, `{{inc(n)}}`\n * and `{{t('key')}}`.\n */\nconst coverKindUi = z.object({\n\ttl: z.array(templateValue).optional(),\n\ttr: z.array(templateValue).optional(),\n\tbl: z.array(templateValue).optional(),\n\tbr: z.array(templateValue).optional(),\n})\n\n/**\n * Cover templates per content kind. A plugin declares the kinds it can\n * produce; the host picks the block matching the resource's cover type\n * (`image`/`video`/`audio`/`default`) and renders each corner as\n * specified, or falls back to the default cover when no block matches.\n */\nconst coverKindUiMap = z.object({\n\timage: coverKindUi.optional(),\n\tvideo: coverKindUi.optional(),\n\taudio: coverKindUi.optional(),\n\tdefault: coverKindUi.optional(),\n})\n\n/**\n * A search facet kind: a named dimension with an icon, rendered as a\n * facet group in the host's search UI. `key` becomes the facet key in\n * the search metadata the plugin produces.\n */\nexport const searchKind = z.object({\n\tkey: z.string().min(1),\n\t/** i18n label key shown as the facet group's title. */\n\tlabel: z.string().min(1),\n\t/** Optional Solar glyph name (template `icon('Name')` or `asset('path')`). */\n\ticon: templateValue.optional(),\n})\nexport type SearchKind = z.infer<typeof searchKind>\n\nconst searchUi = z.object({\n\tkinds: z.array(searchKind),\n})\n\nconst messageUi = z.object({\n\t/**\n\t * Template string for message anchor chip labels. Rendered by the\n\t * host's template engine. Supports `{{data.field}}`, `{{duration(ms)}}`,\n\t * `{{inc(n)}}`, `{{t('key')}}`, etc.\n\t */\n\tanchor: z.string().min(1).optional(),\n})\n\n/**\n * Manifest-declared UI preferences. These shape how the host app\n * presents the plugin's iframe without the plugin shipping any host\n * integration code.\n */\nexport const pluginManifestUi = z.object({\n\t/**\n\t * Preferred preview surface height (any CSS length, e.g. \"85vh\").\n\t * Applied by both the resource detail page and the preview dialog.\n\t */\n\theight: z.string().min(1).optional(),\n\t/**\n\t * Preferred preview surface aspect ratio (e.g. \"16/9\"), capped by the\n\t * host at 70vh. Intended for video-centric plugins; takes precedence\n\t * over `height`. When neither is set the host falls back to 60vh.\n\t */\n\taspect: z.string().min(1).optional(),\n\t/**\n\t * Cover template blocks per content kind. When present, the host\n\t * renders the resource cover from the plugin's file templates\n\t * instead of the built-in thumbnail pipeline.\n\t */\n\tcard: coverKindUiMap.optional(),\n\t/** Search facet kinds; enables the plugin's search integration. */\n\tsearch: searchUi.optional(),\n\t/**\n\t * Anchor chip label template for messages; declares message-anchor\n\t * support in the host UI.\n\t */\n\tmessage: messageUi.optional(),\n\t/**\n\t * Whether the plugin iframe inherits the host's app font (default true).\n\t * Set to false for plugins that must render with their own fonts.\n\t */\n\tinheritFont: z.boolean().optional(),\n})\nexport type PluginManifestUi = z.infer<typeof pluginManifestUi>\nexport type CoverKindUi = z.infer<typeof coverKindUi>\nexport type CoverKindUiMap = z.infer<typeof coverKindUiMap>\n\n/**\n * The plugin manifest contract — the single schema validated everywhere\n * via its parse: the server at install time, the build CLI, and the\n * workbench. A manifest lives at the zip root of a built plugin next to\n * `main.js` and `index.html`.\n */\nexport const pluginManifest = z.object({\n\tid: pluginManifestId,\n\t/** Display name shown in the plugins list and resource badges. */\n\tname: z.string().min(1),\n\t/** One-line description shown in the plugins list. */\n\tdescription: z.string().min(1),\n\t/** Icon: a Solar glyph name or a zip asset path (see {@link iconRef}). */\n\ticon: iconRef.optional(),\n\t/** Semantic plugin version; shown to users on the plugin card. */\n\tversion: z.string().min(1),\n\t/** Declared capabilities (see {@link pluginPermissions}). */\n\tpermissions: pluginPermissions,\n\t/**\n\t * Localized label tables: `{ labelKey: { locale: label } }`, e.g.\n\t * `{ \"cover.open\": { \"en\": \"Open\", \"zh-CN\": \"打开\" } }` — labels are\n\t * referenced from templates with `{{t('labelKey')}}` (see\n\t * {@link localeString}).\n\t */\n\ti18n: z.record(z.string(), localeString).optional(),\n\t/** UI preferences (see {@link pluginManifestUi}). */\n\tui: pluginManifestUi.optional(),\n})\nexport type PluginManifest = z.infer<typeof pluginManifest>\n","/**\n * The zod schema layer of the plugin contract: the manifest schema and\n * the wire anchor envelope. Import this subpath\n * (`@hoardodile/sdk-types/schema`) only where a runtime validator is\n * actually needed — the host, the server, and tooling. The root entry\n * re-exports the inferred types only, so plugin bundles never pull zod.\n */\nimport { z } from \"zod\"\n\nexport * from \"./manifest.ts\"\n\n/**\n * Wire/storage envelope for a message or danmaku anchor. Carries only\n * the plugin-defined location payload in `data`; the host never\n * interprets its contents. The anchor's resource is host state — the SDK\n * injects it from the iframe's binding and the server derives it from\n * the row's `anchor_resource_id` column — so plugins never see a resId\n * here, and a plugin that sends one is rejected (strict).\n *\n * Plugin code works with the raw location data (`PluginSchema[\"anchor\"]`)\n * directly; the SDK wraps it into this envelope when it crosses the\n * wire.\n */\nexport const anchorData = z\n\t.object({\n\t\tdata: z.unknown().optional(),\n\t})\n\t.strict()\nexport type AnchorData = z.infer<typeof anchorData>\n"]}
1
+ {"version":3,"sources":["../src/manifest.ts","../src/schema.ts"],"names":["z"],"mappings":"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","file":"schema.js","sourcesContent":["import { z } from \"zod\"\n\n/**\n * Plugin manifest UUID (v4). Generated once when scaffolding a plugin\n * (e.g. `crypto.randomUUID()`) and never reused across plugins — the\n * server keys installed plugins by this id.\n */\nexport const pluginManifestId = z.string().uuid()\nexport type PluginManifestId = z.infer<typeof pluginManifestId>\n\n/**\n * Declared plugin capabilities. Each flag gates the corresponding API\n * surface: a plugin without `danmaku` gets no danmaku methods and the\n * host enforces the permission at the capability guard, so a manifest\n * that does not declare a capability cannot call it.\n */\nexport const pluginPermissions = z.object({\n\t/** Read/write the resource's source metadata. */\n\tsourceMeta: z.boolean().default(false),\n\t/** Produce and store search metadata facets. */\n\tsearchMeta: z.boolean().default(false),\n\t/** Create/list danmaku for resources this plugin renders. */\n\tdanmaku: z.boolean().default(false),\n\t/** Create/list messages for resources this plugin renders. */\n\tmessage: z.boolean().default(false),\n\t/** Produce content hashes for duplicate detection / image similarity. */\n\timageHashes: z.boolean().default(false),\n\t/**\n\t * List and extract archive (zip/tar/7z/…) entries. The only API\n\t * surface with a write side effect (the host's extraction cache), so\n\t * it is denied by default.\n\t */\n\tcontainer: z.boolean().default(false),\n\t/**\n\t * The plugin asset vault: user-consented downloads into the plugin's\n\t * own `vault/` directory plus the vault read/delete methods. Denied\n\t * by default — every download needs this capability AND the user's\n\t * per-request approval.\n\t */\n\tdownload: z.boolean().default(false),\n})\nexport type PluginPermissions = z.infer<typeof pluginPermissions>\n\n/**\n * Label key → locale table: `{ \"cover.open\": { \"en\": \"Open\", \"zh-CN\": \"打开\" } }`.\n * The host's template engine resolves `t('cover.open')` against the\n * resource's locale from this map.\n */\nexport const localeString = z.record(z.string(), z.string())\n\n/**\n * Icon reference, in exactly one of two static forms:\n *\n * - `<SolarGlyph>` — a Solar glyph name (kebab-case, or the legacy\n * PascalCase spelling); the host renders it from its own bundled three\n * weights (bold / boldDuotone / linear) and follows the user's icon\n * style preference. Names outside the host's Solar index render\n * nothing.\n * - `<relative/path>` — an asset inside the plugin zip (`assets/icon.svg`),\n * served from the plugin's own directory.\n *\n * `http(s):`/`data:` URIs and `..` path segments are rejected: the\n * manifest stays plain static JSON and the host never fetches or\n * executes anything an icon reference asks for.\n */\nexport const iconRef = z\n\t.string()\n\t.min(1)\n\t.max(512)\n\t.refine(\n\t\t(value) =>\n\t\t\t!value.startsWith(\"http://\") &&\n\t\t\t!value.startsWith(\"https://\") &&\n\t\t\t!value.startsWith(\"data:\") &&\n\t\t\t!value.split(\"/\").includes(\"..\"),\n\t\t{\n\t\t\tmessage:\n\t\t\t\t\"plugin icon must be a Solar glyph name or a zip asset path (no URLs, no data: URIs, no .. path segments)\",\n\t\t},\n\t)\n\n/**\n * Corner template slot: a string rendered by the host's template engine\n * over the resource scope. The engine supports `{{data.field}}` paths,\n * pipes (`bytes`, `duration`, `number`, `inc`), comparisons\n * (`eq`/`ne`/`gt`/`lt`/`gte`/`lte`), `if(cond, a, b)`, `join`,\n * `t('key')` for i18n, `icon('<SolarGlyph>')` (a Solar glyph name — see\n * {@link iconRef}), `asset('path')`,\n * `kind(...)`, and `searchKindIcons()` (the plugin's search kinds).\n * Unknown expressions render as the empty string.\n */\nconst templateValue = z.string()\n\n/**\n * Corner template slots for one content kind. Templates are rendered by\n * the host's template engine over the resource's file list; supported\n * directives include `{{data.field}}`, `{{duration(ms)}}`, `{{inc(n)}}`\n * and `{{t('key')}}`.\n */\nconst coverKindUi = z.object({\n\ttl: z.array(templateValue).optional(),\n\ttr: z.array(templateValue).optional(),\n\tbl: z.array(templateValue).optional(),\n\tbr: z.array(templateValue).optional(),\n})\n\n/**\n * Cover templates per content kind. A plugin declares the kinds it can\n * produce; the host picks the block matching the resource's cover type\n * (`image`/`video`/`audio`/`default`) and renders each corner as\n * specified, or falls back to the default cover when no block matches.\n */\nconst coverKindUiMap = z.object({\n\timage: coverKindUi.optional(),\n\tvideo: coverKindUi.optional(),\n\taudio: coverKindUi.optional(),\n\tdefault: coverKindUi.optional(),\n})\n\n/**\n * A search facet kind: a named dimension with an icon, rendered as a\n * facet group in the host's search UI. `key` becomes the facet key in\n * the search metadata the plugin produces.\n */\nexport const searchKind = z.object({\n\tkey: z.string().min(1),\n\t/** i18n label key shown as the facet group's title. */\n\tlabel: z.string().min(1),\n\t/** Optional Solar glyph name (template `icon('Name')` or `asset('path')`). */\n\ticon: templateValue.optional(),\n})\nexport type SearchKind = z.infer<typeof searchKind>\n\nconst searchUi = z.object({\n\tkinds: z.array(searchKind),\n})\n\nconst messageUi = z.object({\n\t/**\n\t * Template string for message anchor chip labels. Rendered by the\n\t * host's template engine. Supports `{{data.field}}`, `{{duration(ms)}}`,\n\t * `{{inc(n)}}`, `{{t('key')}}`, etc.\n\t */\n\tanchor: z.string().min(1).optional(),\n})\n\n/**\n * Manifest-declared UI preferences. These shape how the host app\n * presents the plugin's iframe without the plugin shipping any host\n * integration code.\n */\nexport const pluginManifestUi = z.object({\n\t/**\n\t * Preferred preview surface height (any CSS length, e.g. \"85vh\").\n\t * Applied by both the resource detail page and the preview dialog.\n\t */\n\theight: z.string().min(1).optional(),\n\t/**\n\t * Preferred preview surface aspect ratio (e.g. \"16/9\"), capped by the\n\t * host at 70vh. Intended for video-centric plugins; takes precedence\n\t * over `height`. When neither is set the host falls back to 60vh.\n\t */\n\taspect: z.string().min(1).optional(),\n\t/**\n\t * Cover template blocks per content kind. When present, the host\n\t * renders the resource cover from the plugin's file templates\n\t * instead of the built-in thumbnail pipeline.\n\t */\n\tcard: coverKindUiMap.optional(),\n\t/** Search facet kinds; enables the plugin's search integration. */\n\tsearch: searchUi.optional(),\n\t/**\n\t * Anchor chip label template for messages; declares message-anchor\n\t * support in the host UI.\n\t */\n\tmessage: messageUi.optional(),\n\t/**\n\t * Whether the plugin iframe inherits the host's app font (default true).\n\t * Set to false for plugins that must render with their own fonts.\n\t */\n\tinheritFont: z.boolean().optional(),\n})\nexport type PluginManifestUi = z.infer<typeof pluginManifestUi>\nexport type CoverKindUi = z.infer<typeof coverKindUi>\nexport type CoverKindUiMap = z.infer<typeof coverKindUiMap>\n\n/**\n * The plugin manifest contract — the single schema validated everywhere\n * via its parse: the server at install time, the build CLI, and the\n * workbench. A manifest lives at the zip root of a built plugin next to\n * `main.js` and `index.html`.\n */\nexport const pluginManifest = z.object({\n\tid: pluginManifestId,\n\t/** Display name shown in the plugins list and resource badges. */\n\tname: z.string().min(1),\n\t/** One-line description shown in the plugins list. */\n\tdescription: z.string().min(1),\n\t/** Icon: a Solar glyph name or a zip asset path (see {@link iconRef}). */\n\ticon: iconRef.optional(),\n\t/** Semantic plugin version; shown to users on the plugin card. */\n\tversion: z.string().min(1),\n\t/**\n\t * Minimum hoardodile app version this plugin runs on (e.g. `\"0.1.1\"`).\n\t * Optional: absent means compatible with every app version. Hosts below\n\t * the minimum refuse to install/update the plugin — the marketplace\n\t * hides the install/update entries and the zip upload blocks it.\n\t */\n\tminAppVersion: z.string().min(1).optional(),\n\t/** Declared capabilities (see {@link pluginPermissions}). */\n\tpermissions: pluginPermissions,\n\t/**\n\t * Localized label tables: `{ labelKey: { locale: label } }`, e.g.\n\t * `{ \"cover.open\": { \"en\": \"Open\", \"zh-CN\": \"打开\" } }` — labels are\n\t * referenced from templates with `{{t('labelKey')}}` (see\n\t * {@link localeString}).\n\t */\n\ti18n: z.record(z.string(), localeString).optional(),\n\t/** UI preferences (see {@link pluginManifestUi}). */\n\tui: pluginManifestUi.optional(),\n})\nexport type PluginManifest = z.infer<typeof pluginManifest>\n","/**\n * The zod schema layer of the plugin contract: the manifest schema and\n * the wire anchor envelope. Import this subpath\n * (`@hoardodile/sdk-types/schema`) only where a runtime validator is\n * actually needed — the host, the server, and tooling. The root entry\n * re-exports the inferred types only, so plugin bundles never pull zod.\n */\nimport { z } from \"zod\"\n\nexport * from \"./manifest.ts\"\n\n/**\n * Wire/storage envelope for a message or danmaku anchor. Carries only\n * the plugin-defined location payload in `data`; the host never\n * interprets its contents. The anchor's resource is host state — the SDK\n * injects it from the iframe's binding and the server derives it from\n * the row's `anchor_resource_id` column — so plugins never see a resId\n * here, and a plugin that sends one is rejected (strict).\n *\n * Plugin code works with the raw location data (`PluginSchema[\"anchor\"]`)\n * directly; the SDK wraps it into this envelope when it crosses the\n * wire.\n */\nexport const anchorData = z\n\t.object({\n\t\tdata: z.unknown().optional(),\n\t})\n\t.strict()\nexport type AnchorData = z.infer<typeof anchorData>\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoardodile/sdk-types",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "license": "MIT",
5
5
  "description": "Plugin manifest contract, shared types and constants for hoardodile content plugins.",
6
6
  "keywords": [
package/src/manifest.ts CHANGED
@@ -200,6 +200,13 @@ export const pluginManifest = z.object({
200
200
  icon: iconRef.optional(),
201
201
  /** Semantic plugin version; shown to users on the plugin card. */
202
202
  version: z.string().min(1),
203
+ /**
204
+ * Minimum hoardodile app version this plugin runs on (e.g. `"0.1.1"`).
205
+ * Optional: absent means compatible with every app version. Hosts below
206
+ * the minimum refuse to install/update the plugin — the marketplace
207
+ * hides the install/update entries and the zip upload blocks it.
208
+ */
209
+ minAppVersion: z.string().min(1).optional(),
203
210
  /** Declared capabilities (see {@link pluginPermissions}). */
204
211
  permissions: pluginPermissions,
205
212
  /**