1.0.0 • Published 11 months ago

@mosaicjs/core v1.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

@mosaicjs/core

Framework-agnostic SSR core for Nanoservice-ts - Inertia.js style development experience.

Overview

@mosaicjs/core provides the framework-agnostic protocol logic for MosaicJS, enabling server-side rendering with any frontend framework. It handles the core request/response cycle, component resolution, and data flow patterns that make the Inertia.js development experience possible.

Installation

npm install @mosaicjs/core

Core Concepts

SSRRenderer Interface

The heart of MosaicJS is the SSRRenderer interface that each framework must implement:

interface SSRRenderer {
  render(component: string, props: any, url: string): Promise<{ html: string }>;
  hasComponent(componentName: string): Promise<boolean>;
  getAssets(): { css: string[]; js: string[] };
  initialize(): Promise<void>;
  close(): Promise<void>;
}

MosaicCore Class

The MosaicCore class handles all framework-agnostic logic:

import { MosaicCore, type SSRRenderer } from '@mosaicjs/core';

const core = new MosaicCore(renderer, {
  assetsVersion: "1.0.0",
  isDev: process.env.NODE_ENV !== "production"
});

// Handle requests
const response = await core.handle(ctx, inputs);

BaseMosaic Class

Extend BaseMosaic to create framework-specific nodes:

import { BaseMosaic, type SSRRenderer } from '@mosaicjs/core';

export default class ReactMosaicNode extends BaseMosaic {
  constructor() {
    const renderer = new ReactSSRRenderer();
    super(renderer, { assetsVersion: "1.0.0" });
  }
}

Configuration

MosaicConfig

interface MosaicConfig {
  assetsVersion?: string;
  isDev?: boolean;
  componentResolver?: (path: string) => string;
  sharedPropsResolver?: (ctx: Context) => Record<string, any>;
  titleResolver?: (component: string, props: any) => string;
}

Component Resolution

The default component resolver follows Inertia.js conventions:

import { deriveComponentFromPath } from '@mosaicjs/core';

// URL to component mapping
deriveComponentFromPath('/') // → 'Home'
deriveComponentFromPath('/about') // → 'About'
deriveComponentFromPath('/dashboard/analytics') // → 'DashboardAnalytics'
deriveComponentFromPath('/error/404') // → 'Error'

Shared Props

Configure global data available to all pages:

import { configureSharedProps } from '@mosaicjs/core';

configureSharedProps((ctx) => ({
  auth: { user: ctx.request.user || null },
  flash: { success: ctx.request.session?.flash?.success || null }
}));

Protocol Details

Request Headers

  • X-Mosaic: true - Indicates XHR request for JSON response
  • X-Mosaic-Version: 1.0.0 - Asset version for cache busting
  • X-Mosaic-Partial-Data: prop1,prop2 - Request specific props only
  • X-Mosaic-Partial-Component: ComponentName - Component for partial data

Response Format

HTML Response (Initial Load):

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
    <link rel="stylesheet" href="/assets/app.css">
  </head>
  <body>
    <div id="app" data-page='{"component":"Home","props":{...},"url":"/","version":"1.0.0"}'>
      <!-- SSR content -->
    </div>
    <script type="module" src="/assets/app.js"></script>
  </body>
</html>

JSON Response (Navigation):

{
  "component": "About",
  "props": { "auth": {...}, "pageData": {...} },
  "url": "/about",
  "version": "1.0.0",
  "_headers": {
    "X-Mosaic": "true",
    "Vary": "X-Mosaic"
  }
}

Framework Implementation

To create a framework adapter:

  1. Implement SSRRenderer:
class MyFrameworkSSRRenderer implements SSRRenderer {
  async render(component: string, props: any, url: string) {
    // Framework-specific SSR logic
    return { html: renderedHtml };
  }
  
  async hasComponent(componentName: string) {
    // Check if component exists
    return true;
  }
  
  getAssets() {
    // Return CSS and JS assets
    return { css: [], js: [] };
  }
  
  async initialize() {
    // Setup framework
  }
  
  async close() {
    // Cleanup
  }
}
  1. Create Node Class:
export default class MyFrameworkMosaicNode extends BaseMosaic {
  constructor() {
    super(new MyFrameworkSSRRenderer());
  }
}
  1. Export Package:
export { default as MosaicNode } from './MyFrameworkMosaicNode';
export * from '@mosaicjs/core';

Utilities

Component Resolution

import { deriveComponentFromPath, toPascalCase } from '@mosaicjs/core';

Props Management

import { getSharedProps, mergeProps, configureSharedProps } from '@mosaicjs/core';

Error Handling

The core handles errors gracefully:

  • Version Mismatch (409): Asset version conflicts
  • Component Not Found (404): Missing components
  • SSR Errors (500): Fallback to client-side rendering
  • Validation Errors: Input schema validation

Development vs Production

The core automatically detects environment:

const config = {
  isDev: process.env.NODE_ENV !== "production",
  assetsVersion: process.env.ASSETS_VERSION || "1.0.0"
};

TypeScript Support

Full TypeScript support with strict typing:

import type { 
  SSRRenderer, 
  MosaicConfig, 
  PageData, 
  MosaicNodeInputs 
} from '@mosaicjs/core';

License

MIT