AppSpec and runtime
An app is data around views. Nothing executable or secret belongs in the spec: resources and actions name host capabilities, policies describe intent, and structured bindings connect them to view props.
import {defineApp} from '@zuilib/apps'
export const revenueApp = defineApp({
version: 1,
id: 'revenue-ops',
name: 'Revenue operations',
pages: [{
id: 'overview',
title: 'Overview',
path: '/',
view: revenueOverview,
}, {
id: 'approvals',
title: 'Approvals',
path: '/approvals',
policies: ['finance-approver'],
view: approvalsView,
}],
resources: [{
id: 'pipeline',
name: 'Certified revenue model',
kind: 'query',
params: {region: {$bind: {source: 'state', ref: 'region'}}},
cache: {staleTimeMs: 30_000},
policies: ['app-user'],
}],
variables: [
{name: 'region', scope: 'app', valueType: 'string', initial: 'All'},
{name: 'search', scope: 'url', valueType: 'string', initial: ''},
],
actions: [{
id: 'approve-adjustment',
name: 'Approve adjustment',
kind: 'approval',
policies: ['finance-approver'],
confirmation: {mode: 'type', phrase: 'APPROVE'},
onSuccess: [
{type: 'refresh-resource', target: 'approvals'},
{type: 'notify', message: 'Adjustment approved'},
],
onError: [{type: 'notify', message: 'Approval failed'}],
audit: true,
}],
policies: [{
id: 'finance-approver',
level: 'view',
scope: 'page',
target: 'approvals',
subjects: {groups: ['finance']},
}],
})
Inside a page, a resource binding remains JSON:
{
id: 'pipeline-grid',
type: 'data-grid',
props: {
rows: {$bind: {source: 'resource', ref: 'pipeline'}},
onRowClick: {$action: 'open-opportunity'},
},
}
Attach host capabilities
Adapters are the only executable boundary. A production host can connect a warehouse, semantic layer, policy service and event pipeline without changing the document format.
const adapters: AppRuntimeAdapters = {
resources: ({resource, params, environment, signal}) =>
dataPlatform.run(resource.id, {params, environment, signal}),
actions: ({action, inputs, subject, signal}) =>
actionGateway.execute(action.id, {inputs, subject, signal}),
policies: (policy, context) => authorization.evaluate(policy, context),
state: persistedStateAdapter,
telemetry: productTelemetry,
confirm: (action) => openConfirmation(action.confirmation),
transition: (transition) => applyHostTransition(transition),
}
<AppRuntimeProvider app={app} adapters={adapters} subject={user}>
<PublishedApp registry={registry} />
</AppRuntimeProvider>
Resource executions are cancellable, cache-aware and observable. Actions have input validation, confirmation, retries, idempotency, success/error transitions, policy gates and run history. App, page, URL, session, user and persisted variable scopes provide structured state without placing arbitrary code in the spec.
Unknown node types and bad bindings are contained by the view renderer; app-level validation reports missing references, invalid policy targets, unsafe resources, environment errors and publish blockers before release.