@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-
|
|
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>;
|
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-
|
|
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
|
/**
|
package/dist/schema.js.map
CHANGED
|
@@ -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
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
|
/**
|