---
description: Use specific OC rules over Vite
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
alwaysApply: false
---
# OpenComponents (OC) Framework Documentation

## Core Principles & Best Practices

- **OC = Presentation Layer**: Server for data prep, not full backend services
- **Keep Frontend Light**: Minimize client-side JS, prefer server-side data prep
- **Library Minimalism**: Resist adding libraries, prefer native APIs
- **Server-First Data Flow**: Server prepares → Client displays → User interacts → Server responds

## Anti-Patterns to Avoid

- Building full CRUD in OC components
- Using OC server as backend replacement
- Heavy client-side state management
- Large components with multiple responsibilities
- Client-side data fetching when server-side works

## When to Use Server vs Client

- **Server-side**: Data fetching, transformations, auth, business logic, initial state
- **Client-side**: User interactions, real-time validation, UI state, progressive enhancement

## Project Structure

OpenComponents is a microfrontend technology that resembles a Vite project but with key differences:

- **No index.html**: This file is automatically generated during development
- **Dual architecture**: Contains both client and server components
- **Entry points**: Defined in package.json under `files.template.src` and `files.data`

## Development Commands

- Run locally with: `oc dev folder-where-to-run [port] [options]`
- Execute from parent folder (not component folder)
- Default port: 3000 (can be overridden)
- Common options:
  - `--components component-name`: Load only specific component(s)
  - `--prefix=/path/`: Simulate registry prefix for production matching
  - `--fallbackRegistryUrl=url`: Fetch missing components from remote registry
- Example: `oc dev .. 3030 --components mycomponent --prefix=/registry/`

## Server Architecture

- Server uses `oc-server` helper package
- Must export a `Server` class instance named `server`
- Server configuration methods:
  - `new Server({ stream: true })`: Enable Promise serialization + Map/Set support
  - `.withParameters()`: Define component parameters (initial props from URL)
  - `.handler()`: Method that returns initial data for client rendering
  - `.actions()`: Define endpoints the microfrontend can call

### Parameters vs Actions

- **Parameters**: Initial props passed via URL query string (like `?userId=1`)
- **Actions**: Server endpoints that the client can call after initial render

### Streaming Mode Benefits

- **Non-blocking rendering**: Return Promises instead of awaiting slow operations
- **Enhanced serialization**: Support for Map, Set, and other complex data types
- **Performance**: Client renders immediately, async data resolves later
- **Trade-off**: Small serialization overhead for enhanced capabilities

## Performance Best Practices

- Use `new Server({stream: true})` for non-blocking rendering
- Return Promises instead of awaiting slow operations
- Keep initial payload small
- Fail fast with `undefined` from handlers
- **Use `ctx.setHeader('Cache-Control', 'max-age=X')` when same params produce same result** - reduces registry load

## Client Architecture

- Root component receives server data as initial props
- **Must use default export** - OC build process wraps your component
- **Type Safety**: `InitialData` and `ActionOutput<'actionName'>` are auto-inferred from server
- **Static Assets**: Import directly from `../public/` - auto-resolved to `staticPath`
- **CSS Modules**: All CSS imports are automatically CSS modules
- **Server Actions**: Call via `serverClient.actionName()` with full type safety
- **Initial Data Access**: Use `getInitialData()` to access server handler data anywhere on the client (not on the server)

## File Structure Guidelines

When working with OpenComponents:

1. **Package.json Configuration**

   ```json
   {
     "oc": {
       "files": {
         "data": "src/server.ts", // Server entry point
         "template": {
           "src": "src/index.tsx", // Client entry point
           "type": "oc-template-react" // Template type
         },
         "static": ["public", "assets"] // Static asset directories (array)
       }
     }
   }
   ```

2. **TypeScript Declaration File (Required)**

   ```typescript
   // src/global.d.ts or similar .d.ts file
   /// <reference types="oc-vite/client" />
   ```

   This provides:

   - CSS module typing for imports
   - `window.oc` object typing with utilities
   - `window.oc.events` for component communication

3. **Template Types Available**

   - `oc-template-react`: React components
   - `oc-template-vue`: Vue components
   - `oc-template-svelte`: Svelte components
   - `oc-template-solid`: Solid.js components
   - `oc-template-preact`: Preact components
   - `oc-template-elm`: Elm components
   - `oc-template-es6`: Vanilla JavaScript

4. **Required Dependencies**
   - Core: `oc`, `oc-server`
   - Template-specific compiler (e.g., `oc-template-react-compiler`)
   - Framework dependencies (e.g., `react`, `react-dom`)
   - Note: `oc-vite` is included as transient dependency

## Code Patterns

### Server Pattern (TypeScript)

```typescript
import { Server } from "oc-server";

export const server = new Server({ stream: true }) // Optional: enables Promise serialization + Map/Set support
  .withParameters({
    paramName: {
      default: "defaultValue",
      description: "Parameter description",
      example: "exampleValue",
      mandatory: true, // or false
      type: "string" | "number" | "boolean", // URL parameters only
    },
  })
  .handler(async (params, ctx) => {
    // params: validated parameters from URL
    // ctx: DataContext with staticPath, requestHeaders, etc.

    // Return undefined to prevent rendering
    if (someErrorCondition) {
      return; // or return undefined
    }

    // With stream: true, you can return Promises for non-blocking rendering
    const slowDataPromise = fetchSlowData(); // Don't await - return Promise

    // Set cache headers when same params produce same result
    ctx.setHeader("Cache-Control", "max-age=300"); // 5 minutes cache

    return {
      // immediate data for initial render
      staticPath: ctx.staticPath,
      // Promise will be resolved on client (with stream: true)
      slowData: slowDataPromise,
      // Enhanced serialization support (with stream: true)
      mapData: new Map([["key", "value"]]),
      setData: new Set([1, 2, 3]),
    };
  })
  .action("actionName", async (params: { paramName: ParamType }) => {
    // Server endpoint callable from client
    // With stream: true, actions can also return Promises and enhanced data types
    return {
      // response data - can include Promises, Maps, Sets when streaming enabled
    };
  });

// TypeScript module augmentation for type safety
declare module "oc-server" {
  interface Register {
    server: typeof server;
  }
}
```

