npm package: @netgen/storyblok-siteaccess
Description: Generic siteaccess management for multi-market, multi-language Storyblok websites
Latest version: 0.1.11

# @netgen/storyblok-siteaccess

Generic siteaccess management for multi-market, multi-language Storyblok websites.

## Features

- **Generic & Type-safe**: Works with any market/language structure using TypeScript generics
- **URL Generation**: Convert between Storyblok paths, URLs, and pathnames
- **Multi-market Support**: Handle multiple markets with different languages
- **Alternate Language Management**: Generate language/market alternatives for SEO
- **Framework Agnostic Core**: Works with any framework, with optional React hooks
- **Environment Management**: Support for different environments (dev, prod, etc.)
- **Next.js Integration**: Built-in utilities for Next.js routing and redirects
- **Internationalization**: Generate next-intl routing configurations automatically

## Installation

```bash
npm install @netgen/storyblok-siteaccess
```

## Basic Usage

### 1. Define Your Types

```typescript
// Your project types
export enum Market {
  us = 'us',
  uk = 'uk',
  de = 'de'
}

export enum Language {
  en = 'en',
  de = 'de'
}

// Type your configurations
export type ProjectSiteaccessConfig = SiteaccessConfig<Market, Language>;
export type ProjectEnvironmentConfig = EnvironmentConfig<Market, Language>;
```

### 2. Create Configuration

```typescript
import { SiteaccessResolver, Siteaccess } from '@netgen/storyblok-siteaccess';

const environmentConfig: ProjectEnvironmentConfig = {
  siteaccess: [
    {
      host: 'example.com',
      locale: 'en',
      market: Market.us,
      language: Language.en,
      storyblokFolderPrefix: 'us',
      storyblokVisualEditorPrefix: 'us',
    },
    {
      host: 'example.de',
      locale: 'de',
      market: Market.de,
      language: Language.de,
      storyblokFolderPrefix: 'de',
      storyblokVisualEditorPrefix: 'de',
    }
  ],
  marketConfigs: {
    [Market.us]: {
      primaryLanguage: Language.en,
      tosStoryblokPath: 'terms-of-service'
    },
    [Market.de]: {
      primaryLanguage: Language.de,
      tosStoryblokPath: 'nutzungsbedingungen'
    }
  },
  siteinfoIds: {
    [Market.us]: 'us-siteinfo-id',
    [Market.de]: 'de-siteinfo-id'
  },
  partnersFolderStoryblokPrefix: 'partners',
  hosts: [
    { host: 'example.com', defaultLocale: 'en' },
    { host: 'example.de', defaultLocale: 'de' }
  ]
};

// Create resolver
const siteaccessResolver = new SiteaccessResolver(
  environmentConfig.siteaccess,
  environmentConfig
);
```

### 3. Use Siteaccess

```typescript
// Resolve configuration
const config = siteaccessResolver.resolveByLocale({ locale: 'en' });

// Create siteaccess instance
const siteaccess = new Siteaccess(config, environmentConfig);

// Generate URLs
const url = siteaccess.getUrl({ storyblokPath: 'about' });
// Result: 'https://example.com/about'

const storyblokPath = siteaccess.getStoryblokPath({ pathname: '/about' });
// Result: 'us/about'
```

## React Integration

### Basic Hook Usage

```typescript
import { useSiteaccess } from '@netgen/storyblok-siteaccess';

function MyComponent() {
  const siteaccess = useSiteaccess(siteaccessConfig, environmentConfig);
  
  const aboutUrl = siteaccess.getUrl({ storyblokPath: 'about' });
  
  return <a href={aboutUrl}>About Us</a>;
}
```

### Create Project-Specific Hook

```typescript
import { createSiteaccessHook } from '@netgen/storyblok-siteaccess';

// Create your typed hook
export const useProjectSiteaccess = createSiteaccessHook<Market, Language>(
  () => getCurrentSiteaccessConfig(),
  () => environmentConfig
);

// Use in components
function MyComponent() {
  const siteaccess = useProjectSiteaccess();
  // Fully typed with your Market and Language enums
}
```

## Advanced Features

### Market Alternates

```typescript
const alternates = siteaccess.getMarketAlternates(
  Object.values(Market), // All available markets
  { storyblokAlternates: story.alternates }
);

// Result: Array of alternate URLs for different markets
```

### Language Validation

```typescript
const isValid = siteaccess.isStoryLanguageTranslationValid(story);
// Checks if the current language has a valid translation
```

### Storyblok API Integration

```typescript
const storyblokLanguage = siteaccess.getStoryblokLanguage();
// Returns 'default' for primary language, actual language code otherwise

const apiLanguage = siteaccess.getLanguageForStoryblok();
// Returns undefined for primary language, language code otherwise
```

## Client/Server Resolution

### Next.js Example

