@netgen/storyblok-siteaccess 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +350 -0
- package/dist/classes/Siteaccess.d.ts +82 -0
- package/dist/classes/Siteaccess.js +245 -0
- package/dist/classes/Siteaccess.js.map +1 -0
- package/dist/classes/SiteaccessResolver.d.ts +51 -0
- package/dist/classes/SiteaccessResolver.js +95 -0
- package/dist/classes/SiteaccessResolver.js.map +1 -0
- package/dist/classes/index.d.ts +2 -0
- package/dist/classes/index.js +3 -0
- package/dist/classes/index.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useSiteaccess.d.ts +9 -0
- package/dist/hooks/useSiteaccess.js +11 -0
- package/dist/hooks/useSiteaccess.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/resolvers/client.d.ts +12 -0
- package/dist/resolvers/client.js +18 -0
- package/dist/resolvers/client.js.map +1 -0
- package/dist/resolvers/index.d.ts +3 -0
- package/dist/resolvers/index.js +4 -0
- package/dist/resolvers/index.js.map +1 -0
- package/dist/resolvers/server.d.ts +12 -0
- package/dist/resolvers/server.js +18 -0
- package/dist/resolvers/server.js.map +1 -0
- package/dist/resolvers/universal.d.ts +10 -0
- package/dist/resolvers/universal.js +11 -0
- package/dist/resolvers/universal.js.map +1 -0
- package/dist/types/augmentation.d.ts +12 -0
- package/dist/types/augmentation.js +2 -0
- package/dist/types/augmentation.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/siteaccess.d.ts +91 -0
- package/dist/types/siteaccess.js +2 -0
- package/dist/types/siteaccess.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/routing.d.ts +19 -0
- package/dist/utils/routing.js +45 -0
- package/dist/utils/routing.js.map +1 -0
- package/package.json +82 -0
package/README.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# @netgen/storyblok-siteaccess
|
|
2
|
+
|
|
3
|
+
Generic siteaccess management for multi-market, multi-language Storyblok websites.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Generic & Type-safe**: Works with any market/language structure using TypeScript generics
|
|
8
|
+
- **URL Generation**: Convert between Storyblok paths, URLs, and pathnames
|
|
9
|
+
- **Multi-market Support**: Handle multiple markets with different languages
|
|
10
|
+
- **Alternate Language Management**: Generate language/market alternatives for SEO
|
|
11
|
+
- **Framework Agnostic Core**: Works with any framework, with optional React hooks
|
|
12
|
+
- **Environment Management**: Support for different environments (dev, prod, etc.)
|
|
13
|
+
- **Next.js Integration**: Built-in utilities for Next.js routing and redirects
|
|
14
|
+
- **Internationalization**: Generate next-intl routing configurations automatically
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @netgen/storyblok-siteaccess
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Basic Usage
|
|
23
|
+
|
|
24
|
+
### 1. Define Your Types
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Your project types
|
|
28
|
+
export enum Market {
|
|
29
|
+
us = 'us',
|
|
30
|
+
uk = 'uk',
|
|
31
|
+
de = 'de'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export enum Language {
|
|
35
|
+
en = 'en',
|
|
36
|
+
de = 'de'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Type your configurations
|
|
40
|
+
export type ProjectSiteaccessConfig = SiteaccessConfig<Market, Language>;
|
|
41
|
+
export type ProjectEnvironmentConfig = EnvironmentConfig<Market, Language>;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Create Configuration
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { SiteaccessResolver, Siteaccess } from '@netgen/storyblok-siteaccess';
|
|
48
|
+
|
|
49
|
+
const environmentConfig: ProjectEnvironmentConfig = {
|
|
50
|
+
siteaccess: [
|
|
51
|
+
{
|
|
52
|
+
host: 'example.com',
|
|
53
|
+
locale: 'en',
|
|
54
|
+
market: Market.us,
|
|
55
|
+
language: Language.en,
|
|
56
|
+
storyblokFolderPrefix: 'us',
|
|
57
|
+
storyblokVisualEditorPrefix: 'us',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
host: 'example.de',
|
|
61
|
+
locale: 'de',
|
|
62
|
+
market: Market.de,
|
|
63
|
+
language: Language.de,
|
|
64
|
+
storyblokFolderPrefix: 'de',
|
|
65
|
+
storyblokVisualEditorPrefix: 'de',
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
marketConfigs: {
|
|
69
|
+
[Market.us]: {
|
|
70
|
+
primaryLanguage: Language.en,
|
|
71
|
+
tosStoryblokPath: 'terms-of-service'
|
|
72
|
+
},
|
|
73
|
+
[Market.de]: {
|
|
74
|
+
primaryLanguage: Language.de,
|
|
75
|
+
tosStoryblokPath: 'nutzungsbedingungen'
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
siteinfoIds: {
|
|
79
|
+
[Market.us]: 'us-siteinfo-id',
|
|
80
|
+
[Market.de]: 'de-siteinfo-id'
|
|
81
|
+
},
|
|
82
|
+
partnersFolderStoryblokPrefix: 'partners',
|
|
83
|
+
hosts: [
|
|
84
|
+
{ host: 'example.com', defaultLocale: 'en' },
|
|
85
|
+
{ host: 'example.de', defaultLocale: 'de' }
|
|
86
|
+
]
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Create resolver
|
|
90
|
+
const siteaccessResolver = new SiteaccessResolver(
|
|
91
|
+
environmentConfig.siteaccess,
|
|
92
|
+
environmentConfig
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 3. Use Siteaccess
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Resolve configuration
|
|
100
|
+
const config = siteaccessResolver.resolveByLocale({ locale: 'en' });
|
|
101
|
+
|
|
102
|
+
// Create siteaccess instance
|
|
103
|
+
const siteaccess = new Siteaccess(config, environmentConfig);
|
|
104
|
+
|
|
105
|
+
// Generate URLs
|
|
106
|
+
const url = siteaccess.getUrl({ storyblokPath: 'about' });
|
|
107
|
+
// Result: 'https://example.com/about'
|
|
108
|
+
|
|
109
|
+
const storyblokPath = siteaccess.getStoryblokPath({ pathname: '/about' });
|
|
110
|
+
// Result: 'us/about'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## React Integration
|
|
114
|
+
|
|
115
|
+
### Basic Hook Usage
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { useSiteaccess } from '@netgen/storyblok-siteaccess';
|
|
119
|
+
|
|
120
|
+
function MyComponent() {
|
|
121
|
+
const siteaccess = useSiteaccess(siteaccessConfig, environmentConfig);
|
|
122
|
+
|
|
123
|
+
const aboutUrl = siteaccess.getUrl({ storyblokPath: 'about' });
|
|
124
|
+
|
|
125
|
+
return <a href={aboutUrl}>About Us</a>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Create Project-Specific Hook
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { createSiteaccessHook } from '@netgen/storyblok-siteaccess';
|
|
133
|
+
|
|
134
|
+
// Create your typed hook
|
|
135
|
+
export const useProjectSiteaccess = createSiteaccessHook<Market, Language>(
|
|
136
|
+
() => getCurrentSiteaccessConfig(),
|
|
137
|
+
() => environmentConfig
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Use in components
|
|
141
|
+
function MyComponent() {
|
|
142
|
+
const siteaccess = useProjectSiteaccess();
|
|
143
|
+
// Fully typed with your Market and Language enums
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Advanced Features
|
|
148
|
+
|
|
149
|
+
### Market Alternates
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const alternates = siteaccess.getMarketAlternates(
|
|
153
|
+
Object.values(Market), // All available markets
|
|
154
|
+
{ storyblokAlternates: story.alternates }
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Result: Array of alternate URLs for different markets
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Language Validation
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const isValid = siteaccess.isStoryLanguageTranslationValid(story);
|
|
164
|
+
// Checks if the current language has a valid translation
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Storyblok API Integration
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const storyblokLanguage = siteaccess.getStoryblokLanguage();
|
|
171
|
+
// Returns 'default' for primary language, actual language code otherwise
|
|
172
|
+
|
|
173
|
+
const apiLanguage = siteaccess.getLanguageForStoryblok();
|
|
174
|
+
// Returns undefined for primary language, language code otherwise
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Client/Server Resolution
|
|
178
|
+
|
|
179
|
+
### Next.js Example
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import {
|
|
183
|
+
createLocaleBasedClientResolver,
|
|
184
|
+
createLocaleBasedServerResolver,
|
|
185
|
+
createUniversalSiteaccessResolver
|
|
186
|
+
} from '@netgen/storyblok-siteaccess';
|
|
187
|
+
import { useLocale } from 'next-intl';
|
|
188
|
+
import { getLocale } from 'next-intl/server';
|
|
189
|
+
|
|
190
|
+
// Create resolvers
|
|
191
|
+
const clientResolver = createLocaleBasedClientResolver(
|
|
192
|
+
useLocale,
|
|
193
|
+
(locale) => siteaccessResolver.resolveByLocale({ locale })
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const serverResolver = createLocaleBasedServerResolver(
|
|
197
|
+
getLocale,
|
|
198
|
+
(locale) => siteaccessResolver.resolveByLocale({ locale })
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const universalResolver = createUniversalSiteaccessResolver(
|
|
202
|
+
clientResolver,
|
|
203
|
+
serverResolver
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Use in your app
|
|
207
|
+
export const getCurrentSiteaccessConfig = {
|
|
208
|
+
client: universalResolver.client,
|
|
209
|
+
server: universalResolver.server
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## API Reference
|
|
214
|
+
|
|
215
|
+
### Classes
|
|
216
|
+
|
|
217
|
+
- **`Siteaccess<TMarket, TLanguage>`**: Main class for URL generation and path conversion
|
|
218
|
+
- **`SiteaccessResolver<TMarket, TLanguage>`**: Resolves siteaccess configurations based on various criteria
|
|
219
|
+
|
|
220
|
+
### Types
|
|
221
|
+
|
|
222
|
+
- **`SiteaccessConfig<TMarket, TLanguage>`**: Configuration for a single siteaccess
|
|
223
|
+
- **`EnvironmentConfig<TMarket, TLanguage>`**: Complete environment configuration
|
|
224
|
+
- **`AlternateStory<TMarket, TLanguage>`**: Language/market alternate information
|
|
225
|
+
- **`SiteaccessInputType`**: Input union type for path conversion methods
|
|
226
|
+
|
|
227
|
+
### Hooks
|
|
228
|
+
|
|
229
|
+
- **`useSiteaccess()`**: React hook for creating Siteaccess instances
|
|
230
|
+
- **`createSiteaccessHook()`**: Factory for creating project-specific hooks
|
|
231
|
+
|
|
232
|
+
### Resolvers
|
|
233
|
+
|
|
234
|
+
- **`createClientSiteaccessResolver()`**: Create client-side resolver
|
|
235
|
+
- **`createServerSiteaccessResolver()`**: Create server-side resolver
|
|
236
|
+
- **`createUniversalSiteaccessResolver()`**: Create universal resolver
|
|
237
|
+
|
|
238
|
+
## Migration from Project-Specific Implementation
|
|
239
|
+
|
|
240
|
+
1. **Extract your types**: Move Market/Language enums to your project
|
|
241
|
+
2. **Update imports**: Import from `@netgen/storyblok-siteaccess`
|
|
242
|
+
3. **Add type parameters**: Add `<Market, Language>` to class instantiations
|
|
243
|
+
4. **Update configurations**: Move environment configs to your project
|
|
244
|
+
5. **Create typed hooks**: Use `createSiteaccessHook()` for React integration
|
|
245
|
+
|
|
246
|
+
## Next.js Integration
|
|
247
|
+
|
|
248
|
+
### Automatic Routing Configuration
|
|
249
|
+
|
|
250
|
+
Generate Next.js internationalization routing from your environment config:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { generateNextIntlRouting } from '@netgen/storyblok-siteaccess';
|
|
254
|
+
import { defineRouting } from 'next-intl/routing';
|
|
255
|
+
|
|
256
|
+
// Generate routing configuration
|
|
257
|
+
const routingConfig = generateNextIntlRouting(environmentConfig, {
|
|
258
|
+
defaultLocale: 'en',
|
|
259
|
+
alternateLinks: false,
|
|
260
|
+
localeCookie: false,
|
|
261
|
+
localePrefix: { mode: 'always' }
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
export const routing = defineRouting(routingConfig);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Storyblok Visual Editor Redirects
|
|
268
|
+
|
|
269
|
+
Generate redirects for Storyblok's visual editor:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { generateStoryblokRedirects } from '@netgen/storyblok-siteaccess';
|
|
273
|
+
|
|
274
|
+
// Generate redirects
|
|
275
|
+
export const storyblokRedirects = generateStoryblokRedirects(environmentConfig, {
|
|
276
|
+
permanent: false
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Use in next.config.js
|
|
280
|
+
module.exports = {
|
|
281
|
+
async redirects() {
|
|
282
|
+
return storyblokRedirects;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Utility Functions
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import {
|
|
291
|
+
getLocales,
|
|
292
|
+
getHosts,
|
|
293
|
+
getDomainConfig,
|
|
294
|
+
isValidLocale,
|
|
295
|
+
getDefaultLocaleForHost
|
|
296
|
+
} from '@netgen/storyblok-siteaccess';
|
|
297
|
+
|
|
298
|
+
// Get all locales
|
|
299
|
+
const locales = getLocales(environmentConfig);
|
|
300
|
+
|
|
301
|
+
// Check if locale is valid
|
|
302
|
+
const isValid = isValidLocale(environmentConfig, 'en-US');
|
|
303
|
+
|
|
304
|
+
// Get domain configuration
|
|
305
|
+
const domainConfig = getDomainConfig(environmentConfig, 'example.com');
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Integration with next-intl
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { isValidLocale } from '@netgen/storyblok-siteaccess';
|
|
312
|
+
import { getRequestConfig } from 'next-intl/server';
|
|
313
|
+
|
|
314
|
+
export default getRequestConfig(async ({ requestLocale }) => {
|
|
315
|
+
const locale = await requestLocale;
|
|
316
|
+
|
|
317
|
+
// Validate locale using the package utility
|
|
318
|
+
if (!isValidLocale(environmentConfig, locale)) {
|
|
319
|
+
// Handle invalid locale
|
|
320
|
+
return { locale: 'en', messages: {} };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
locale,
|
|
325
|
+
messages: (await import(`./messages/${locale}.json`)).default
|
|
326
|
+
};
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## API Reference
|
|
331
|
+
|
|
332
|
+
### Utility Functions
|
|
333
|
+
|
|
334
|
+
- **`generateNextIntlRouting(config, options?)`**: Generate Next.js internationalization routing
|
|
335
|
+
- **`generateStoryblokRedirects(config, options?)`**: Generate Storyblok visual editor redirects
|
|
336
|
+
- **`getLocales(config)`**: Extract all unique locales
|
|
337
|
+
- **`getHosts(config)`**: Extract all unique hosts
|
|
338
|
+
- **`getDomainConfig(config, host)`**: Get domain configuration for specific host
|
|
339
|
+
- **`isValidLocale(config, locale)`**: Check if locale exists in configuration
|
|
340
|
+
- **`getDefaultLocaleForHost(config, host)`**: Get default locale for specific host
|
|
341
|
+
|
|
342
|
+
### Types
|
|
343
|
+
|
|
344
|
+
- **`NextRoutingConfig`**: Next.js routing configuration interface
|
|
345
|
+
- **`NextRedirectConfig`**: Next.js redirect configuration interface
|
|
346
|
+
- **`NextDomainConfig`**: Next.js domain configuration interface
|
|
347
|
+
|
|
348
|
+
## License
|
|
349
|
+
|
|
350
|
+
ISC
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ISbAlternateObject, ISbStoryData } from "@storyblok/react";
|
|
2
|
+
import type { AlternateStory, EnvironmentConfig, SiteaccessConfig, SiteaccessInputType } from "../types";
|
|
3
|
+
import { GenericSiteaccessResolver } from "./SiteaccessResolver";
|
|
4
|
+
import type { Market, Language } from "../types/augmentation";
|
|
5
|
+
/**
|
|
6
|
+
* Generic Siteaccess class
|
|
7
|
+
* Handles URL generation, path conversion, and alternate language management
|
|
8
|
+
*/
|
|
9
|
+
export declare class Siteaccess<TMarket extends Market = Market, TLanguage extends Language = Language, TEnvironmentConfig extends EnvironmentConfig<TMarket, TLanguage> = EnvironmentConfig<TMarket, TLanguage>> {
|
|
10
|
+
markets: TMarket[];
|
|
11
|
+
languages: TLanguage[];
|
|
12
|
+
private siteaccessResolver;
|
|
13
|
+
static readonly storyblokDefaultLanguage = "default";
|
|
14
|
+
readonly host: string;
|
|
15
|
+
readonly uri?: string;
|
|
16
|
+
readonly locale: string;
|
|
17
|
+
readonly market: TMarket;
|
|
18
|
+
readonly language: TLanguage;
|
|
19
|
+
readonly storyblokVisualEditorPrefix: string;
|
|
20
|
+
readonly baseUrl: string;
|
|
21
|
+
readonly origin: string;
|
|
22
|
+
readonly storyblokFolderPrefix: string;
|
|
23
|
+
readonly siteaccessConfig: SiteaccessConfig<TMarket, TLanguage>;
|
|
24
|
+
environmentConfig: TEnvironmentConfig;
|
|
25
|
+
constructor(markets: TMarket[], languages: TLanguage[], siteaccessConfig: SiteaccessConfig<TMarket, TLanguage>, environmentConfig: TEnvironmentConfig, siteaccessResolver: GenericSiteaccessResolver<TMarket, TLanguage>);
|
|
26
|
+
/**
|
|
27
|
+
* Get the Storyblok language for API calls
|
|
28
|
+
* Returns 'default' for primary market language, actual language otherwise
|
|
29
|
+
*/
|
|
30
|
+
getStoryblokLanguage(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Convert input to Storyblok path
|
|
33
|
+
*/
|
|
34
|
+
getStoryblokPath(input: SiteaccessInputType): string;
|
|
35
|
+
/**
|
|
36
|
+
* Convert input to pathname
|
|
37
|
+
*/
|
|
38
|
+
getPathname(input: SiteaccessInputType): string;
|
|
39
|
+
/**
|
|
40
|
+
* Convert input to full URL
|
|
41
|
+
*/
|
|
42
|
+
getUrl(input: SiteaccessInputType): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get primary language for current market
|
|
45
|
+
* Requires environmentConfig to be provided
|
|
46
|
+
*/
|
|
47
|
+
getMarketPrimaryLanguage(): TLanguage;
|
|
48
|
+
/**
|
|
49
|
+
* Get market alternates for a story
|
|
50
|
+
* Requires environmentConfig and all available markets to be provided
|
|
51
|
+
*/
|
|
52
|
+
getMarketAlternates({ storyblokAlternates, }?: {
|
|
53
|
+
storyblokAlternates?: ISbAlternateObject[];
|
|
54
|
+
}): AlternateStory<TMarket, TLanguage>[];
|
|
55
|
+
/**
|
|
56
|
+
* Get language for Storyblok API calls (undefined for primary language)
|
|
57
|
+
*/
|
|
58
|
+
getLanguageForStoryblok(): TLanguage | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Check if story language translation is valid
|
|
61
|
+
*/
|
|
62
|
+
isStoryLanguageTranslationValid(story: ISbStoryData): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Get language alternates for a story
|
|
65
|
+
*/
|
|
66
|
+
getLanguageAlternates(story: ISbStoryData, siteaccessResolver: GenericSiteaccessResolver<TMarket, TLanguage>): AlternateStory<TMarket, TLanguage>[];
|
|
67
|
+
/**
|
|
68
|
+
* Static utility: Strip leading and trailing slashes
|
|
69
|
+
*/
|
|
70
|
+
static stripSlashes(path?: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Static utility: Join URL parts
|
|
73
|
+
*/
|
|
74
|
+
static joinUrlParts(...parts: (string | undefined | null)[]): string;
|
|
75
|
+
private storyblokPathToLocation;
|
|
76
|
+
private locationToStoryblokPath;
|
|
77
|
+
private locationToUrl;
|
|
78
|
+
private urlToLocation;
|
|
79
|
+
private pathnameToLocation;
|
|
80
|
+
private locationToPathname;
|
|
81
|
+
private normalizeInput;
|
|
82
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { GenericSiteaccessResolver } from "./SiteaccessResolver";
|
|
2
|
+
/**
|
|
3
|
+
* Generic Siteaccess class
|
|
4
|
+
* Handles URL generation, path conversion, and alternate language management
|
|
5
|
+
*/
|
|
6
|
+
export class Siteaccess {
|
|
7
|
+
markets;
|
|
8
|
+
languages;
|
|
9
|
+
siteaccessResolver;
|
|
10
|
+
static storyblokDefaultLanguage = "default";
|
|
11
|
+
host;
|
|
12
|
+
uri;
|
|
13
|
+
locale;
|
|
14
|
+
market;
|
|
15
|
+
language;
|
|
16
|
+
storyblokVisualEditorPrefix;
|
|
17
|
+
baseUrl;
|
|
18
|
+
origin;
|
|
19
|
+
storyblokFolderPrefix;
|
|
20
|
+
siteaccessConfig;
|
|
21
|
+
environmentConfig;
|
|
22
|
+
constructor(markets, languages, siteaccessConfig, environmentConfig, siteaccessResolver) {
|
|
23
|
+
this.markets = markets;
|
|
24
|
+
this.languages = languages;
|
|
25
|
+
this.siteaccessResolver = siteaccessResolver;
|
|
26
|
+
this.host = Siteaccess.stripSlashes(siteaccessConfig.host);
|
|
27
|
+
this.locale = Siteaccess.stripSlashes(siteaccessConfig.locale);
|
|
28
|
+
this.uri = Siteaccess.stripSlashes(siteaccessConfig.uri);
|
|
29
|
+
this.market = siteaccessConfig.market;
|
|
30
|
+
this.language = siteaccessConfig.language;
|
|
31
|
+
this.storyblokVisualEditorPrefix = Siteaccess.stripSlashes(siteaccessConfig.storyblokVisualEditorPrefix);
|
|
32
|
+
this.siteaccessConfig = siteaccessConfig;
|
|
33
|
+
this.origin = `https://${this.host}`;
|
|
34
|
+
this.baseUrl = `${this.origin}/${this.uri || ""}`;
|
|
35
|
+
this.storyblokFolderPrefix = Siteaccess.stripSlashes(siteaccessConfig.storyblokFolderPrefix);
|
|
36
|
+
this.environmentConfig = environmentConfig;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the Storyblok language for API calls
|
|
40
|
+
* Returns 'default' for primary market language, actual language otherwise
|
|
41
|
+
*/
|
|
42
|
+
getStoryblokLanguage() {
|
|
43
|
+
const marketPrimaryLanguage = this.getMarketPrimaryLanguage();
|
|
44
|
+
const isCurrentLanguageMarketPrimary = marketPrimaryLanguage === this.language;
|
|
45
|
+
return isCurrentLanguageMarketPrimary
|
|
46
|
+
? Siteaccess.storyblokDefaultLanguage
|
|
47
|
+
: String(this.language);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Convert input to Storyblok path
|
|
51
|
+
*/
|
|
52
|
+
getStoryblokPath(input) {
|
|
53
|
+
const location = this.normalizeInput(input);
|
|
54
|
+
return this.locationToStoryblokPath(location);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Convert input to pathname
|
|
58
|
+
*/
|
|
59
|
+
getPathname(input) {
|
|
60
|
+
const location = this.normalizeInput(input);
|
|
61
|
+
return this.locationToPathname(location);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Convert input to full URL
|
|
65
|
+
*/
|
|
66
|
+
getUrl(input) {
|
|
67
|
+
const location = this.normalizeInput(input);
|
|
68
|
+
return this.locationToUrl(location);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get primary language for current market
|
|
72
|
+
* Requires environmentConfig to be provided
|
|
73
|
+
*/
|
|
74
|
+
getMarketPrimaryLanguage() {
|
|
75
|
+
const currentMarketConfig = this.environmentConfig.marketConfigs[this.market];
|
|
76
|
+
return currentMarketConfig.primaryLanguage;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get market alternates for a story
|
|
80
|
+
* Requires environmentConfig and all available markets to be provided
|
|
81
|
+
*/
|
|
82
|
+
getMarketAlternates({ storyblokAlternates = [], } = {}) {
|
|
83
|
+
return this.markets.map((market) => {
|
|
84
|
+
const storyblokAlternate = storyblokAlternates.find((alternate) => {
|
|
85
|
+
const alternateSiteaccess = this.siteaccessResolver.resolveByStoryblokPath({
|
|
86
|
+
storyblokPath: alternate.full_slug,
|
|
87
|
+
});
|
|
88
|
+
return alternateSiteaccess.market === market;
|
|
89
|
+
});
|
|
90
|
+
if (storyblokAlternate) {
|
|
91
|
+
const marketSiteaccessConfig = this.siteaccessResolver.resolveByMarket({
|
|
92
|
+
market,
|
|
93
|
+
});
|
|
94
|
+
const marketSiteaccess = new Siteaccess(this.markets, this.languages, marketSiteaccessConfig, this.environmentConfig, this.siteaccessResolver);
|
|
95
|
+
const url = marketSiteaccess.getUrl({
|
|
96
|
+
storyblokPath: storyblokAlternate.full_slug,
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
language: this.language,
|
|
100
|
+
market,
|
|
101
|
+
url,
|
|
102
|
+
isCurrent: market === this.market,
|
|
103
|
+
locale: marketSiteaccess.locale,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const marketSiteaccessConfig = this.siteaccessResolver.resolveByMarket({
|
|
107
|
+
market,
|
|
108
|
+
});
|
|
109
|
+
const marketSiteaccess = new Siteaccess(this.markets, this.languages, marketSiteaccessConfig, this.environmentConfig, this.siteaccessResolver);
|
|
110
|
+
return {
|
|
111
|
+
language: marketSiteaccess.language,
|
|
112
|
+
market,
|
|
113
|
+
url: marketSiteaccess.baseUrl,
|
|
114
|
+
isCurrent: market === this.market,
|
|
115
|
+
locale: marketSiteaccess.locale,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get language for Storyblok API calls (undefined for primary language)
|
|
121
|
+
*/
|
|
122
|
+
getLanguageForStoryblok() {
|
|
123
|
+
const marketPrimaryLanguage = this.getMarketPrimaryLanguage();
|
|
124
|
+
const isCurrentLanguageMarketPrimary = marketPrimaryLanguage === this.language;
|
|
125
|
+
return isCurrentLanguageMarketPrimary ? undefined : this.language;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Check if story language translation is valid
|
|
129
|
+
*/
|
|
130
|
+
isStoryLanguageTranslationValid(story) {
|
|
131
|
+
const validTranslatedLanguages = story.translated_slugs
|
|
132
|
+
?.filter((variant) => !!variant.name)
|
|
133
|
+
.map((variant) => variant.lang) || [];
|
|
134
|
+
const marketPrimaryLanguage = this.getMarketPrimaryLanguage();
|
|
135
|
+
const validLanguages = [marketPrimaryLanguage, ...validTranslatedLanguages];
|
|
136
|
+
return validLanguages.includes(this.language);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get language alternates for a story
|
|
140
|
+
*/
|
|
141
|
+
getLanguageAlternates(story, siteaccessResolver) {
|
|
142
|
+
const translatedVariants = story.translated_slugs || [];
|
|
143
|
+
const siteaccessConfig = siteaccessResolver.resolveByStoryblokPath({
|
|
144
|
+
storyblokPath: story.full_slug || "",
|
|
145
|
+
});
|
|
146
|
+
const siteaccess = new Siteaccess(this.markets, this.languages, siteaccessConfig, this.environmentConfig, this.siteaccessResolver);
|
|
147
|
+
const defaultVariant = {
|
|
148
|
+
path: story.default_full_slug || "",
|
|
149
|
+
lang: siteaccess.getMarketPrimaryLanguage(),
|
|
150
|
+
name: story.name,
|
|
151
|
+
published: story.published || story.is_published || !!story.published_at,
|
|
152
|
+
};
|
|
153
|
+
const allLanguageVariants = [defaultVariant, ...translatedVariants];
|
|
154
|
+
const validatedLanguageVariants = allLanguageVariants
|
|
155
|
+
.map((variantInfo) => {
|
|
156
|
+
const isPublished = "published" in variantInfo && variantInfo.published;
|
|
157
|
+
const isCurrentLanguage = variantInfo.lang === siteaccess.language;
|
|
158
|
+
if ((!isPublished && !isCurrentLanguage) || !variantInfo.name)
|
|
159
|
+
return null;
|
|
160
|
+
const language = this.languages.find((language) => language === variantInfo.lang);
|
|
161
|
+
if (!language)
|
|
162
|
+
return null;
|
|
163
|
+
const languageSiteaccessConfig = siteaccessResolver.findMatchingSiteaccessConfig((siteaccessConfig) => {
|
|
164
|
+
const languageMatch = siteaccessConfig.language === language;
|
|
165
|
+
const normalizedStoryblokPath = Siteaccess.stripSlashes(variantInfo.path);
|
|
166
|
+
const storyblokPrefixMatch = normalizedStoryblokPath.startsWith(siteaccessConfig.storyblokFolderPrefix);
|
|
167
|
+
return languageMatch && storyblokPrefixMatch;
|
|
168
|
+
});
|
|
169
|
+
if (!languageSiteaccessConfig)
|
|
170
|
+
return null;
|
|
171
|
+
const languageSiteaccess = new Siteaccess(this.markets, this.languages, languageSiteaccessConfig, this.environmentConfig, this.siteaccessResolver);
|
|
172
|
+
const url = languageSiteaccess.getUrl({
|
|
173
|
+
storyblokPath: variantInfo.path,
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
language,
|
|
177
|
+
isCurrent: variantInfo.lang === siteaccess.language,
|
|
178
|
+
url,
|
|
179
|
+
market: languageSiteaccess.market,
|
|
180
|
+
locale: languageSiteaccess.locale,
|
|
181
|
+
};
|
|
182
|
+
})
|
|
183
|
+
.filter((variant) => variant !== null);
|
|
184
|
+
return validatedLanguageVariants;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Static utility: Strip leading and trailing slashes
|
|
188
|
+
*/
|
|
189
|
+
static stripSlashes(path) {
|
|
190
|
+
return path?.replace(/^\/+|\/+$/g, "") || "";
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Static utility: Join URL parts
|
|
194
|
+
*/
|
|
195
|
+
static joinUrlParts(...parts) {
|
|
196
|
+
return parts.filter(Boolean).join("/").replace(/\/+/g, "/");
|
|
197
|
+
}
|
|
198
|
+
// Private methods for internal path conversion
|
|
199
|
+
storyblokPathToLocation(storyblokPath) {
|
|
200
|
+
let result = storyblokPath;
|
|
201
|
+
if (this.storyblokVisualEditorPrefix &&
|
|
202
|
+
result.startsWith(this.storyblokVisualEditorPrefix)) {
|
|
203
|
+
result = result.slice(this.storyblokVisualEditorPrefix.length);
|
|
204
|
+
}
|
|
205
|
+
if (this.storyblokFolderPrefix &&
|
|
206
|
+
result.startsWith(this.storyblokFolderPrefix)) {
|
|
207
|
+
result = result.slice(this.storyblokFolderPrefix.length);
|
|
208
|
+
}
|
|
209
|
+
return Siteaccess.stripSlashes(result);
|
|
210
|
+
}
|
|
211
|
+
locationToStoryblokPath(location) {
|
|
212
|
+
return Siteaccess.joinUrlParts(this.storyblokFolderPrefix, location);
|
|
213
|
+
}
|
|
214
|
+
locationToUrl(location) {
|
|
215
|
+
const pathname = this.locationToPathname(location);
|
|
216
|
+
return `${this.origin}${pathname}`;
|
|
217
|
+
}
|
|
218
|
+
urlToLocation(url) {
|
|
219
|
+
const urlObject = new URL(url);
|
|
220
|
+
return this.pathnameToLocation(urlObject.pathname);
|
|
221
|
+
}
|
|
222
|
+
pathnameToLocation(pathname) {
|
|
223
|
+
const uriWithSlash = this.uri ? `/${this.uri}` : "";
|
|
224
|
+
return pathname.replace(uriWithSlash, "");
|
|
225
|
+
}
|
|
226
|
+
locationToPathname(location) {
|
|
227
|
+
return `/${Siteaccess.joinUrlParts(this.uri, location)}`;
|
|
228
|
+
}
|
|
229
|
+
normalizeInput(input) {
|
|
230
|
+
if (input.pathname !== undefined) {
|
|
231
|
+
const normalizedPathname = Siteaccess.stripSlashes(input.pathname);
|
|
232
|
+
return this.pathnameToLocation(normalizedPathname);
|
|
233
|
+
}
|
|
234
|
+
if (input.storyblokPath !== undefined) {
|
|
235
|
+
const normalizedStoryblokPath = Siteaccess.stripSlashes(input.storyblokPath);
|
|
236
|
+
return this.storyblokPathToLocation(normalizedStoryblokPath);
|
|
237
|
+
}
|
|
238
|
+
if (input.url !== undefined) {
|
|
239
|
+
const normalizedUrl = Siteaccess.stripSlashes(input.url);
|
|
240
|
+
return this.urlToLocation(normalizedUrl);
|
|
241
|
+
}
|
|
242
|
+
throw new Error("Must provide one of: pathname, storyblokPath, or url.");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=Siteaccess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Siteaccess.js","sourceRoot":"","sources":["../../src/classes/Siteaccess.ts"],"names":[],"mappings":"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"}
|