### Client Patterns (React/TypeScript)

```typescript
import { useState, useEffect } from "react";
import {
  serverClient,
  InitialData,
  ActionOutput,
  getInitialData,
} from "oc-server";
import styles from "./styles.css"; // Always CSS modules in OC
import logo from "../public/logo.png"; // Auto-resolved to staticPath

// Type-safe interfaces from server module declaration
type AdditionalData = ActionOutput<"actionName">; // Inferred from server action

// getInitialData() - sync function to access server handler data anywhere
const serverData = getInitialData(); // Returns same data as props, works in any template

// MUST be default export - wrapped by OC build process
const App: React.FC<InitialData> = (props: InitialData) => {
  // InitialData is inferred from server handler return type
  const { serverProp1, serverProp2, slowData } = props; // slowData might be a Promise

  // Handle streaming data (when server uses stream: true)
  const [resolvedSlowData, setResolvedSlowData] = useState(null);

  useEffect(() => {
    if (slowData instanceof Promise) {
      slowData.then(setResolvedSlowData);
    } else {
      setResolvedSlowData(slowData);
    }
  }, [slowData]);

  // Alternative: access server data without props
  const alternativeData = getInitialData();

  const [actionData, setActionData] = useState<AdditionalData | null>(null);
  const [error, setError] = useState("");

  const callServerAction = async () => {
    try {
      // Type-safe server action calls
      const data = await serverClient.actionName({ param: value });
      setActionData(data);
    } catch (err) {
      setError(String(err)); // Handle action errors
    }
  };

  return (
    <div className={styles.container}>
      <img src={logo} alt="Static asset" />
      {resolvedSlowData ? <div>{resolvedSlowData}</div> : <div>Loading...</div>}
      {/* Component JSX */}
    </div>
  );
};

export default App; // Required: Must be default export
```

## Important Notes

- OpenComponents is a microfrontend architecture with registry-based distribution
- Components are published with name/version and served at runtime
- **TypeScript Setup**: Add `/// <reference types="oc-vite/client" />` to a .d.ts file for proper typing
- **Component Communication**: Use `window.oc.events` for inter-component communication

  `window.oc.events` provides the following methods:

  - `on(eventName, handler)`: Register a listener for an event.
  - `off(eventName, handler?)`: Remove a specific listener or all listeners for an event.
  - `fire(eventName, data?)`: Emit an event with optional data payload.
  - `reset()`: Remove all registered event listeners.

- **Local Development**: `oc dev` simulates an OC Registry (like npm for components)
- **Static assets**: Auto-resolved using Vite's `experimental.renderBuiltUrl`
- **CSS Modules**: All CSS imports are automatically CSS modules
- **Default Export Required**: Client components must use default export for OC build wrapper
- **Type Safety**: Full TypeScript inference from server to client via module declaration
- **Initial Data**: Access server handler data via props OR `getInitialData()` (works in any template)
- **Parameters**: Only `string`, `number`, `boolean` types (passed via URL)
- **Error handling**: Return `undefined` from handler to prevent rendering
- **Streaming Mode**: Use `new Server({ stream: true })` for Promise serialization and enhanced data types
- **Non-blocking Rendering**: Return Promises from handler/actions instead of awaiting slow operations
- **Action Errors**: Use try-catch blocks for server action error handling
- **Build Commands**: `oc package` or `oc publish` (includes package) for production builds
- **Component usage**: `<oc-component src="registry-url/component/version?props" />`
- **Fallback Registry**: Local dev can fetch missing components from remote registry
- **Multi-component**: Use `--components` flag to load specific components only

## DataContext Interface

The handler's `ctx` parameter provides:

- `staticPath`: Path to static assets (`/name/version/static`)
- `requestHeaders`: HTTP request headers
- `requestIp`: Client IP address
- `baseUrl`: Component base URL
- `acceptLanguage`: Client language preferences
- `setHeader()`: Set response headers
- `setEmptyResponse()`: Set empty response
- `env`: Environment variables
- `params`: Validated parameters
- `plugins`: Available plugins
- `templates`: Available templates

### Using setHeader() for Response Control

The `setHeader()` method allows you to set HTTP response headers before the component is rendered. This is particularly useful for:

- **Caching**: Set cache headers to reduce registry load and improve performance
- **Security**: Set security headers like CSP, X-Frame-Options, etc.
- **Content Type**: Override default content type if needed
- **Custom Headers**: Set any custom headers for your use case

#### Caching Example

```typescript
.handler(async (params, ctx) => {
  // If the same parameters will produce the same result for a period of time,
  // you can cache the response to reduce registry load
  ctx.setHeader('Cache-Control', 'max-age=600'); // Cache for 10 minutes

  // Your handler logic here
  return { data: "cached response" };
});
```

This is especially useful when:

- Component data doesn't change frequently for the same parameters
- You want to reduce load on your backend services
- You want to improve performance for repeated requests
- The component output is deterministic for given parameters

## Remember

Server-rendered microfrontends. Keep it simple, let server do heavy lifting.
