Manifest
Parse and check storeware.app.toml as the platform does.
import { ... } from '@storeware/plugin-sdk';isIndexAction
Whether a point is a list's action, and so carries a selection.
function isIndexAction(point: string): booleanhostedAppOrigin
The address a hosted app is served from.
function hostedAppOrigin(slug: string, domain?: string): stringparseManifest
Parses and structurally validates a manifest.
function parseManifest(source: string): PluginManifestmanifestUrls
Every address Storeware sends a shop's data to or frames for an app.
function manifestUrls(manifest: PluginManifest): string[]settingAccepts
Whether a value fits a setting. The same rule the admin form and the API apply to what a merchant saves, so a default that parses always saves.
function settingAccepts(field: SettingField, value: unknown): booleanvalidateScopes
Checks scope names against the platform's live vocabulary.
function validateScopes(manifest: PluginManifest, knownScopes: readonly string[]): voidvalidateEvents
Checks declared events against the platform's catalogue and the app's scopes.
function validateEvents(manifest: PluginManifest, catalogue: readonly EventAccess[]): voidvalidateRelations
Checks declared relations against the platform's graph vocabulary.
function validateRelations(manifest: PluginManifest, knownRelations: readonly string[]): voidManifestError
new ManifestError(message: string, issues?: string[])AdminExtensionPoint
type AdminExtensionPoint = (typeof ADMIN_EXTENSION_POINTS)[number];StorefrontBlock
type StorefrontBlock = (typeof STOREFRONT_BLOCKS)[number];SettingType
type SettingType = (typeof SETTING_TYPES)[number];SettingField
type SettingField = z.infer<typeof settingField>;BlockFormat
type BlockFormat = (typeof BLOCK_FORMATS)[number];AutomationTrigger
type AutomationTrigger = z.infer<typeof automationTrigger>;AutomationAction
type AutomationAction = z.infer<typeof automationAction>;PluginManifest
type PluginManifest = z.infer<typeof manifestSchema>;BillingPlan
One plan from [[billing.plans]].
type BillingPlan = z.infer<typeof billingPlan>;EventAccess
One topic the platform emits, and the scopes any one of which lets an app hear it.
| Name | Type | Description |
|---|---|---|
topic | string | |
anyOf | readonly string[] |
ADMIN_EXTENSION_POINTS
Where a plugin may render inside the admin.
const ADMIN_EXTENSION_POINTS: readonly ['admin.product.detail.sidebar', 'admin.product.detail.action', 'admin.product.index.action', 'admin.collection.detail.sidebar', 'admin.collection.detail.action', 'admin.order.detail.sidebar', 'admin.order.detail.action', 'admin.orders.index.action', 'admin.customer.detail.sidebar', 'admin.customer.detail.action', 'admin.customers.index.action', 'admin.settings.block']INDEX_ACTION_POINTS
The points on a list, which act on the rows a merchant ticked.
const INDEX_ACTION_POINTS: readonly ['admin.product.index.action', 'admin.orders.index.action', 'admin.customers.index.action']STOREFRONT_BLOCKS
Named slots a storefront theme renders plugin blocks into.
const STOREFRONT_BLOCKS: readonly ['storefront.product.below_description', 'storefront.product.above_add_to_cart', 'storefront.cart.summary', 'storefront.collection.header']HOSTED_APPS_DOMAIN
Where Storeware runs apps it hosts: each app at <slug>.storewareapps.com.
const HOSTED_APPS_DOMAIN: 'storewareapps.com'MAX_EGRESS_HOSTS
The most hosts a hosted app may send requests to, besides Storeware's API.
const MAX_EGRESS_HOSTS: 20MAX_BUNDLE_BYTES
The largest bundle storeware app deploy uploads, in bytes.
const MAX_BUNDLE_BYTES: numberSETTING_TYPES
What a merchant can type into an app's settings.
const SETTING_TYPES: readonly ['single_line_text', 'multi_line_text', 'integer', 'decimal', 'boolean', 'url', 'color', 'select', 'secret']BLOCK_FORMATS
What an app's block or section answers with.
const BLOCK_FORMATS: readonly ['html', 'components']TRIGGER_FIELD_TYPES
The kinds of value an app's trigger carries, which a condition can test.
const TRIGGER_FIELD_TYPES: readonly ['text', 'number', 'boolean', 'date', 'url']ACTION_FIELD_TYPES
The kinds of value a merchant fills in for an app's action.
const ACTION_FIELD_TYPES: readonly ['text', 'multi_line_text', 'number', 'boolean', 'url', 'select']manifestSchema
The manifest's shape. Exported for manifest-schema.ts, which publishes it as JSON Schema; everything else parses through parseManifest.
const manifestSchema: z.ZodObject<{ manifest_version: z.ZodOptional<z.ZodNumber>; app: z.ZodObject<{ slug: z.ZodString; name: z.ZodString; description: z.ZodOptional<z.ZodString>; homepage_url: z.ZodOptional<z.ZodString>; url: z.ZodOptional<z.ZodString>; navigation: z.ZodOptional<z.ZodArray<z.ZodObject<{ label: z.ZodString; path: z.ZodString; }, z.core.$strip>>>; privacy_policy_url: z.ZodOptional<z.ZodString>; support_url: z.ZodOptional<z.ZodString>; }, z.core.$strip>; oauth: z.ZodObject<{ redirect_uris: z.ZodDefault<z.ZodArray<z.ZodString>>; scopes: z.ZodArray<z.ZodString>; }, z.core.$strip>; privacy: z.ZodOptional<z.ZodObject<{ url: z.ZodString; }, z.core.$strip>>; billing: z.ZodOptional<z.ZodObject<{ mode: z.ZodEnum<{ managed: 'managed'; api: 'api'; }>; plans: z.ZodDefault<z.ZodArray<z.ZodObject<{ key: z.ZodString; name: z.ZodString; description: z.ZodOptional<z.ZodString>; interval: z.ZodDefault<z.ZodEnum<{ month: 'month'; year: 'year'; }>>; prices: z.ZodOptional<z.ZodObject<{ usd: z.ZodNumber; gbp: z.ZodNumber; eur: z.ZodNumber; inr: z.ZodNumber; }, z.core.$strict>>; trial_days: z.ZodDefault<z.ZodNumber>; features: z.ZodDefault<z.ZodArray<z.ZodString>>; usage: z.ZodOptional<z.ZodObject<{ cap: z.ZodObject<{ usd: z.ZodNumber; gbp: z.ZodNumber; eur: z.ZodNumber; inr: z.ZodNumber; }, z.core.$strict>; terms: z.ZodString; }, z.core.$strict>>; }, z.core.$strict>>>; }, z.core.$strict>>; webhooks: z.ZodOptional<z.ZodObject<{ url: z.ZodString; events: z.ZodArray<z.ZodString>; }, z.core.$strip>>; extensions: z.ZodOptional<z.ZodObject<{ admin: z.ZodOptional<z.ZodArray<z.ZodObject<{ point: z.ZodEnum<{ "admin.product.detail.sidebar": 'admin.product.detail.sidebar'; "admin.product.detail.action": 'admin.product.detail.action'; "admin.product.index.action": 'admin.product.index.action'; "admin.collection.detail.sidebar": 'admin.collection.detail.sidebar'; "admin.collection.detail.action": 'admin.collection.detail.action'; "admin.order.detail.sidebar": 'admin.order.detail.sidebar'; "admin.order.detail.action": 'admin.order.detail.action'; "admin.orders.index.action": 'admin.orders.index.action'; "admin.customer.detail.sidebar": 'admin.customer.detail.sidebar'; "admin.customer.detail.action": 'admin.customer.detail.action'; "admin.customers.index.action": 'admin.customers.index.action'; "admin.settings.block": 'admin.settings.block'; }>; url: z.ZodString; title: z.ZodString; }, z.core.$strip>>>; storefront: z.ZodOptional<z.ZodArray<z.ZodObject<{ block: z.ZodEnum<{ "storefront.product.below_description": 'storefront.product.below_description'; "storefront.product.above_add_to_cart": 'storefront.product.above_add_to_cart'; "storefront.cart.summary": 'storefront.cart.summary'; "storefront.collection.header": 'storefront.collection.header'; }>; url: z.ZodString; format: z.ZodDefault<z.ZodEnum<{ components: 'components'; html: 'html'; }>>; }, z.core.$strip>>>; sections: z.ZodOptional<z.ZodArray<z.ZodObject<{ handle: z.ZodString; name: z.ZodString; description: z.ZodOptional<z.ZodString>; url: z.ZodString; format: z.ZodDefault<z.ZodEnum<{ components: 'components'; html: 'html'; }>>; }, z.core.$strict>>>; }, z.core.$strip>>; settings: z.ZodOptional<z.ZodArray<z.ZodObject<{ key: z.ZodString; label: z.ZodString; help: z.ZodOptional<z.ZodString>; type: z.ZodEnum<{ boolean: 'boolean'; secret: 'secret'; url: 'url'; single_line_text: 'single_line_text'; multi_line_text: 'multi_line_text'; integer: 'integer'; decimal: 'decimal'; color: 'color'; select: 'select'; }>; required: z.ZodDefault<z.ZodBoolean>; default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>; options: z.ZodOptional<z.ZodArray<z.ZodObject<{ value: z.ZodString; label: z.ZodString; }, z.core.$strict>>>; }, z.core.$strict>>>; app_proxy: z.ZodOptional<z.ZodObject<{ url: z.ZodString; }, z.core.$strict>>; automations: z.ZodOptional<z.ZodObject<{ triggers: z.ZodDefault<z.ZodArray<z.ZodObject<{ key: z.ZodString; label: z.ZodString; description: z.ZodOptional<z.ZodString>; fields: z.ZodDefault<z.ZodArray<z.ZodObject<{ key: z.ZodString; label: z.ZodString; type: z.ZodEnum<{ number: 'number'; boolean: 'boolean'; text: 'text'; date: 'date'; url: 'url'; }>; }, z.core.$strict>>>; }, z.core.$strict>>>; actions: z.ZodDefault<z.ZodArray<z.ZodObject<{ key: z.ZodString; label: z.ZodString; description: z.ZodOptional<z.ZodString>; url: z.ZodString; fields: z.ZodDefault<z.ZodArray<z.ZodObject<{ key: z.ZodString; label: z.ZodString; type: z.ZodEnum<{ number: 'number'; boolean: 'boolean'; text: 'text'; url: 'url'; multi_line_text: 'multi_line_text'; select: 'select'; }>; required: z.ZodDefault<z.ZodBoolean>; options: z.ZodOptional<z.ZodArray<z.ZodObject<{ value: z.ZodString; label: z.ZodString; }, z.core.$strict>>>; }, z.core.$strict>>>; }, z.core.$strict>>>; }, z.core.$strict>>; checkout: z.ZodOptional<z.ZodObject<{ delivery_rates: z.ZodOptional<z.ZodObject<{ url: z.ZodString; }, z.core.$strict>>; }, z.core.$strict>>; runtime: z.ZodOptional<z.ZodObject<{ main: z.ZodDefault<z.ZodString>; egress: z.ZodDefault<z.ZodArray<z.ZodString>>; bundle: z.ZodOptional<z.ZodString>; }, z.core.$strict>>; knowledge: z.ZodOptional<z.ZodObject<{ url: z.ZodString; attributes: z.ZodDefault<z.ZodArray<z.ZodString>>; relations: z.ZodDefault<z.ZodArray<z.ZodString>>; scopes: z.ZodDefault<z.ZodArray<z.ZodString>>; }, z.core.$strip>>; metafields: z.ZodOptional<z.ZodObject<{ definitions: z.ZodArray<z.ZodObject<{ owner_type: z.ZodEnum<{ product: 'product'; variant: 'variant'; collection: 'collection'; customer: 'customer'; order: 'order'; store: 'store'; }>; key: z.ZodString; name: z.ZodString; description: z.ZodOptional<z.ZodString>; type: z.ZodEnum<{ boolean: 'boolean'; date: 'date'; url: 'url'; single_line_text: 'single_line_text'; multi_line_text: 'multi_line_text'; integer: 'integer'; decimal: 'decimal'; }>; is_list: z.ZodDefault<z.ZodBoolean>; storefront_visible: z.ZodDefault<z.ZodBoolean>; }, z.core.$strip>>; scopes: z.ZodDefault<z.ZodArray<z.ZodString>>; }, z.core.$strip>>; mcp: z.ZodOptional<z.ZodObject<{ tools: z.ZodOptional<z.ZodArray<z.ZodObject<{ name: z.ZodString; description: z.ZodString; url: z.ZodString; scopes: z.ZodDefault<z.ZodArray<z.ZodString>>; input_schema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>; }, z.core.$strip>>>; }, z.core.$strip>>; }, z.core.$strict>WITHHELD_FROM_APPS
Scopes no app may hold, whatever it asks for.
const WITHHELD_FROM_APPS: readonly string[]