```typescript
import { 
  createLocaleBasedClientResolver,
  createLocaleBasedServerResolver,
  createUniversalSiteaccessResolver 
} from '@netgen/storyblok-siteaccess';
import { useLocale } from 'next-intl';
import { getLocale } from 'next-intl/server';

// Create resolvers
const clientResolver = createLocaleBasedClientResolver(
  useLocale,
  (locale) => siteaccessResolver.resolveByLocale({ locale })
);

const serverResolver = createLocaleBasedServerResolver(
  getLocale,
  (locale) => siteaccessResolver.resolveByLocale({ locale })
);

const universalResolver = createUniversalSiteaccessResolver(
  clientResolver,
  serverResolver
);

// Use in your app
export const getCurrentSiteaccessConfig = {
  client: universalResolver.client,
  server: universalResolver.server
};
```

## API Reference

### Classes

- **`Siteaccess<TMarket, TLanguage>`**: Main class for URL generation and path conversion
- **`SiteaccessResolver<TMarket, TLanguage>`**: Resolves siteaccess configurations based on various criteria

### Types

- **`SiteaccessConfig<TMarket, TLanguage>`**: Configuration for a single siteaccess
- **`EnvironmentConfig<TMarket, TLanguage>`**: Complete environment configuration
- **`AlternateStory<TMarket, TLanguage>`**: Language/market alternate information
- **`SiteaccessInputType`**: Input union type for path conversion methods

### Hooks

- **`useSiteaccess()`**: React hook for creating Siteaccess instances
- **`createSiteaccessHook()`**: Factory for creating project-specific hooks

### Resolvers

- **`createClientSiteaccessResolver()`**: Create client-side resolver
- **`createServerSiteaccessResolver()`**: Create server-side resolver
- **`createUniversalSiteaccessResolver()`**: Create universal resolver

## Migration from Project-Specific Implementation

1. **Extract your types**: Move Market/Language enums to your project
2. **Update imports**: Import from `@netgen/storyblok-siteaccess`
3. **Add type parameters**: Add `<Market, Language>` to class instantiations
4. **Update configurations**: Move environment configs to your project
5. **Create typed hooks**: Use `createSiteaccessHook()` for React integration

## Next.js Integration

### Automatic Routing Configuration

Generate Next.js internationalization routing from your environment config:

```typescript
import { generateNextIntlRouting } from '@netgen/storyblok-siteaccess';
import { defineRouting } from 'next-intl/routing';

// Generate routing configuration
const routingConfig = generateNextIntlRouting(environmentConfig, {
  defaultLocale: 'en',
  alternateLinks: false,
  localeCookie: false,
  localePrefix: { mode: 'always' }
});

export const routing = defineRouting(routingConfig);
```

### Storyblok Visual Editor Redirects

Generate redirects for Storyblok's visual editor:

```typescript
import { generateStoryblokRedirects } from '@netgen/storyblok-siteaccess';

// Generate redirects
export const storyblokRedirects = generateStoryblokRedirects(environmentConfig, {
  permanent: false
});

// Use in next.config.js
module.exports = {
  async redirects() {
    return storyblokRedirects;
  }
};
```

### Utility Functions

```typescript
import {
  getLocales,
  getHosts,
  getDomainConfig,
  isValidLocale,
  getDefaultLocaleForHost
} from '@netgen/storyblok-siteaccess';

// Get all locales
const locales = getLocales(environmentConfig);

// Check if locale is valid
const isValid = isValidLocale(environmentConfig, 'en-US');

// Get domain configuration
const domainConfig = getDomainConfig(environmentConfig, 'example.com');
```

### Integration with next-intl

```typescript
import { isValidLocale } from '@netgen/storyblok-siteaccess';
import { getRequestConfig } from 'next-intl/server';

export default getRequestConfig(async ({ requestLocale }) => {
  const locale = await requestLocale;

  // Validate locale using the package utility
  if (!isValidLocale(environmentConfig, locale)) {
    // Handle invalid locale
    return { locale: 'en', messages: {} };
  }

  return {
    locale,
    messages: (await import(`./messages/${locale}.json`)).default
  };
});
```

## API Reference

### Utility Functions

- **`generateNextIntlRouting(config, options?)`**: Generate Next.js internationalization routing
- **`generateStoryblokRedirects(config, options?)`**: Generate Storyblok visual editor redirects
- **`getLocales(config)`**: Extract all unique locales
- **`getHosts(config)`**: Extract all unique hosts
- **`getDomainConfig(config, host)`**: Get domain configuration for specific host
- **`isValidLocale(config, locale)`**: Check if locale exists in configuration
- **`getDefaultLocaleForHost(config, host)`**: Get default locale for specific host

### Types

- **`NextRoutingConfig`**: Next.js routing configuration interface
- **`NextRedirectConfig`**: Next.js redirect configuration interface
- **`NextDomainConfig`**: Next.js domain configuration interface

## License

ISC