npm.io
3.0.5 • Published 18h agoCLI

@ossy/app

Licence
MIT
Version
3.0.5
Deps
44
Size
157 kB
Vulns
0
Weekly
0

@ossy/app

Server-side rendering runtime and build tooling for Ossy apps.

For custom setups (Next.js, Vite, etc.), use @ossy/connected-components directly.

Setup

Add @ossy/app, react, react-dom, and @ossy/connected-components (plus its peer deps) to your package.json, then run app build.

{
  "scripts": {
    "build": "app build",
    "start": "node build/server.js"
  }
}

Pages

Create *.page.jsx files in src/. Each file becomes a route.

// src/home.page.jsx
export const metadata = {
  id: 'home',
  title: 'Home',
  path: '/',
}

export default function Home({ url }) {
  return (
    <body>
      <h1>Welcome</h1>
      <p>Current URL: {url}</p>
    </body>
  )
}

File → route mapping: home.page.jsx/, about.page.jsx/about. The metadata export controls the route id, path, and page title. For multi-language paths:

export const metadata = {
  id: 'about',
  path: { en: '/about', sv: '/om' },
}

What the framework provides: The build wraps every page automatically with <html>, <head> (including the page title and injected styles), and <App> (providers for theme, router, SDK). Your page component only needs to return the <body> element and its content.

Props: The full app config is passed as props to the page component — url, theme, isAuthenticated, pages, workspaceId, apiUrl, etc. You can also access config via useApp() / useRouter() hooks from @ossy/connected-components and @ossy/router-react.

Build output: Each page produces two self-contained bundles (React included):

  • build/ssr/<id>.mjs — used by the server for SSR on each request
  • build/public/static/<id>.js — loaded by the browser for hydration

Config

Add src/config.js to set workspace, theme, and API options:

import { CloudLight } from '@ossy/themes'

export default {
  workspaceId: 'your-workspace-id',
  theme: CloudLight,
  apiUrl: 'https://api.ossy.se/api/v0',
}

Optional flags:

Key Default Purpose
enablePushInvalidation false App-wide SSE cache invalidation. Prefer <PushInvalidationSubscriber /> on specific pages instead — see @ossy/sdk-react README.

Config is loaded at build time and merged with request-time settings (e.g. user theme preference from cookies).

API routes

Create *.api.js files in src/. Each file exports a metadata object and a default handler function.

// src/health.api.js
export const metadata = {
  id: 'health',
  path: '/api/health',
}

export default function handle(req, res) {
  res.json({ status: 'ok' })
}

The handler receives the raw Express req and res. API routes are matched before page rendering. The router supports dynamic segments (e.g. path: '/api/users/:id').

Build output: build/.ossy/api.generated.json — a registry of [{ id, path, module }] entries. Handlers are lazy-imported on first request.

Background tasks (*.task.js)

Task modules export metadata (id, optional triggers / schedule) and a run function. They are registered by TaskService at server startup.

Sync invoke: POST /actions with { action, payload } awaits the primary task at {feature}/tasks/{intent} derived from the action id.

Async: changestream events and cron schedules dispatch matching tasks without blocking the caller.

Build output includes task entries in the manifest for server registration. Async execution does not use a separate polling worker — see ADR 0003.

Task ids use the canonical form @ossy/{feature}/tasks/{intent}. Action ids (@ossy/{feature}/actions/{intent}) map to their primary task via ADR 0003.

Schemas (*.schema.js)

Resource document schemas are POJOs default-exported from *.schema.js in src/ or installed feature packages. The build discovers them, validates canonical ids (@{provider}/{feature}/schema/{concept}), and merges them into manifest.schemas.

// packages/booking/src/service.schema.js
export default {
  id: '@ossy/booking/schema/service',
  name: 'Service',
  fields: [{ name: 'name', type: 'text', required: true }],
}

Forms reference a schema via metadata.schemaId (not templateId).

Forms (*.form.js)

Form modules export intent metadata only — field definitions live on the schema:

export const metadata = {
  id: '@ossy/booking/form/service',
  schemaId: '@ossy/booking/schema/service',
}

Build manifest

app build writes build/manifest.json plus derived catalogs:

Output Purpose
manifest.json Pages, actions, tasks, schemas, components, layouts, taskCatalog, taskGraphEdges, …
build/capabilities.json Automation UI catalog — actions, tasks, schemas, edges
build/actions.schema.json JSON Schema for agent/MCP tool inputs per action

Action and task metadata are validated at build time via @ossy/schema. Task triggers reference schema ids (type: '@ossy/platform/schema/file') and lifecycle events (Created, Patched, …).

Components (*.component.jsx)

Register injectable UI via *.component.jsx in src/ or feature packages. Components are bundled separately and resolved into slots at runtime.

App chrome — the app assigns placement in *.layout.jsx:

export const slots = {
  'app:header': 'app-header',
  'app:sidebar': 'app-sidebar',
}

Resource / input UI — feature packages use metadata.id as the slot key:

export const metadata = { id: '@ossy/booking/form/service' }
export default function ServiceForm() { … }

See docs/component-primitive.md and design-system/docs/SLOTS.md.

Port configuration

The server listens on port 3000 by default.

PORT=4000 node build/server.js
node build/server.js --port 4000
node build/server.js -p 4000